Repository: google/go-github
Pull request #1330 (2019-11-09 00:13:44)

Add struct response for AddCollaborator function

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 then comment `@googlebot I fixed it.`. 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%2F1330) for more info**.

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

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

```diff
@@            Coverage Diff             @@
##           master    #1330      +/-   ##
==========================================
- Coverage   73.45%   73.43%   -0.02%     
==========================================
  Files          86       86              
  Lines        6061     6065       +4     
==========================================
+ Hits         4452     4454       +2     
- Misses        838      839       +1     
- Partials      771      772       +1
```


| [Impacted Files](https://codecov.io/gh/google/go-github/pull/1330?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [github/repos\_collaborators.go](https://codecov.io/gh/google/go-github/pull/1330/diff?src=pr&el=tree#diff-Z2l0aHViL3JlcG9zX2NvbGxhYm9yYXRvcnMuZ28=) | `79.16% <71.42%> (-2.66%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/google/go-github/pull/1330?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/1330?src=pr&el=footer). Last update [e555eab...af6373f](https://codecov.io/gh/google/go-github/pull/1330?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

safwanolaimat: @gmlewis I have created Licence Agreement before i did the pull request 
safwanolaimat: @googlebot I fixed it.
googlebot: CLAs look good, thanks!

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

<!-- ok -->
safwanolaimat: @gmlewis can you check again ?

Patch:
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5d5a597f..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-language: go
-
-go:
-  - "1.x"
-  - "1.12.x"
-  - master
-
-matrix:
-  allow_failures:
-    - go: master
-  fast_finish: true
-
-cache:
-  directories:
-    - $HOME/.cache/go-build
-    - $HOME/gopath/pkg/mod
-
-env:
-  global:
-    - GO111MODULE=on
-
-script:
-  - diff -u <(echo -n) <(gofmt -d -s .)
-  - go generate -x ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code) # Check that go generate ./... produces a zero diff; clean up any changes afterwards.
-  - go vet ./...
-  - go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
-  - go test -v -tags=integration -run=^$ ./test/integration # Check that integration test builds successfully, but don't run any of the tests (they hit live GitHub API).
-  - cd scrape && go test ./... # explicitly run scrape tests, since it is in a separate module
-
-after_success:
-  - bash <(curl -s https://codecov.io/bash)
diff --git a/README.md b/README.md
index 66503b24..c6f57924 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
 # go-github #
 
-[![GoDoc](https://godoc.org/github.com/google/go-github/github?status.svg)](https://godoc.org/github.com/google/go-github/github) [![Build Status](https://travis-ci.org/google/go-github.svg?branch=master)](https://travis-ci.org/google/go-github) [![Test Coverage](https://codecov.io/gh/google/go-github/branch/master/graph/badge.svg)](https://codecov.io/gh/google/go-github) [![Discuss at go-github@googlegroups.com](https://img.shields.io/badge/discuss-go--github%40googlegroups.com-blue.svg)](https://groups.google.com/group/go-github) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/796/badge)](https://bestpractices.coreinfrastructure.org/projects/796)
+[![GoDoc](https://godoc.org/github.com/google/go-github/github?status.svg)](https://godoc.org/github.com/google/go-github/github)
+[![Test Status](https://github.com/google/go-github/workflows/tests/badge.svg)](https://github.com/google/go-github/actions?query=workflow%3Atests)
+[![Test Coverage](https://codecov.io/gh/google/go-github/branch/master/graph/badge.svg)](https://codecov.io/gh/google/go-github)
+[![Discuss at go-github@googlegroups.com](https://img.shields.io/badge/discuss-go--github%40googlegroups.com-blue.svg)](https://groups.google.com/group/go-github)
+[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/796/badge)](https://bestpractices.coreinfrastructure.org/projects/796)
 
 go-github is a Go client library for accessing the [GitHub API v3][].
 
diff --git a/example/topics/main.go b/example/topics/main.go
new file mode 100644
index 00000000..96098efe
--- /dev/null
+++ b/example/topics/main.go
@@ -0,0 +1,40 @@
+// 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.
+
+// The simple command demonstrates a simple functionality which
+// prompts the user for a GitHub topic and lists all the public
+// organization memberships of the specified topic.
+package main
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/google/go-github/v28/github"
+)
+
+// Fetch all the public organizations' membership of a user.
+//
+func FetchTopics(topic string) (*github.TopicsSearchResult, error) {
+	client := github.NewClient(nil)
+	topics, _, err := client.Search.Topics(context.Background(), topic, nil)
+	return topics, err
+}
+
+func main() {
+	var topic string
+	fmt.Print("Enter GitHub topic: ")
+	fmt.Scanf("%s", &topic)
+
+	topics, err := FetchTopics(topic)
+	if err != nil {
+		fmt.Printf("Error: %v\n", err)
+		return
+	}
+
+	for _, topic := range topics.Topics {
+		fmt.Println(*topic.Name)
+	}
+}
diff --git a/github/admin.go b/github/admin.go
index 2d96733a..31cd1fa2 100644
--- a/github/admin.go
+++ b/github/admin.go
@@ -62,6 +62,24 @@ func (m UserLDAPMapping) String() string {
 	return Stringify(m)
 }
 
+// Enterprise represents the GitHub enterprise profile.
+type Enterprise struct {
+	ID          *int       `json:"id,omitempty"`
+	Slug        *string    `json:"slug,omitempty"`
+	Name        *string    `json:"name,omitempty"`
+	NodeID      *string    `json:"node_id,omitempty"`
+	AvatarURL   *string    `json:"avatar_url,omitempty"`
+	Description *string    `json:"description,omitempty"`
+	WebsiteURL  *string    `json:"website_url,omitempty"`
+	HTMLURL     *string    `json:"html_url,omitempty"`
+	CreatedAt   *Timestamp `json:"created_at,omitempty"`
+	UpdatedAt   *Timestamp `json:"updated_at,omitempty"`
+}
+
+func (m Enterprise) String() string {
+	return Stringify(m)
+}
+
 // UpdateUserLDAPMapping updates the mapping between a GitHub user and an LDAP user.
 //
 // GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user
diff --git a/github/admin_users.go b/github/admin_users.go
index ea7a47d3..74285550 100644
--- a/github/admin_users.go
+++ b/github/admin_users.go
@@ -7,6 +7,7 @@ package github
 
 import (
 	"context"
+	"fmt"
 )
 
 // createUserRequest is a subset of User and is used internally
@@ -59,3 +60,74 @@ func (s *AdminService) DeleteUser(ctx context.Context, username string) (*Respon
 
 	return resp, nil
 }
+
+// ImpersonateUserOptions represents the scoping for the OAuth token.
+type ImpersonateUserOptions struct {
+	Scopes []string `json:"scopes,omitempty"`
+}
+
+// OAuthAPP represents the GitHub Site Administrator OAuth app.
+type OAuthAPP struct {
+	URL      *string `json:"url,omitempty"`
+	Name     *string `json:"name,omitempty"`
+	ClientID *string `json:"client_id,omitempty"`
+}
+
+func (s OAuthAPP) String() string {
+	return Stringify(s)
+}
+
+// UserAuthorization represents the impersonation response.
+type UserAuthorization struct {
+	ID             *int64     `json:"id,omitempty"`
+	URL            *string    `json:"url,omitempty"`
+	Scopes         []string   `json:"scopes,omitempty"`
+	Token          *string    `json:"token,omitempty"`
+	TokenLastEight *string    `json:"token_last_eight,omitempty"`
+	HashedToken    *string    `json:"hashed_token,omitempty"`
+	App            *OAuthAPP  `json:"app,omitempty"`
+	Note           *string    `json:"note,omitempty"`
+	NoteURL        *string    `json:"note_url,omitempty"`
+	UpdatedAt      *Timestamp `json:"updated_at,omitempty"`
+	CreatedAt      *Timestamp `json:"created_at,omitempty"`
+	Fingerprint    *string    `json:"fingerprint,omitempty"`
+}
+
+// CreateUserImpersonation creates an impersonation OAuth token.
+//
+// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#create-an-impersonation-oauth-token
+func (s *AdminService) CreateUserImpersonation(ctx context.Context, username string, opt *ImpersonateUserOptions) (*UserAuthorization, *Response, error) {
+	u := fmt.Sprintf("admin/users/%s/authorizations", username)
+
+	req, err := s.client.NewRequest("POST", u, opt)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	a := new(UserAuthorization)
+	resp, err := s.client.Do(ctx, req, a)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return a, resp, nil
+}
+
+// DeleteUserImpersonation deletes an impersonation OAuth token.
+//
+// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#delete-an-impersonation-oauth-token
+func (s *AdminService) DeleteUserImpersonation(ctx context.Context, username string) (*Response, error) {
+	u := fmt.Sprintf("admin/users/%s/authorizations", 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/apps.go b/github/apps.go
index a546a70c..251ac631 100644
--- a/github/apps.go
+++ b/github/apps.go
@@ -19,15 +19,18 @@ type AppsService service
 
 // App represents a GitHub App.
 type App struct {
-	ID          *int64     `json:"id,omitempty"`
-	NodeID      *string    `json:"node_id,omitempty"`
-	Owner       *User      `json:"owner,omitempty"`
-	Name        *string    `json:"name,omitempty"`
-	Description *string    `json:"description,omitempty"`
-	ExternalURL *string    `json:"external_url,omitempty"`
-	HTMLURL     *string    `json:"html_url,omitempty"`
-	CreatedAt   *Timestamp `json:"created_at,omitempty"`
-	UpdatedAt   *Timestamp `json:"updated_at,omitempty"`
+	ID          *int64                   `json:"id,omitempty"`
+	Slug        *string                  `json:"slug,omitempty"`
+	NodeID      *string                  `json:"node_id,omitempty"`
+	Owner       *User                    `json:"owner,omitempty"`
+	Name        *string                  `json:"name,omitempty"`
+	Description *string                  `json:"description,omitempty"`
+	ExternalURL *string                  `json:"external_url,omitempty"`
+	HTMLURL     *string                  `json:"html_url,omitempty"`
+	CreatedAt   *Timestamp               `json:"created_at,omitempty"`
+	UpdatedAt   *Timestamp               `json:"updated_at,omitempty"`
+	Permissions *InstallationPermissions `json:"permissions,omitempty"`
+	Events      []*Event                 `json:"events,omitempty"`
 }
 
 // InstallationToken represents an installation token.
@@ -56,10 +59,13 @@ type InstallationTokenOptions struct {
 //   https://developer.github.com/enterprise/v3/apps/permissions/
 type InstallationPermissions struct {
 	Administration              *string `json:"administration,omitempty"`
+	Blocking                    *string `json:"blocking,omitempty"`
 	Checks                      *string `json:"checks,omitempty"`
 	Contents                    *string `json:"contents,omitempty"`
 	ContentReferences           *string `json:"content_references,omitempty"`
 	Deployments                 *string `json:"deployments,omitempty"`
+	Emails                      *string `json:"emails,omitempty"`
+	Followers                   *string `json:"followers,omitempty"`
 	Issues                      *string `json:"issues,omitempty"`
 	Metadata                    *string `json:"metadata,omitempty"`
 	Members                     *string `json:"members,omitempty"`
@@ -145,7 +151,7 @@ func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response,
 
 // ListInstallations lists the installations that the current GitHub App has.
 //
-// GitHub API docs: https://developer.github.com/v3/apps/#find-installations
+// GitHub API docs: https://developer.github.com/v3/apps/#list-installations
 func (s *AppsService) ListInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) {
 	u, err := addOptions("app/installations", opt)
 	if err != nil {
@@ -252,14 +258,14 @@ func (s *AppsService) CreateAttachment(ctx context.Context, contentReferenceID i
 
 // FindOrganizationInstallation finds the organization's installation information.
 //
-// GitHub API docs: https://developer.github.com/v3/apps/#find-organization-installation
+// GitHub API docs: https://developer.github.com/v3/apps/#get-an-organization-installation
 func (s *AppsService) FindOrganizationInstallation(ctx context.Context, org string) (*Installation, *Response, error) {
 	return s.getInstallation(ctx, fmt.Sprintf("orgs/%v/installation", org))
 }
 
 // FindRepositoryInstallation finds the repository's installation information.
 //
-// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation
+// GitHub API docs: https://developer.github.com/v3/apps/#get-a-repository-installation
 func (s *AppsService) FindRepositoryInstallation(ctx context.Context, owner, repo string) (*Installation, *Response, error) {
 	return s.getInstallation(ctx, fmt.Sprintf("repos/%v/%v/installation", owner, repo))
 }
@@ -273,7 +279,7 @@ func (s *AppsService) FindRepositoryInstallationByID(ctx context.Context, id int
 
 // FindUserInstallation finds the user's installation information.
 //
-// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation
+// GitHub API docs: https://developer.github.com/v3/apps/#get-a-user-installation
 func (s *AppsService) FindUserInstallation(ctx context.Context, user string) (*Installation, *Response, error) {
 	return s.getInstallation(ctx, fmt.Sprintf("users/%v/installation", user))
 }
diff --git a/github/apps_manifest.go b/github/apps_manifest.go
new file mode 100644
index 00000000..8d6ab3c8
--- /dev/null
+++ b/github/apps_manifest.go
@@ -0,0 +1,53 @@
+// 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"
+	"fmt"
+)
+
+const (
+	mediaTypeAppManifestPreview = "application/vnd.github.fury-preview+json"
+)
+
+// AppConfig describes the configuration of a GitHub App.
+type AppConfig struct {
+	ID            *int64     `json:"id,omitempty"`
+	NodeID        *string    `json:"node_id,omitempty"`
+	Owner         *User      `json:"owner,omitempty"`
+	Name          *string    `json:"name,omitempty"`
+	Description   *string    `json:"description,omitempty"`
+	ExternalURL   *string    `json:"external_url,omitempty"`
+	HTMLURL       *string    `json:"html_url,omitempty"`
+	CreatedAt     *Timestamp `json:"created_at,omitempty"`
+	UpdatedAt     *Timestamp `json:"updated_at,omitempty"`
+	ClientID      *string    `json:"client_id,omitempty"`
+	ClientSecret  *string    `json:"client_secret,omitempty"`
+	WebhookSecret *string    `json:"webhook_secret,omitempty"`
+	PEM           *string    `json:"pem,omitempty"`
+}
+
+// CompleteAppManifest completes the App manifest handshake flow for the given
+// code.
+//
+// GitHub API docs: https://developer.github.com/apps/building-github-apps/creating-github-apps-from-a-manifest/#3-you-exchange-the-temporary-code-to-retrieve-the-app-configuration
+func (s *AppsService) CompleteAppManifest(ctx context.Context, code string) (*AppConfig, *Response, error) {
+	u := fmt.Sprintf("app-manifests/%s/conversions", code)
+	req, err := s.client.NewRequest("POST", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+	req.Header.Set("Accept", mediaTypeAppManifestPreview)
+
+	cfg := new(AppConfig)
+	resp, err := s.client.Do(ctx, req, cfg)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return cfg, resp, nil
+}
diff --git a/github/checks.go b/github/checks.go
index 84f84034..1acc26e9 100644
--- a/github/checks.go
+++ b/github/checks.go
@@ -140,7 +140,6 @@ func (s *ChecksService) GetCheckSuite(ctx context.Context, owner, repo string, c
 // CreateCheckRunOptions sets up parameters needed to create a CheckRun.
 type CreateCheckRunOptions struct {
 	Name        string            `json:"name"`                   // The name of the check (e.g., "code-coverage"). (Required.)
-	HeadBranch  string            `json:"head_branch"`            // The name of the branch to perform a check against. (Required.)
 	HeadSHA     string            `json:"head_sha"`               // The SHA of the commit. (Required.)
 	DetailsURL  *string           `json:"details_url,omitempty"`  // The URL of the integrator's site that has the full details of the check. (Optional.)
 	ExternalID  *string           `json:"external_id,omitempty"`  // A reference for the run on the integrator's system. (Optional.)
@@ -183,7 +182,6 @@ func (s *ChecksService) CreateCheckRun(ctx context.Context, owner, repo string,
 // UpdateCheckRunOptions sets up parameters needed to update a CheckRun.
 type UpdateCheckRunOptions struct {
 	Name        string            `json:"name"`                   // The name of the check (e.g., "code-coverage"). (Required.)
-	HeadBranch  *string           `json:"head_branch,omitempty"`  // The name of the branch to perform a check against. (Optional.)
 	HeadSHA     *string           `json:"head_sha,omitempty"`     // The SHA of the commit. (Optional.)
 	DetailsURL  *string           `json:"details_url,omitempty"`  // The URL of the integrator's site that has the full details of the check. (Optional.)
 	ExternalID  *string           `json:"external_id,omitempty"`  // A reference for the run on the integrator's system. (Optional.)
diff --git a/github/event.go b/github/event.go
index 0ba8f46a..de20f584 100644
--- a/github/event.go
+++ b/github/event.go
@@ -110,6 +110,8 @@ func (e *Event) ParsePayload() (payload interface{}, err error) {
 		payload = &TeamEvent{}
 	case "TeamAddEvent":
 		payload = &TeamAddEvent{}
+	case "UserEvent":
+		payload = &UserEvent{}
 	case "WatchEvent":
 		payload = &WatchEvent{}
 	}
diff --git a/github/event_types.go b/github/event_types.go
index 049751aa..a04c7f62 100644
--- a/github/event_types.go
+++ b/github/event_types.go
@@ -872,6 +872,20 @@ type TeamAddEvent struct {
 	Installation *Installation `json:"installation,omitempty"`
 }
 
+// UserEvent is triggered when a user is created or deleted.
+// The Webhook event name is "user".
+//
+// Only global webhooks can subscribe to this event type.
+//
+// GitHub API docs: https://developer.github.com/enterprise/v3/activity/events/types/#userevent-enterprise
+type UserEvent struct {
+	User *User `json:"user,omitempty"`
+	// The action performed. Possible values are: "created" or "deleted".
+	Action     *string     `json:"action,omitempty"`
+	Enterprise *Enterprise `json:"enterprise,omitempty"`
+	Sender     *User       `json:"sender,omitempty"`
+}
+
 // WatchEvent is related to starring a repository, not watching. See this API
 // blog post for an explanation: https://developer.github.com/changes/2012-09-05-watcher-api/
 //
diff --git a/github/git_trees.go b/github/git_trees.go
index 4bc29135..df843f4d 100644
--- a/github/git_trees.go
+++ b/github/git_trees.go
@@ -7,6 +7,7 @@ package github
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
 )
 
@@ -43,6 +44,39 @@ func (t TreeEntry) String() string {
 	return Stringify(t)
 }
 
+func (t *TreeEntry) MarshalJSON() ([]byte, error) {
+	if t.SHA == nil && t.Content == nil {
+		return json.Marshal(struct {
+			SHA  *string `json:"sha"`
+			Path *string `json:"path,omitempty"`
+			Mode *string `json:"mode,omitempty"`
+			Type *string `json:"type,omitempty"`
+		}{
+			nil,
+			t.Path,
+			t.Mode,
+			t.Type,
+		})
+	}
+	return json.Marshal(struct {
+		SHA     *string `json:"sha,omitempty"`
+		Path    *string `json:"path,omitempty"`
+		Mode    *string `json:"mode,omitempty"`
+		Type    *string `json:"type,omitempty"`
+		Size    *int    `json:"size,omitempty"`
+		Content *string `json:"content,omitempty"`
+		URL     *string `json:"url,omitempty"`
+	}{
+		SHA:     t.SHA,
+		Path:    t.Path,
+		Mode:    t.Mode,
+		Type:    t.Type,
+		Size:    t.Size,
+		Content: t.Content,
+		URL:     t.URL,
+	})
+}
+
 // GetTree fetches the Tree object for a given sha hash from a repository.
 //
 // GitHub API docs: https://developer.github.com/v3/git/trees/#get-a-tree
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 17018a3f..b26ee6b7 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -180,6 +180,22 @@ func (a *App) GetOwner() *User {
 	return a.Owner
 }
 
+// GetPermissions returns the Permissions field.
+func (a *App) GetPermissions() *InstallationPermissions {
+	if a == nil {
+		return nil
+	}
+	return a.Permissions
+}
+
+// GetSlug returns the Slug field if it's non-nil, zero value otherwise.
+func (a *App) GetSlug() string {
+	if a == nil || a.Slug == nil {
+		return ""
+	}
+	return *a.Slug
+}
+
 // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise.
 func (a *App) GetUpdatedAt() Timestamp {
 	if a == nil || a.UpdatedAt == nil {
@@ -188,6 +204,110 @@ func (a *App) GetUpdatedAt() Timestamp {
 	return *a.UpdatedAt
 }
 
+// GetClientID returns the ClientID field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetClientID() string {
+	if a == nil || a.ClientID == nil {
+		return ""
+	}
+	return *a.ClientID
+}
+
+// GetClientSecret returns the ClientSecret field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetClientSecret() string {
+	if a == nil || a.ClientSecret == nil {
+		return ""
+	}
+	return *a.ClientSecret
+}
+
+// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetCreatedAt() Timestamp {
+	if a == nil || a.CreatedAt == nil {
+		return Timestamp{}
+	}
+	return *a.CreatedAt
+}
+
+// GetDescription returns the Description field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetDescription() string {
+	if a == nil || a.Description == nil {
+		return ""
+	}
+	return *a.Description
+}
+
+// GetExternalURL returns the ExternalURL field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetExternalURL() string {
+	if a == nil || a.ExternalURL == nil {
+		return ""
+	}
+	return *a.ExternalURL
+}
+
+// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetHTMLURL() string {
+	if a == nil || a.HTMLURL == nil {
+		return ""
+	}
+	return *a.HTMLURL
+}
+
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetID() int64 {
+	if a == nil || a.ID == nil {
+		return 0
+	}
+	return *a.ID
+}
+
+// GetName returns the Name field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetName() string {
+	if a == nil || a.Name == nil {
+		return ""
+	}
+	return *a.Name
+}
+
+// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetNodeID() string {
+	if a == nil || a.NodeID == nil {
+		return ""
+	}
+	return *a.NodeID
+}
+
+// GetOwner returns the Owner field.
+func (a *AppConfig) GetOwner() *User {
+	if a == nil {
+		return nil
+	}
+	return a.Owner
+}
+
+// GetPEM returns the PEM field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetPEM() string {
+	if a == nil || a.PEM == nil {
+		return ""
+	}
+	return *a.PEM
+}
+
+// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetUpdatedAt() Timestamp {
+	if a == nil || a.UpdatedAt == nil {
+		return Timestamp{}
+	}
+	return *a.UpdatedAt
+}
+
+// GetWebhookSecret returns the WebhookSecret field if it's non-nil, zero value otherwise.
+func (a *AppConfig) GetWebhookSecret() string {
+	if a == nil || a.WebhookSecret == nil {
+		return ""
+	}
+	return *a.WebhookSecret
+}
+
 // GetBody returns the Body field if it's non-nil, zero value otherwise.
 func (a *Attachment) GetBody() string {
 	if a == nil || a.Body == nil {
@@ -1076,6 +1196,70 @@ func (c *CodeSearchResult) GetTotal() int {
 	return *c.Total
 }
 
+// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
+func (c *CollaboratorInvitation) GetCreatedAt() Timestamp {
+	if c == nil || c.CreatedAt == nil {
+		return Timestamp{}
+	}
+	return *c.CreatedAt
+}
+
+// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise.
+func (c *CollaboratorInvitation) GetHTMLURL() string {
+	if c == nil || c.HTMLURL == nil {
+		return ""
+	}
+	return *c.HTMLURL
+}
+
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (c *CollaboratorInvitation) GetID() int64 {
+	if c == nil || c.ID == nil {
+		return 0
+	}
+	return *c.ID
+}
+
+// GetInvitee returns the Invitee field.
+func (c *CollaboratorInvitation) GetInvitee() *User {
+	if c == nil {
+		return nil
+	}
+	return c.Invitee
+}
+
+// GetInviter returns the Inviter field.
+func (c *CollaboratorInvitation) GetInviter() *User {
+	if c == nil {
+		return nil
+	}
+	return c.Inviter
+}
+
+// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise.
+func (c *CollaboratorInvitation) GetPermissions() string {
+	if c == nil || c.Permissions == nil {
+		return ""
+	}
+	return *c.Permissions
+}
+
+// GetRepo returns the Repo field.
+func (c *CollaboratorInvitation) GetRepo() *Repository {
+	if c == nil {
+		return nil
+	}
+	return c.Repo
+}
+
+// GetURL returns the URL field if it's non-nil, zero value otherwise.
+func (c *CollaboratorInvitation) GetURL() string {
+	if c == nil || c.URL == nil {
+		return ""
+	}
+	return *c.URL
+}
+
 // GetCommitURL returns the CommitURL field if it's non-nil, zero value otherwise.
 func (c *CombinedStatus) GetCommitURL() string {
 	if c == nil || c.CommitURL == nil {
@@ -1988,6 +2172,14 @@ func (c *CreateOrgInvitationOptions) GetRole() string {
 	return *c.Role
 }
 
+// GetBody returns the Body field if it's non-nil, zero value otherwise.
+func (c *CreateUserProjectOptions) GetBody() string {
+	if c == nil || c.Body == nil {
+		return ""
+	}
+	return *c.Body
+}
+
 // GetInstallation returns the Installation field.
 func (d *DeleteEvent) GetInstallation() *Installation {
 	if d == nil {
@@ -2604,6 +2796,86 @@ func (d *DraftReviewComment) GetPosition() int {
 	return *d.Position
 }
 
+// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise.
+func (e *Enterprise) GetAvatarURL() string {
+	if e == nil || e.AvatarURL == nil {
+		return ""
+	}
+	return *e.AvatarURL
+}
+
+// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
+func (e *Enterprise) GetCreatedAt() Timestamp {
+	if e == nil || e.CreatedAt == nil {
+		return Timestamp{}
+	}
+	return *e.CreatedAt
+}
+
+// GetDescription returns the Description field if it's non-nil, zero value otherwise.
+func (e *Enterprise) GetDescription() string {
+	if e == nil || e.Description == nil {
+		return ""
+	}
+	return *e.Description
+}
+
+// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise.
+func (e *Enterprise) GetHTMLURL() string {
+	if e == nil || e.HTMLURL == nil {
+		return ""
+	}
+	return *e.HTMLURL
+}
+
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (e *Enterprise) GetID() int {
+	if e == nil || e.ID == nil {
+		return 0
+	}
+	return *e.ID
+}
+
+// GetName returns the Name field if it's non-nil, zero value otherwise.
+func (e *Enterprise) GetName() string {
+	if e == nil || e.Name == nil {
+		return ""
+	}
+	return *e.Name
+}
+
+// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise.
+func (e *Enterprise) GetNodeID() string {
+	if e == nil || e.NodeID == nil {
+		return ""
+	}
+	return *e.NodeID
+}
+
+// GetSlug returns the Slug field if it's non-nil, zero value otherwise.
+func (e *Enterprise) GetSlug() string {
+	if e == nil || e.Slug == nil {
+		return ""
+	}
+	return *e.Slug
+}
+
+// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise.
+func (e *Enterprise) GetUpdatedAt() Timestamp {
+	if e == nil || e.UpdatedAt == nil {
+		return Timestamp{}
+	}
+	return *e.UpdatedAt
+}
+
+// GetWebsiteURL returns the WebsiteURL field if it's non-nil, zero value otherwise.
+func (e *Enterprise) GetWebsiteURL() string {
+	if e == nil || e.WebsiteURL == nil {
+		return ""
+	}
+	return *e.WebsiteURL
+}
+
 // GetActor returns the Actor field.
 func (e *Event) GetActor() *User {
 	if e == nil {
@@ -3676,6 +3948,14 @@ func (i *InstallationPermissions) GetAdministration() string {
 	return *i.Administration
 }
 
+// GetBlocking returns the Blocking field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetBlocking() string {
+	if i == nil || i.Blocking == nil {
+		return ""
+	}
+	return *i.Blocking
+}
+
 // GetChecks returns the Checks field if it's non-nil, zero value otherwise.
 func (i *InstallationPermissions) GetChecks() string {
 	if i == nil || i.Checks == nil {
@@ -3708,6 +3988,22 @@ func (i *InstallationPermissions) GetDeployments() string {
 	return *i.Deployments
 }
 
+// GetEmails returns the Emails field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetEmails() string {
+	if i == nil || i.Emails == nil {
+		return ""
+	}
+	return *i.Emails
+}
+
+// GetFollowers returns the Followers field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetFollowers() string {
+	if i == nil || i.Followers == nil {
+		return ""
+	}
+	return *i.Followers
+}
+
 // GetIssues returns the Issues field if it's non-nil, zero value otherwise.
 func (i *InstallationPermissions) GetIssues() string {
 	if i == nil || i.Issues == nil {
@@ -4484,6 +4780,30 @@ func (i *IssueEvent) GetURL() string {
 	return *i.URL
 }
 
+// GetDirection returns the Direction field if it's non-nil, zero value otherwise.
+func (i *IssueListCommentsOptions) GetDirection() string {
+	if i == nil || i.Direction == nil {
+		return ""
+	}
+	return *i.Direction
+}
+
+// GetSince returns the Since field if it's non-nil, zero value otherwise.
+func (i *IssueListCommentsOptions) GetSince() time.Time {
+	if i == nil || i.Since == nil {
+		return time.Time{}
+	}
+	return *i.Since
+}
+
+// GetSort returns the Sort field if it's non-nil, zero value otherwise.
+func (i *IssueListCommentsOptions) GetSort() string {
+	if i == nil || i.Sort == nil {
+		return ""
+	}
+	return *i.Sort
+}
+
 // GetAssignee returns the Assignee field if it's non-nil, zero value otherwise.
 func (i *IssueRequest) GetAssignee() string {
 	if i == nil || i.Assignee == nil {
@@ -6036,6 +6356,30 @@ func (n *NotificationSubject) GetURL() string {
 	return *n.URL
 }
 
+// GetClientID returns the ClientID field if it's non-nil, zero value otherwise.
+func (o *OAuthAPP) GetClientID() string {
+	if o == nil || o.ClientID == nil {
+		return ""
+	}
+	return *o.ClientID
+}
+
+// GetName returns the Name field if it's non-nil, zero value otherwise.
+func (o *OAuthAPP) GetName() string {
+	if o == nil || o.Name == nil {
+		return ""
+	}
+	return *o.Name
+}
+
+// GetURL returns the URL field if it's non-nil, zero value otherwise.
+func (o *OAuthAPP) GetURL() string {
+	if o == nil || o.URL == nil {
+		return ""
+	}
+	return *o.URL
+}
+
 // GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise.
 func (o *Organization) GetAvatarURL() string {
 	if o == nil || o.AvatarURL == nil {
@@ -8444,6 +8788,14 @@ func (p *PullRequestReviewRequest) GetNodeID() string {
 	return *p.NodeID
 }
 
+// GetDismissalRestrictions returns the DismissalRestrictions field.
+func (p *PullRequestReviewsEnforcement) GetDismissalRestrictions() *DismissalRestrictions {
+	if p == nil {
+		return nil
+	}
+	return p.DismissalRestrictions
+}
+
 // GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field.
 func (p *PullRequestReviewsEnforcementRequest) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest {
 	if p == nil {
@@ -11940,6 +12292,102 @@ func (t *Timeline) GetURL() string {
 	return *t.URL
 }
 
+// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
+func (t *TopicResult) GetCreatedAt() Timestamp {
+	if t == nil || t.CreatedAt == nil {
+		return Timestamp{}
+	}
+	return *t.CreatedAt
+}
+
+// GetCreatedBy returns the CreatedBy field if it's non-nil, zero value otherwise.
+func (t *TopicResult) GetCreatedBy() string {
+	if t == nil || t.CreatedBy == nil {
+		return ""
+	}
+	return *t.CreatedBy
+}
+
+// GetCurated returns the Curated field if it's non-nil, zero value otherwise.
+func (t *TopicResult) GetCurated() bool {
+	if t == nil || t.Curated == nil {
+		return false
+	}
+	return *t.Curated
+}
+
+// GetDescription returns the Description field if it's non-nil, zero value otherwise.
+func (t *TopicResult) GetDescription() string {
+	if t == nil || t.Description == nil {
+		return ""
+	}
+	return *t.Description
+}
+
+// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise.
+func (t *TopicResult) GetDisplayName() string {
+	if t == nil || t.DisplayName == nil {
+		return ""
+	}
+	return *t.DisplayName
+}
+
+// GetFeatured returns the Featured field if it's non-nil, zero value otherwise.
+func (t *TopicResult) GetFeatured() bool {
+	if t == nil || t.Featured == nil {
+		return false
+	}
+	return *t.Featured
+}
+
+// GetName returns the Name field if it's non-nil, zero value otherwise.
+func (t *TopicResult) GetName() string {
+	if t == nil || t.Name == nil {
+		return ""
+	}
+	return *t.Name
+}
+
+// GetScore returns the Score field.
+func (t *TopicResult) GetScore() *float64 {
+	if t == nil {
+		return nil
+	}
+	return t.Score
+}
+
+// GetShortDescription returns the ShortDescription field if it's non-nil, zero value otherwise.
+func (t *TopicResult) GetShortDescription() string {
+	if t == nil || t.ShortDescription == nil {
+		return ""
+	}
+	return *t.ShortDescription
+}
+
+// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise.
+func (t *TopicResult) GetUpdatedAt() string {
+	if t == nil || t.UpdatedAt == nil {
+		return ""
+	}
+	return *t.UpdatedAt
+}
+
+// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise.
+func (t *TopicsSearchResult) GetIncompleteResults() bool {
+	if t == nil || t.IncompleteResults == nil {
+		return false
+	}
+	return *t.IncompleteResults
+}
+
+// GetTotal returns the Total field if it's non-nil, zero value otherwise.
+func (t *TopicsSearchResult) GetTotal() int {
+	if t == nil || t.Total == nil {
+		return 0
+	}
+	return *t.Total
+}
+
 // GetCount returns the Count field if it's non-nil, zero value otherwise.
 func (t *TrafficClones) GetCount() int {
 	if t == nil || t.Count == nil {
@@ -12156,14 +12604,6 @@ func (u *UpdateCheckRunOptions) GetExternalID() string {
 	return *u.ExternalID
 }
 
-// GetHeadBranch returns the HeadBranch field if it's non-nil, zero value otherwise.
-func (u *UpdateCheckRunOptions) GetHeadBranch() string {
-	if u == nil || u.HeadBranch == nil {
-		return ""
-	}
-	return *u.HeadBranch
-}
-
 // GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise.
 func (u *UpdateCheckRunOptions) GetHeadSHA() string {
 	if u == nil || u.HeadSHA == nil {
@@ -12516,6 +12956,94 @@ func (u *User) GetURL() string {
 	return *u.URL
 }
 
+// GetApp returns the App field.
+func (u *UserAuthorization) GetApp() *OAuthAPP {
+	if u == nil {
+		return nil
+	}
+	return u.App
+}
+
+// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
+func (u *UserAuthorization) GetCreatedAt() Timestamp {
+	if u == nil || u.CreatedAt == nil {
+		return Timestamp{}
+	}
+	return *u.CreatedAt
+}
+
+// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise.
+func (u *UserAuthorization) GetFingerprint() string {
+	if u == nil || u.Fingerprint == nil {
+		return ""
+	}
+	return *u.Fingerprint
+}
+
+// GetHashedToken returns the HashedToken field if it's non-nil, zero value otherwise.
+func (u *UserAuthorization) GetHashedToken() string {
+	if u == nil || u.HashedToken == nil {
+		return ""
+	}
+	return *u.HashedToken
+}
+
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (u *UserAuthorization) GetID() int64 {
+	if u == nil || u.ID == nil {
+		return 0
+	}
+	return *u.ID
+}
+
+// GetNote returns the Note field if it's non-nil, zero value otherwise.
+func (u *UserAuthorization) GetNote() string {
+	if u == nil || u.Note == nil {
+		return ""
+	}
+	return *u.Note
+}
+
+// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise.
+func (u *UserAuthorization) GetNoteURL() string {
+	if u == nil || u.NoteURL == nil {
+		return ""
+	}
+	return *u.NoteURL
+}
+
+// GetToken returns the Token field if it's non-nil, zero value otherwise.
+func (u *UserAuthorization) GetToken() string {
+	if u == nil || u.Token == nil {
+		return ""
+	}
+	return *u.Token
+}
+
+// GetTokenLastEight returns the TokenLastEight field if it's non-nil, zero value otherwise.
+func (u *UserAuthorization) GetTokenLastEight() string {
+	if u == nil || u.TokenLastEight == nil {
+		return ""
+	}
+	return *u.TokenLastEight
+}
+
+// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise.
+func (u *UserAuthorization) GetUpdatedAt() Timestamp {
+	if u == nil || u.UpdatedAt == nil {
+		return Timestamp{}
+	}
+	return *u.UpdatedAt
+}
+
+// GetURL returns the URL field if it's non-nil, zero value otherwise.
+func (u *UserAuthorization) GetURL() string {
+	if u == nil || u.URL == nil {
+		return ""
+	}
+	return *u.URL
+}
+
 // GetMessage returns the Message field if it's non-nil, zero value otherwise.
 func (u *UserContext) GetMessage() string {
 	if u == nil || u.Message == nil {
@@ -12564,6 +13092,38 @@ func (u *UserEmail) GetVisibility() string {
 	return *u.Visibility
 }
 
+// GetAction returns the Action field if it's non-nil, zero value otherwise.
+func (u *UserEvent) GetAction() string {
+	if u == nil || u.Action == nil {
+		return ""
+	}
+	return *u.Action
+}
+
+// GetEnterprise returns the Enterprise field.
+func (u *UserEvent) GetEnterprise() *Enterprise {
+	if u == nil {
+		return nil
+	}
+	return u.Enterprise
+}
+
+// GetSender returns the Sender field.
+func (u *UserEvent) GetSender() *User {
+	if u == nil {
+		return nil
+	}
+	return u.Sender
+}
+
+// GetUser returns the User field.
+func (u *UserEvent) GetUser() *User {
+	if u == nil {
+		return nil
+	}
+	return u.User
+}
+
 // 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 0580271c..3f4edd4a 100644
--- a/github/github.go
+++ b/github/github.go
@@ -67,9 +67,6 @@ const (
 	// https://developer.github.com/changes/2016-05-23-timeline-preview-api/
 	mediaTypeTimelinePreview = "application/vnd.github.mockingbird-preview+json"
 
-	// https://developer.github.com/changes/2016-07-06-github-pages-preiew-api/
-	mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json"
-
 	// https://developer.github.com/changes/2016-09-14-projects-api/
 	mediaTypeProjectsPreview = "application/vnd.github.inertia-preview+json"
 
@@ -148,9 +145,6 @@ 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"
 
@@ -648,7 +642,7 @@ type TwoFactorAuthError ErrorResponse
 func (r *TwoFactorAuthError) Error() string { return (*ErrorResponse)(r).Error() }
 
 // RateLimitError occurs when GitHub returns 403 Forbidden response with a rate limit
-// remaining value of 0, and error message starts with "API rate limit exceeded for ".
+// remaining value of 0.
 type RateLimitError struct {
 	Rate     Rate           // Rate specifies last known rate limit for the client
 	Response *http.Response // HTTP response that caused this error
@@ -763,7 +757,7 @@ func CheckResponse(r *http.Response) error {
 	switch {
 	case r.StatusCode == http.StatusUnauthorized && strings.HasPrefix(r.Header.Get(headerOTP), "required"):
 		return (*TwoFactorAuthError)(errorResponse)
-	case r.StatusCode == http.StatusForbidden && r.Header.Get(headerRateRemaining) == "0" && strings.HasPrefix(errorResponse.Message, "API rate limit exceeded for "):
+	case r.StatusCode == http.StatusForbidden && r.Header.Get(headerRateRemaining) == "0":
 		return &RateLimitError{
 			Rate:     parseRate(r),
 			Response: errorResponse.Response,
diff --git a/github/issues_comments.go b/github/issues_comments.go
index ab68afe2..42d6bc78 100644
--- a/github/issues_comments.go
+++ b/github/issues_comments.go
@@ -36,13 +36,13 @@ func (i IssueComment) String() string {
 // IssuesService.ListComments method.
 type IssueListCommentsOptions struct {
 	// Sort specifies how to sort comments. Possible values are: created, updated.
-	Sort string `url:"sort,omitempty"`
+	Sort *string `url:"sort,omitempty"`
 
 	// Direction in which to sort comments. Possible values are: asc, desc.
-	Direction string `url:"direction,omitempty"`
+	Direction *string `url:"direction,omitempty"`
 
 	// Since filters comments by time.
-	Since time.Time `url:"since,omitempty"`
+	Since *time.Time `url:"since,omitempty"`
 
 	ListOptions
 }
diff --git a/github/messages.go b/github/messages.go
index ac7c17f3..a87daa3e 100644
--- a/github/messages.go
+++ b/github/messages.go
@@ -80,6 +80,7 @@ var (
 		"status":                         "StatusEvent",
 		"team":                           "TeamEvent",
 		"team_add":                       "TeamAddEvent",
+		"user":                           "UserEvent",
 		"watch":                          "WatchEvent",
 	}
 )
diff --git a/github/reactions.go b/github/reactions.go
index 0865f8cd..1935ce11 100644
--- a/github/reactions.go
+++ b/github/reactions.go
@@ -44,10 +44,21 @@ func (r Reaction) String() string {
 	return Stringify(r)
 }
 
+// ListCommentReactionOptions specifies the optional parameters to the
+// ReactionsService.ListCommentReactions method.
+type ListCommentReactionOptions struct {
+	// Content restricts the returned comment reactions to only those with the given type.
+	// Omit this parameter to list all reactions to a commit comment.
+	// Possible values are: "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes".
+	Content string `url:"content,omitempty"`
+
+	ListOptions
+}
+
 // ListCommentReactions lists the reactions for a commit comment.
 //
 // GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-commit-comment
-func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) {
+func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListCommentReactionOptions) ([]*Reaction, *Response, error) {
 	u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id)
 	u, err := addOptions(u, opt)
 	if err != nil {
diff --git a/github/repos.go b/github/repos.go
index 37a09d5b..34f22ebc 100644
--- a/github/repos.go
+++ b/github/repos.go
@@ -519,6 +519,26 @@ type ListContributorsOptions struct {
 	ListOptions
 }
 
+// GetVulnerabilityAlerts checks if vulnerability alerts are enabled for a repository.
+//
+// GitHub API docs: https://developer.github.com/v3/repos/#check-if-vulnerability-alerts-are-enabled-for-a-repository
+func (s *RepositoriesService) GetVulnerabilityAlerts(ctx context.Context, owner, repository string) (bool, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/vulnerability-alerts", owner, repository)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return false, nil, err
+	}
+
+	// TODO: remove custom Accept header when this API fully launches
+	req.Header.Set("Accept", mediaTypeRequiredVulnerabilityAlertsPreview)
+
+	resp, err := s.client.Do(ctx, req, nil)
+	vulnerabilityAlertsEnabled, err := parseBoolResponse(err)
+
+	return vulnerabilityAlertsEnabled, resp, err
+}
+
 // EnableVulnerabilityAlerts enables vulnerability alerts and the dependency graph for a repository.
 //
 // GitHub API docs: https://developer.github.com/v3/repos/#enable-vulnerability-alerts
@@ -736,7 +756,7 @@ type RequiredStatusChecksRequest struct {
 // PullRequestReviewsEnforcement represents the pull request reviews enforcement of a protected branch.
 type PullRequestReviewsEnforcement struct {
 	// Specifies which users and teams can dismiss pull request reviews.
-	DismissalRestrictions DismissalRestrictions `json:"dismissal_restrictions"`
+	DismissalRestrictions *DismissalRestrictions `json:"dismissal_restrictions,omitempty"`
 	// Specifies if approved reviews are dismissed automatically, when a new commit is pushed.
 	DismissStaleReviews bool `json:"dismiss_stale_reviews"`
 	// RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner.
@@ -791,6 +811,8 @@ type BranchRestrictions struct {
 	Users []*User `json:"users"`
 	// The list of team slugs with push access.
 	Teams []*Team `json:"teams"`
+	// The list of app slugs with push access.
+	Apps []*App `json:"apps"`
 }
 
 // BranchRestrictionsRequest represents the request to create/edit the
@@ -802,6 +824,8 @@ type BranchRestrictionsRequest struct {
 	Users []string `json:"users"`
 	// The list of team slugs with push access. (Required; use []string{} instead of nil for empty list.)
 	Teams []string `json:"teams"`
+	// The list of app slugs with push access.
+	Apps []string `json:"apps,omitempty"`
 }
 
 // DismissalRestrictions specifies which users and teams can dismiss pull request reviews.
@@ -1134,9 +1158,9 @@ func (s *RepositoriesService) UpdatePullRequestReviewEnforcement(ctx context.Con
 func (s *RepositoriesService) DisableDismissalRestrictions(ctx context.Context, owner, repo, branch string) (*PullRequestReviewsEnforcement, *Response, error) {
 	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch)
 
-	data := struct {
-		R []interface{} `json:"dismissal_restrictions"`
-	}{[]interface{}{}}
+	data := new(struct {
+		DismissalRestrictionsRequest `json:"dismissal_restrictions"`
+	})
 
 	req, err := s.client.NewRequest("PATCH", u, data)
 	if err != nil {
@@ -1287,6 +1311,93 @@ func (s *RepositoriesService) ReplaceAllTopics(ctx context.Context, owner, repo
 	return t.Names, resp, nil
 }
 
+// ListApps lists the Github apps that have push access to a given protected branch.
+// It requires the Github apps to have `write` access to the `content` permission.
+//
+// GitHub API docs: https://developer.github.com/v3/repos/branches/#list-apps-with-access-to-protected-branch
+func (s *RepositoriesService) ListApps(ctx context.Context, owner, repo, branch string) ([]*App, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch)
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var apps []*App
+	resp, err := s.client.Do(ctx, req, &apps)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return apps, resp, nil
+}
+
+// ReplaceAppRestrictions replaces the apps that have push access to a given protected branch.
+// It removes all apps that previously had push access and grants push access to the new list of apps.
+// It requires the Github apps to have `write` access to the `content` permission.
+//
+// Note: The list of users, apps, and teams in total is limited to 100 items.
+//
+// GitHub API docs: https://developer.github.com/v3/repos/branches/#replace-app-restrictions-of-protected-branch
+func (s *RepositoriesService) ReplaceAppRestrictions(ctx context.Context, owner, repo, branch string, slug []string) ([]*App, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch)
+	req, err := s.client.NewRequest("PUT", u, slug)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var apps []*App
+	resp, err := s.client.Do(ctx, req, &apps)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return apps, resp, nil
+}
+
+// AddAppRestrictions grants the specified apps push access to a given protected branch.
+// It requires the Github apps to have `write` access to the `content` permission.
+//
+// Note: The list of users, apps, and teams in total is limited to 100 items.
+//
+// GitHub API docs: https://developer.github.com/v3/repos/branches/#add-app-restrictions-of-protected-branch
+func (s *RepositoriesService) AddAppRestrictions(ctx context.Context, owner, repo, branch string, slug []string) ([]*App, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch)
+	req, err := s.client.NewRequest("POST", u, slug)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var apps []*App
+	resp, err := s.client.Do(ctx, req, &apps)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return apps, resp, nil
+}
+
+// RemoveAppRestrictions removes the ability of an app to push to this branch.
+// It requires the Github apps to have `write` access to the `content` permission.
+//
+// Note: The list of users, apps, and teams in total is limited to 100 items.
+//
+// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-app-restrictions-of-protected-branch
+func (s *RepositoriesService) RemoveAppRestrictions(ctx context.Context, owner, repo, branch string, slug []string) ([]*App, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch)
+	req, err := s.client.NewRequest("DELETE", u, slug)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var apps []*App
+	resp, err := s.client.Do(ctx, req, &apps)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return apps, resp, nil
+}
+
 // TransferRequest represents a request to transfer a repository.
 type TransferRequest struct {
 	NewOwner string  `json:"new_owner"`
diff --git a/github/repos_collaborators.go b/github/repos_collaborators.go
index 757e9f39..04d80687 100644
--- a/github/repos_collaborators.go
+++ b/github/repos_collaborators.go
@@ -26,6 +26,19 @@ type ListCollaboratorsOptions struct {
 	ListOptions
 }
 
+// CollaboratorInvitation represents an invitation created when adding a collaborator.
+// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#response-when-a-new-invitation-is-created
+type CollaboratorInvitation struct {
+	ID          *int64      `json:"id,omitempty"`
+	Repo        *Repository `json:"repository,omitempty"`
+	Invitee     *User       `json:"invitee,omitempty"`
+	Inviter     *User       `json:"inviter,omitempty"`
+	Permissions *string     `json:"permissions,omitempty"`
+	CreatedAt   *Timestamp  `json:"created_at,omitempty"`
+	URL         *string     `json:"url,omitempty"`
+	HTMLURL     *string     `json:"html_url,omitempty"`
+}
+
 // ListCollaborators lists the GitHub users that have access to the repository.
 //
 // GitHub API docs: https://developer.github.com/v3/repos/collaborators/#list-collaborators
@@ -113,14 +126,18 @@ type RepositoryAddCollaboratorOptions struct {
 // to become a collaborator to the given repo.
 //
 // GitHub API docs: https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator
-func (s *RepositoriesService) AddCollaborator(ctx context.Context, owner, repo, user string, opt *RepositoryAddCollaboratorOptions) (*Response, error) {
+func (s *RepositoriesService) AddCollaborator(ctx context.Context, owner, repo, user string, opt *RepositoryAddCollaboratorOptions) (*CollaboratorInvitation, *Response, error) {
 	u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user)
 	req, err := s.client.NewRequest("PUT", u, opt)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
-
-	return s.client.Do(ctx, req, nil)
+	acr := new(CollaboratorInvitation)
+	resp, err := s.client.Do(ctx, req, acr)
+	if err != nil {
+		return nil, resp, err
+	}
+	return acr, resp, nil
 }
 
 // RemoveCollaborator removes the specified GitHub user as collaborator from the given repo.
diff --git a/github/repos_contents.go b/github/repos_contents.go
index bf6cabc5..eed0420a 100644
--- a/github/repos_contents.go
+++ b/github/repos_contents.go
@@ -240,15 +240,28 @@ const (
 // or github.Zipball constant.
 //
 // GitHub API docs: https://developer.github.com/v3/repos/contents/#get-archive-link
-func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo string, archiveformat archiveFormat, opt *RepositoryContentGetOptions) (*url.URL, *Response, error) {
+func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo string, archiveformat archiveFormat, opt *RepositoryContentGetOptions, followRedirects bool) (*url.URL, *Response, error) {
 	u := fmt.Sprintf("repos/%s/%s/%s", owner, repo, archiveformat)
 	if opt != nil && opt.Ref != "" {
 		u += fmt.Sprintf("/%s", opt.Ref)
 	}
-	req, err := s.client.NewRequest("GET", u, nil)
+	resp, err := s.getArchiveLinkFromURL(ctx, u, followRedirects)
 	if err != nil {
 		return nil, nil, err
 	}
+	if resp.StatusCode != http.StatusFound {
+		return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status)
+	}
+	parsedURL, err := url.Parse(resp.Header.Get("Location"))
+	return parsedURL, newResponse(resp), err
+}
+
+func (s *RepositoriesService) getArchiveLinkFromURL(ctx context.Context, u string, followRedirects bool) (*http.Response, error) {
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, err
+	}
+
 	var resp *http.Response
 	// Use http.DefaultTransport if no custom Transport is configured
 	req = withContext(ctx, req)
@@ -258,12 +271,14 @@ func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo st
 		resp, err = s.client.client.Transport.RoundTrip(req)
 	}
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	resp.Body.Close()
-	if resp.StatusCode != http.StatusFound {
-		return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status)
+
+	// If redirect response is returned, follow it
+	if followRedirects && resp.StatusCode == http.StatusMovedPermanently {
+		u = resp.Header.Get("Location")
+		resp, err = s.getArchiveLinkFromURL(ctx, u, false)
 	}
-	parsedURL, err := url.Parse(resp.Header.Get("Location"))
-	return parsedURL, newResponse(resp), err
+	return resp, err
 }
diff --git a/github/repos_pages.go b/github/repos_pages.go
index 28674130..7eb32a9d 100644
--- a/github/repos_pages.go
+++ b/github/repos_pages.go
@@ -8,7 +8,6 @@ package github
 import (
 	"context"
 	"fmt"
-	"strings"
 )
 
 // Pages represents a GitHub Pages site configuration.
@@ -54,9 +53,7 @@ func (s *RepositoriesService) EnablePages(ctx context.Context, owner, repo strin
 		return nil, nil, err
 	}
 
-	// TODO: remove custom Accept header when this API fully launches.
-	acceptHeaders := []string{mediaTypeEnablePagesAPIPreview, mediaTypePagesPreview}
-	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
+	req.Header.Set("Accept", mediaTypeEnablePagesAPIPreview)
 
 	enable := new(Pages)
 	resp, err := s.client.Do(ctx, req, enable)
@@ -93,9 +90,6 @@ func (s *RepositoriesService) GetPagesInfo(ctx context.Context, owner, repo stri
 		return nil, nil, err
 	}
 
-	// TODO: remove custom Accept header when this API fully launches.
-	req.Header.Set("Accept", mediaTypePagesPreview)
-
 	site := new(Pages)
 	resp, err := s.client.Do(ctx, req, site)
 	if err != nil {
@@ -177,9 +171,6 @@ func (s *RepositoriesService) RequestPageBuild(ctx context.Context, owner, repo
 		return nil, nil, err
 	}
 
-	// TODO: remove custom Accept header when this API fully launches.
-	req.Header.Set("Accept", mediaTypePagesPreview)
-
 	build := new(PagesBuild)
 	resp, err := s.client.Do(ctx, req, build)
 	if err != nil {
diff --git a/github/search.go b/github/search.go
index 13cc4c84..4e66ba1c 100644
--- a/github/search.go
+++ b/github/search.go
@@ -76,6 +76,37 @@ func (s *SearchService) Repositories(ctx context.Context, query string, opt *Sea
 	return result, resp, err
 }
 
+// TopicsSearchResult represents the result of a topics search.
+type TopicsSearchResult struct {
+	Total             *int           `json:"total_count,omitempty"`
+	IncompleteResults *bool          `json:"incomplete_results,omitempty"`
+	Topics            []*TopicResult `json:"items,omitempty"`
+}
+
+type TopicResult struct {
+	Name             *string    `json:"name,omitempty"`
+	DisplayName      *string    `json:"display_name,omitempty"`
+	ShortDescription *string    `json:"short_description,omitempty"`
+	Description      *string    `json:"description,omitempty"`
+	CreatedBy        *string    `json:"created_by,omitempty"`
+	CreatedAt        *Timestamp `json:"created_at,omitempty"`
+	UpdatedAt        *string    `json:"updated_at,omitempty"`
+	Featured         *bool      `json:"featured,omitempty"`
+	Curated          *bool      `json:"curated,omitempty"`
+	Score            *float64   `json:"score,omitempty"`
+}
+
+// Topics finds topics via various criteria. Results are sorted by best match.
+// Please see https://help.github.com/en/articles/searching-topics for more
+// information about search qualifiers.
+//
+// GitHub API docs: https://developer.github.com/v3/search/#search-topics
+func (s *SearchService) Topics(ctx context.Context, query string, opt *SearchOptions) (*TopicsSearchResult, *Response, error) {
+	result := new(TopicsSearchResult)
+	resp, err := s.search(ctx, "topics", &searchParameters{Query: query}, opt, result)
+	return result, resp, err
+}
+
 // CommitsSearchResult represents the result of a commits search.
 type CommitsSearchResult struct {
 	Total             *int            `json:"total_count,omitempty"`
@@ -245,6 +276,10 @@ func (s *SearchService) search(ctx context.Context, searchType string, parameter
 		// Accept header for search commits preview endpoint
 		// TODO: remove custom Accept header when this API fully launches.
 		req.Header.Set("Accept", mediaTypeCommitSearchPreview)
+	case searchType == "topics":
+		// Accept header for search repositories based on topics preview endpoint
+		// TODO: remove custom Accept header when this API fully launches.
+		req.Header.Set("Accept", mediaTypeTopicsPreview)
 	case searchType == "repositories":
 		// Accept header for search repositories based on topics preview endpoint
 		// TODO: remove custom Accept header when this API fully launches.
diff --git a/github/teams.go b/github/teams.go
index b4953e63..7f00b986 100644
--- a/github/teams.go
+++ b/github/teams.go
@@ -504,9 +504,6 @@ func (s *TeamsService) ListIDPGroupsInOrganization(ctx context.Context, org stri
 		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 {
@@ -526,9 +523,6 @@ func (s *TeamsService) ListIDPGroupsForTeam(ctx context.Context, teamID string)
 		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 {
@@ -549,9 +543,6 @@ func (s *TeamsService) CreateOrUpdateIDPGroupConnections(ctx context.Context, te
 		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 {
diff --git a/github/users_projects.go b/github/users_projects.go
new file mode 100644
index 00000000..bd2ca75e
--- /dev/null
+++ b/github/users_projects.go
@@ -0,0 +1,68 @@
+// 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"
+	"fmt"
+)
+
+// ListProjects lists the projects for the specified user.
+//
+// GitHub API docs: https://developer.github.com/v3/projects/#list-user-projects
+func (s *UsersService) ListProjects(ctx context.Context, user string, opt *ProjectListOptions) ([]*Project, *Response, error) {
+	u := fmt.Sprintf("users/%v/projects", user)
+	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", mediaTypeProjectsPreview)
+
+	var projects []*Project
+	resp, err := s.client.Do(ctx, req, &projects)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return projects, resp, nil
+}
+
+// CreateUserProjectOptions specifies the parameters to the UsersService.CreateProject method.
+type CreateUserProjectOptions struct {
+	// The name of the project. (Required.)
+	Name string `json:"name"`
+	// The description of the project. (Optional.)
+	Body *string `json:"body,omitempty"`
+}
+
+// CreateProject creates a GitHub Project for the current user.
+//
+// GitHub API docs: https://developer.github.com/v3/projects/#create-a-user-project
+func (s *UsersService) CreateProject(ctx context.Context, opt *CreateUserProjectOptions) (*Project, *Response, error) {
+	u := "users/projects"
+	req, err := s.client.NewRequest("POST", u, opt)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// TODO: remove custom Accept header when this API fully launches.
+	req.Header.Set("Accept", mediaTypeProjectsPreview)
+
+	project := &Project{}
+	resp, err := s.client.Do(ctx, req, project)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return project, resp, nil
+}
diff --git a/scrape/apps.go b/scrape/apps.go
index 0c52bad8..eb2f9005 100644
--- a/scrape/apps.go
+++ b/scrape/apps.go
@@ -9,11 +9,15 @@
 package scrape
 
 import (
+	"bytes"
+	"encoding/json"
 	"errors"
+	"net/http"
 	"strconv"
 	"strings"
 
 	"github.com/PuerkitoBio/goquery"
+	"github.com/google/go-github/v28/github"
 )
 
 // AppRestrictionsEnabled returns whether the specified organization has
@@ -103,3 +107,40 @@ type OAuthApp struct {
 	State       OAuthAppReviewState
 	RequestedBy string
 }
+
+// AppManifest represents a GitHub App manifest, used for preconfiguring
+// GitHub App configuration.
+type AppManifest struct {
+	// The name of the GitHub App.
+	Name *string `json:"name,omitempty"`
+	//Required. The homepage of your GitHub App.
+	URL *string `json:"url,omitempty"`
+	// Required. The configuration of the GitHub App's webhook.
+	HookAttributes map[string]string `json:"hook_attributes,omitempty"`
+	// The full URL to redirect to after the person installs the GitHub App.
+	RedirectURL *string `json:"redirect_url,omitempty"`
+	// A description of the GitHub App.
+	Description *string `json:"description,omitempty"`
+	// Set to true when your GitHub App is available to the public or false when
+	// it is only accessible to the owner of the app.
+	Public *bool `json:"public,omitempty"`
+	// The list of events the GitHub App subscribes to.
+	DefaultEvents []string `json:"default_events,omitempty"`
+	// The set of permissions needed by the GitHub App.
+	DefaultPermissions *github.InstallationPermissions `json:"default_permissions,omitempty"`
+}
+
+// CreateApp creates a new GitHub App with the given manifest configuration.
+func (c *Client) CreateApp(m *AppManifest) (*http.Response, error) {
+	u, err := c.baseURL.Parse("/settings/apps/new")
+	if err != nil {
+		return nil, err
+	}
+
+	body, err := json.Marshal(map[string]*AppManifest{"manifest": m})
+	if err != nil {
+		return nil, err
+	}
+
+	return c.Client.Post(u.String(), "json", bytes.NewReader(body))
+}
diff --git a/scrape/go.mod b/scrape/go.mod
index aa575d7a..f936c07d 100644
--- a/scrape/go.mod
+++ b/scrape/go.mod
@@ -5,6 +5,7 @@ go 1.13
 require (
 	github.com/PuerkitoBio/goquery v1.5.0
 	github.com/google/go-cmp v0.3.1
+	github.com/google/go-github/v28 v28.1.1
 	github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
 	golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582
 )
diff --git a/scrape/go.sum b/scrape/go.sum
index 1894f0bd..7f226791 100644
--- a/scrape/go.sum
+++ b/scrape/go.sum
@@ -2,14 +2,24 @@ github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP
 github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
 github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
 github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
+github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
+github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4=
 github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA=
 golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
diff --git a/scrape/payment.go b/scrape/payment.go
new file mode 100644
index 00000000..fff9a753
--- /dev/null
+++ b/scrape/payment.go
@@ -0,0 +1,51 @@
+// Copyright 2013 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.
+
+// apps.go contains functions for accessing data about applications installed
+// on a GitHub organization.
+
+package scrape
+
+import (
+	"strings"
+
+	"github.com/PuerkitoBio/goquery"
+)
+
+// OrgPaymentInformation returns payment information for the specified org.
+func (c *Client) OrgPaymentInformation(org string) (PaymentInformation, error) {
+	var info PaymentInformation
+
+	doc, err := c.get("/organizations/%s/settings/billing/payment_information", org)
+	if err != nil {
+		return info, err
+	}
+
+	doc.Find("main h4.mb-1").Each(func(i int, s *goquery.Selection) {
+		name := strings.TrimSpace(strings.ToLower(s.Text()))
+		value := strings.Join(strings.Fields(strings.TrimSpace(s.NextFiltered("p").Text())), " ")
+
+		switch name {
+		case "payment method":
+			info.PaymentMethod = value
+		case "last payment":
+			info.LastPayment = value
+		case "coupon":
+			info.Coupon = value
+		case "extra information":
+			info.ExtraInformation = value
+		}
+	})
+
+	return info, nil
+}
+
+// PaymentInformation for an organization on a paid plan.
+type PaymentInformation struct {
+	PaymentMethod    string
+	LastPayment      string
+	Coupon           string
+	ExtraInformation string
+}


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 00000000..d89f367e
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,69 @@
+on: [push, pull_request]
+name: tests
+env:
+  GO111MODULE: on
+
+jobs:
+  linux:
+    strategy:
+      matrix:
+        go-version: [1.x, 1.12.x]
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/setup-go@v1
+      with:
+        go-version: ${{ matrix.go-version }}
+    - uses: actions/checkout@v1
+
+    - name: Cache go modules
+      uses: actions/cache@preview
+      with:
+        path: ~/go/pkg/mod
+        key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
+        restore-keys: ${{ runner.os }}-go-
+
+    - name: Run go fmt
+      run: diff -u <(echo -n) <(gofmt -d -s .)
+
+    - name: Ensure go generate produces a zero diff
+      run:  go generate -x ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code)
+
+    - name: Run go vet
+      run: go vet ./...
+
+    - name: Run go test
+      run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
+
+    - name: Ensure integration tests build
+      # don't actually run tests since they hit live GitHub API
+      run: go test -v -tags=integration -run=^$ ./test/integration
+
+    - name: Run scrape tests
+      run: cd scrape && go test ./...
+
+    - name: Upload coverage to Codecov
+      env:
+        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+      run: bash <(curl -s https://codecov.io/bash)
+
+  # On Windows, just run the local tests.  Don't bother with checking gofmt, go
+  # vet, or uploading results to Codecov
+  windows:
+    strategy:
+      matrix:
+        go-version: [1.x, 1.12.x]
+    runs-on: windows-latest
+
+    steps:
+    - uses: actions/setup-go@v1
+      with:
+        go-version: ${{ matrix.go-version }}
+    - uses: actions/checkout@v1
+    - name: Cache go modules
+      uses: actions/cache@preview
+      with:
+        path: ~/go/pkg/mod
+        key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
+        restore-keys: ${{ runner.os }}-go-
+    - run: go test ./...
diff --git a/github/admin_users_test.go b/github/admin_users_test.go
index ad5379e2..2c3b6d7a 100644
--- a/github/admin_users_test.go
+++ b/github/admin_users_test.go
@@ -12,6 +12,7 @@ import (
 	"net/http"
 	"reflect"
 	"testing"
+	"time"
 )
 
 func TestAdminUsers_Create(t *testing.T) {
@@ -55,3 +56,74 @@ func TestAdminUsers_Delete(t *testing.T) {
 		t.Errorf("Admin.DeleteUser returned error: %v", err)
 	}
 }
+
+func TestUserImpersonation_Create(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/admin/users/github/authorizations", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		testBody(t, r, `{"scopes":["repo"]}`+"\n")
+		fmt.Fprint(w, `{"id": 1234,
+		"url": "https://git.company.com/api/v3/authorizations/1234",
+		"app": {
+		  "name": "GitHub Site Administrator",
+		  "url": "https://developer.github.com/v3/enterprise/users/",
+		  "client_id": "1234"
+		},
+		"token": "1234",
+		"hashed_token": "1234",
+		"token_last_eight": "1234",
+		"note": null,
+		"note_url": null,
+		"created_at": "2018-01-01T00:00:00Z",
+		"updated_at": "2018-01-01T00:00:00Z",
+		"scopes": [
+		  "repo"
+		],
+		"fingerprint": null}`)
+	})
+
+	opt := &ImpersonateUserOptions{Scopes: []string{"repo"}}
+	auth, _, err := client.Admin.CreateUserImpersonation(context.Background(), "github", opt)
+	if err != nil {
+		t.Errorf("Admin.CreateUserImpersonation returned error: %v", err)
+	}
+
+	date := Timestamp{Time: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC)}
+	want := &UserAuthorization{
+		ID:  Int64(1234),
+		URL: String("https://git.company.com/api/v3/authorizations/1234"),
+		App: &OAuthAPP{
+			Name:     String("GitHub Site Administrator"),
+			URL:      String("https://developer.github.com/v3/enterprise/users/"),
+			ClientID: String("1234"),
+		},
+		Token:          String("1234"),
+		HashedToken:    String("1234"),
+		TokenLastEight: String("1234"),
+		Note:           nil,
+		NoteURL:        nil,
+		CreatedAt:      &date,
+		UpdatedAt:      &date,
+		Scopes:         []string{"repo"},
+		Fingerprint:    nil,
+	}
+	if !reflect.DeepEqual(auth, want) {
+		t.Errorf("Admin.CreateUserImpersonation returned %+v, want %+v", auth, want)
+	}
+}
+
+func TestUserImpersonation_Delete(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/admin/users/github/authorizations", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+	})
+
+	_, err := client.Admin.DeleteUserImpersonation(context.Background(), "github")
+	if err != nil {
+		t.Errorf("Admin.DeleteUserImpersonation returned error: %v", err)
+	}
+}
diff --git a/github/apps_manifest_test.go b/github/apps_manifest_test.go
new file mode 100644
index 00000000..92a2be9d
--- /dev/null
+++ b/github/apps_manifest_test.go
@@ -0,0 +1,53 @@
+// 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"
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+)
+
+const (
+	manifestJSON = `{
+	"id": 1,
+  "client_id": "a" ,
+  "client_secret": "b",
+  "webhook_secret": "c",
+  "pem": "key"
+}
+`
+)
+
+func TestGetConfig(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/app-manifests/code/conversions", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		testHeader(t, r, "Accept", mediaTypeAppManifestPreview)
+		fmt.Fprint(w, manifestJSON)
+	})
+
+	cfg, _, err := client.Apps.CompleteAppManifest(context.Background(), "code")
+	if err != nil {
+		t.Errorf("AppManifest.GetConfig returned error: %v", err)
+	}
+
+	want := &AppConfig{
+		ID:            Int64(1),
+		ClientID:      String("a"),
+		ClientSecret:  String("b"),
+		WebhookSecret: String("c"),
+		PEM:           String("key"),
+	}
+
+	if !reflect.DeepEqual(cfg, want) {
+		t.Errorf("GetConfig returned %+v, want %+v", cfg, want)
+	}
+}
diff --git a/github/checks_test.go b/github/checks_test.go
index 848c7e7d..e510e32f 100644
--- a/github/checks_test.go
+++ b/github/checks_test.go
@@ -102,11 +102,10 @@ func TestChecksService_CreateCheckRun(t *testing.T) {
 	})
 	startedAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z")
 	checkRunOpt := CreateCheckRunOptions{
-		HeadBranch: "master",
-		Name:       "testCreateCheckRun",
-		HeadSHA:    "deadbeef",
-		Status:     String("in_progress"),
-		StartedAt:  &Timestamp{startedAt},
+		Name:      "testCreateCheckRun",
+		HeadSHA:   "deadbeef",
+		Status:    String("in_progress"),
+		StartedAt: &Timestamp{startedAt},
 		Output: &CheckRunOutput{
 			Title:   String("Mighty test report"),
 			Summary: String(""),
@@ -200,7 +199,6 @@ func TestChecksService_UpdateCheckRun(t *testing.T) {
 	})
 	startedAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z")
 	updateCheckRunOpt := UpdateCheckRunOptions{
-		HeadBranch:  String("master"),
 		Name:        "testUpdateCheckRun",
 		HeadSHA:     String("deadbeef"),
 		Status:      String("completed"),
diff --git a/github/git_commits_test.go b/github/git_commits_test.go
index f316f01e..6e858baf 100644
--- a/github/git_commits_test.go
+++ b/github/git_commits_test.go
@@ -18,6 +18,106 @@ import (
 	"golang.org/x/crypto/openpgp"
 )
 
+func TestCommit_Marshal(t *testing.T) {
+	testJSONMarshal(t, &Commit{}, "{}")
+
+	u := &Commit{
+		SHA: String("s"),
+		Author: &CommitAuthor{
+			Date:  &referenceTime,
+			Name:  String("n"),
+			Email: String("e"),
+			Login: String("u"),
+		},
+		Committer: &CommitAuthor{
+			Date:  &referenceTime,
+			Name:  String("n"),
+			Email: String("e"),
+			Login: String("u"),
+		},
+		Message: String("m"),
+		Tree: &Tree{
+			SHA: String("s"),
+			Entries: []TreeEntry{{
+				SHA:     String("s"),
+				Path:    String("p"),
+				Mode:    String("m"),
+				Type:    String("t"),
+				Size:    Int(1),
+				Content: String("c"),
+				URL:     String("u"),
+			}},
+			Truncated: Bool(false),
+		},
+		Parents: nil,
+		Stats: &CommitStats{
+			Additions: Int(1),
+			Deletions: Int(1),
+			Total:     Int(1),
+		},
+		HTMLURL: String("h"),
+		URL:     String("u"),
+		Verification: &SignatureVerification{
+			Verified:  Bool(false),
+			Reason:    String("r"),
+			Signature: String("s"),
+			Payload:   String("p"),
+		},
+		NodeID:       String("n"),
+		CommentCount: Int(1),
+		SigningKey:   &openpgp.Entity{},
+	}
+
+	want := `{
+		"sha": "s",
+		"author": {
+			"date": ` + referenceTimeStr + `,
+			"name": "n",
+			"email": "e",
+			"username": "u"
+		},
+		"committer": {
+			"date": ` + referenceTimeStr + `,
+			"name": "n",
+			"email": "e",
+			"username": "u"
+		},
+		"message": "m",
+		"tree": {
+			"sha": "s",
+			"tree": [
+				{
+					"sha": "s",
+					"path": "p",
+					"mode": "m",
+					"type": "t",
+					"size": 1,
+					"content": "c",
+					"url": "u"
+				}
+			],
+			"truncated": false
+		},
+		"stats": {
+			"additions": 1,
+			"deletions": 1,
+			"total": 1
+		},
+		"html_url": "h",
+		"url": "u",
+		"verification": {
+			"verified": false,
+			"reason": "r",
+			"signature": "s",
+			"payload": "p"
+		},
+		"node_id": "n",
+		"comment_count": 1
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
 func TestGitService_GetCommit(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
diff --git a/github/git_trees_test.go b/github/git_trees_test.go
index 1f7024fc..1d1b789c 100644
--- a/github/git_trees_test.go
+++ b/github/git_trees_test.go
@@ -186,6 +186,72 @@ func TestGitService_CreateTree_Content(t *testing.T) {
 	}
 }
 
+func TestGitService_CreateTree_Delete(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := []TreeEntry{
+		{
+			Path: String("content.md"),
+			Mode: String("100644"),
+		},
+	}
+
+	mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) {
+		v := new(createTree)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "POST")
+
+		want := &createTree{
+			BaseTree: "b",
+			Entries:  input,
+		}
+		if !reflect.DeepEqual(v, want) {
+			t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want)
+		}
+
+		fmt.Fprint(w, `{
+		  "sha": "5c6780ad2c68743383b740fd1dab6f6a33202b11",
+		  "url": "https://api.github.com/repos/o/r/git/trees/5c6780ad2c68743383b740fd1dab6f6a33202b11",
+		  "tree": [
+		    {
+			  "mode": "100644",
+			  "type": "blob",
+			  "sha":  null,
+			  "path": "content.md",
+			  "size": 12,
+			  "url": "https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b"
+		    }
+		  ]
+		}`)
+	})
+
+	tree, _, err := client.Git.CreateTree(context.Background(), "o", "r", "b", input)
+	if err != nil {
+		t.Errorf("Git.CreateTree returned error: %v", err)
+	}
+
+	want := Tree{
+		String("5c6780ad2c68743383b740fd1dab6f6a33202b11"),
+		[]TreeEntry{
+			{
+				Path: String("content.md"),
+				Mode: String("100644"),
+				Type: String("blob"),
+				Size: Int(12),
+				SHA:  nil,
+				URL:  String("https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b"),
+			},
+		},
+		nil,
+	}
+
+	if !reflect.DeepEqual(*tree, want) {
+		t.Errorf("Git.CreateTree returned %+v, want %+v", *tree, want)
+	}
+}
+
 func TestGitService_CreateTree_invalidOwner(t *testing.T) {
 	client, _, _, teardown := setup()
 	defer teardown()
diff --git a/github/github-stringify_test.go b/github/github-stringify_test.go
index f7e6d2b4..04cd771c 100644
--- a/github/github-stringify_test.go
+++ b/github/github-stringify_test.go
@@ -307,6 +307,25 @@ func TestDraftReviewComment_String(t *testing.T) {
 	}
 }
 
+func TestEnterprise_String(t *testing.T) {
+	v := Enterprise{
+		ID:          Int(0),
+		Slug:        String(""),
+		Name:        String(""),
+		NodeID:      String(""),
+		AvatarURL:   String(""),
+		Description: String(""),
+		WebsiteURL:  String(""),
+		HTMLURL:     String(""),
+		CreatedAt:   &Timestamp{},
+		UpdatedAt:   &Timestamp{},
+	}
+	want := `github.Enterprise{ID:0, Slug:"", Name:"", NodeID:"", AvatarURL:"", Description:"", WebsiteURL:"", HTMLURL:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}`
+	if got := v.String(); got != want {
+		t.Errorf("Enterprise.String = %v, want %v", got, want)
+	}
+}
+
 func TestEvent_String(t *testing.T) {
 	v := Event{
 		Type:   String(""),
@@ -779,6 +798,18 @@ func TestNewTeam_String(t *testing.T) {
 	}
 }
 
+func TestOAuthAPP_String(t *testing.T) {
+	v := OAuthAPP{
+		URL:      String(""),
+		Name:     String(""),
+		ClientID: String(""),
+	}
+	want := `github.OAuthAPP{URL:"", Name:"", ClientID:""}`
+	if got := v.String(); got != want {
+		t.Errorf("OAuthAPP.String = %v, want %v", got, want)
+	}
+}
+
 func TestOrgStats_String(t *testing.T) {
 	v := OrgStats{
 		TotalOrgs:        Int(0),
diff --git a/github/github_test.go b/github/github_test.go
index 9dfac059..759d76c5 100644
--- a/github/github_test.go
+++ b/github/github_test.go
@@ -6,7 +6,6 @@
 package github
 
 import (
-	"bytes"
 	"context"
 	"encoding/json"
 	"fmt"
@@ -135,32 +134,29 @@ func testBody(t *testing.T, r *http.Request, want string) {
 	}
 }
 
-// Helper function to test that a value is marshalled to JSON as expected.
+// Test whether the marshaling of v produces JSON that corresponds
+// to the want string.
 func testJSONMarshal(t *testing.T, v interface{}, want string) {
-	j, err := json.Marshal(v)
+	// Unmarshal the wanted JSON, to verify its correctness, and marshal it back
+	// to sort the keys.
+	u := reflect.New(reflect.TypeOf(v)).Interface()
+	if err := json.Unmarshal([]byte(want), &u); err != nil {
+		t.Errorf("Unable to unmarshal JSON for %v: %v", want, err)
+	}
+	w, err := json.Marshal(u)
 	if err != nil {
-		t.Errorf("Unable to marshal JSON for %v", v)
+		t.Errorf("Unable to marshal JSON for %#v", u)
 	}
 
-	w := new(bytes.Buffer)
-	err = json.Compact(w, []byte(want))
+	// Marshal the target value.
+	j, err := json.Marshal(v)
 	if err != nil {
-		t.Errorf("String is not valid json: %s", want)
+		t.Errorf("Unable to marshal JSON for %#v", v)
 	}
 
-	if w.String() != string(j) {
+	if string(w) != string(j) {
 		t.Errorf("json.Marshal(%q) returned %s, want %s", v, j, w)
 	}
-
-	// now go the other direction and make sure things unmarshal as expected
-	u := reflect.ValueOf(v).Interface()
-	if err := json.Unmarshal([]byte(want), u); err != nil {
-		t.Errorf("Unable to unmarshal JSON for %v: %v", want, err)
-	}
-
-	if !reflect.DeepEqual(v, u) {
-		t.Errorf("json.Unmarshal(%q) returned %s, want %s", want, u, v)
-	}
 }
 
 func TestNewClient(t *testing.T) {
@@ -862,6 +858,56 @@ func TestCheckResponse(t *testing.T) {
 	}
 }
 
+func TestCheckResponse_RateLimit(t *testing.T) {
+	res := &http.Response{
+		Request:    &http.Request{},
+		StatusCode: http.StatusForbidden,
+		Header:     http.Header{},
+		Body: ioutil.NopCloser(strings.NewReader(`{"message":"m",
+			"documentation_url": "url"}`)),
+	}
+	res.Header.Set(headerRateLimit, "60")
+	res.Header.Set(headerRateRemaining, "0")
+	res.Header.Set(headerRateReset, "243424")
+
+	err := CheckResponse(res).(*RateLimitError)
+
+	if err == nil {
+		t.Errorf("Expected error response.")
+	}
+
+	want := &RateLimitError{
+		Rate:     parseRate(res),
+		Response: res,
+		Message:  "m",
+	}
+	if !reflect.DeepEqual(err, want) {
+		t.Errorf("Error = %#v, want %#v", err, want)
+	}
+}
+
+func TestCheckResponse_AbuseRateLimit(t *testing.T) {
+	res := &http.Response{
+		Request:    &http.Request{},
+		StatusCode: http.StatusForbidden,
+		Body: ioutil.NopCloser(strings.NewReader(`{"message":"m",
+			"documentation_url": "developer.github.com/v3/#abuse-rate-limits"}`)),
+	}
+	err := CheckResponse(res).(*AbuseRateLimitError)
+
+	if err == nil {
+		t.Errorf("Expected error response.")
+	}
+
+	want := &AbuseRateLimitError{
+		Response: res,
+		Message:  "m",
+	}
+	if !reflect.DeepEqual(err, want) {
+		t.Errorf("Error = %#v, want %#v", err, want)
+	}
+}
+
 // ensure that we properly handle API errors that do not contain a response body
 func TestCheckResponse_noBody(t *testing.T) {
 	res := &http.Response{
diff --git a/github/issues_comments_test.go b/github/issues_comments_test.go
index 6911a798..69acb242 100644
--- a/github/issues_comments_test.go
+++ b/github/issues_comments_test.go
@@ -31,10 +31,11 @@ func TestIssuesService_ListComments_allIssues(t *testing.T) {
 		fmt.Fprint(w, `[{"id":1}]`)
 	})
 
+	since := time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)
 	opt := &IssueListCommentsOptions{
-		Sort:        "updated",
-		Direction:   "desc",
-		Since:       time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC),
+		Sort:        String("updated"),
+		Direction:   String("desc"),
+		Since:       &since,
 		ListOptions: ListOptions{Page: 2},
 	}
 	comments, _, err := client.Issues.ListComments(context.Background(), "o", "r", 0, opt)
diff --git a/github/messages_test.go b/github/messages_test.go
index 105904c6..30f7d95f 100644
--- a/github/messages_test.go
+++ b/github/messages_test.go
@@ -339,6 +339,10 @@ func TestParseWebHook(t *testing.T) {
 			payload:     &TeamAddEvent{},
 			messageType: "team_add",
 		},
+		{
+			payload:     &UserEvent{},
+			messageType: "user",
+		},
 		{
 			payload:     &WatchEvent{},
 			messageType: "watch",
diff --git a/github/orgs_test.go b/github/orgs_test.go
index 9383bf11..c39f6097 100644
--- a/github/orgs_test.go
+++ b/github/orgs_test.go
@@ -14,6 +14,38 @@ import (
 	"testing"
 )
 
+func TestOrganization_marshal(t *testing.T) {
+	testJSONMarshal(t, &Organization{}, "{}")
+
+	o := &Organization{
+		BillingEmail:                         String("support@github.com"),
+		Blog:                                 String("https://github.com/blog"),
+		Company:                              String("GitHub"),
+		Email:                                String("support@github.com"),
+		Location:                             String("San Francisco"),
+		Name:                                 String("github"),
+		Description:                          String("GitHub, the company."),
+		DefaultRepoPermission:                String("read"),
+		MembersCanCreateRepos:                Bool(true),
+		MembersAllowedRepositoryCreationType: String("all"),
+	}
+	want := `
+		{
+			"billing_email": "support@github.com",
+			"blog": "https://github.com/blog",
+			"company": "GitHub",
+			"email": "support@github.com",
+			"location": "San Francisco",
+			"name": "github",
+			"description": "GitHub, the company.",
+			"default_repository_permission": "read",
+			"members_can_create_repositories": true,
+			"members_allowed_repository_creation_type": "all"
+		}
+	`
+	testJSONMarshal(t, o, want)
+}
+
 func TestOrganizationsService_ListAll(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
diff --git a/github/reactions_test.go b/github/reactions_test.go
index ef1660d9..c32274de 100644
--- a/github/reactions_test.go
+++ b/github/reactions_test.go
@@ -7,6 +7,7 @@ package github
 
 import (
 	"context"
+	"fmt"
 	"net/http"
 	"reflect"
 	"testing"
@@ -67,17 +68,18 @@ func TestReactionsService_ListCommentReactions(t *testing.T) {
 		testMethod(t, r, "GET")
 		testHeader(t, r, "Accept", mediaTypeReactionsPreview)
 
-		w.WriteHeader(http.StatusOK)
-		w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`))
+		testFormValues(t, r, values{"content": "+1"})
+		fmt.Fprint(w, `[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)
 	})
 
-	got, _, err := client.Reactions.ListCommentReactions(context.Background(), "o", "r", 1, nil)
+	opt := &ListCommentReactionOptions{Content: "+1"}
+	reactions, _, err := client.Reactions.ListCommentReactions(context.Background(), "o", "r", 1, opt)
 	if err != nil {
 		t.Errorf("ListCommentReactions returned error: %v", err)
 	}
 	want := []*Reaction{{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")}}
-	if !reflect.DeepEqual(got, want) {
-		t.Errorf("ListCommentReactions = %+v, want %+v", got, want)
+	if !reflect.DeepEqual(reactions, want) {
+		t.Errorf("ListCommentReactions = %+v, want %+v", reactions, want)
 	}
 }
 
diff --git a/github/repos_collaborators_test.go b/github/repos_collaborators_test.go
index a4d3210c..d2165579 100644
--- a/github/repos_collaborators_test.go
+++ b/github/repos_collaborators_test.go
@@ -149,30 +149,48 @@ func TestRepositoriesService_AddCollaborator(t *testing.T) {
 	defer teardown()
 
 	opt := &RepositoryAddCollaboratorOptions{Permission: "admin"}
-
 	mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) {
 		v := new(RepositoryAddCollaboratorOptions)
 		json.NewDecoder(r.Body).Decode(v)
-
 		testMethod(t, r, "PUT")
 		if !reflect.DeepEqual(v, opt) {
 			t.Errorf("Request body = %+v, want %+v", v, opt)
 		}
-
-		w.WriteHeader(http.StatusNoContent)
+		w.WriteHeader(http.StatusOK)
+		w.Write([]byte(`{"permissions": "write","url": "https://api.github.com/user/repository_invitations/1296269","html_url": "https://github.com/octocat/Hello-World/invitations","id":1,"permissions":"write","repository":{"url":"s","name":"r","id":1},"invitee":{"login":"u"},"inviter":{"login":"o"}}`))
 	})
-
-	_, err := client.Repositories.AddCollaborator(context.Background(), "o", "r", "u", opt)
+	collaboratorInvitation, _, err := client.Repositories.AddCollaborator(context.Background(), "o", "r", "u", opt)
 	if err != nil {
 		t.Errorf("Repositories.AddCollaborator returned error: %v", err)
 	}
+	want := &CollaboratorInvitation{
+		ID: Int64(1),
+		Repo: &Repository{
+			ID:   Int64(1),
+			URL:  String("s"),
+			Name: String("r"),
+		},
+		Invitee: &User{
+			Login: String("u"),
+		},
+		Inviter: &User{
+			Login: String("o"),
+		},
+		Permissions: String("write"),
+		URL:         String("https://api.github.com/user/repository_invitations/1296269"),
+		HTMLURL:     String("https://github.com/octocat/Hello-World/invitations"),
+	}
+
+	if !reflect.DeepEqual(collaboratorInvitation, want) {
+		t.Errorf("AddCollaborator returned %+v, want %+v", collaboratorInvitation, want)
+	}
 }
 
 func TestRepositoriesService_AddCollaborator_invalidUser(t *testing.T) {
 	client, _, _, teardown := setup()
 	defer teardown()
 
-	_, err := client.Repositories.AddCollaborator(context.Background(), "%", "%", "%", nil)
+	_, _, err := client.Repositories.AddCollaborator(context.Background(), "%", "%", "%", nil)
 	testURLParseError(t, err)
 }
 
diff --git a/github/repos_contents_test.go b/github/repos_contents_test.go
index 29bab8d2..f1225258 100644
--- a/github/repos_contents_test.go
+++ b/github/repos_contents_test.go
@@ -10,6 +10,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"net/http"
+	"net/url"
 	"reflect"
 	"testing"
 )
@@ -375,7 +376,46 @@ func TestRepositoriesService_GetArchiveLink(t *testing.T) {
 		testMethod(t, r, "GET")
 		http.Redirect(w, r, "http://github.com/a", http.StatusFound)
 	})
-	url, resp, err := client.Repositories.GetArchiveLink(context.Background(), "o", "r", Tarball, &RepositoryContentGetOptions{})
+	url, resp, err := client.Repositories.GetArchiveLink(context.Background(), "o", "r", Tarball, &RepositoryContentGetOptions{}, true)
+	if err != nil {
+		t.Errorf("Repositories.GetArchiveLink returned error: %v", err)
+	}
+	if resp.StatusCode != http.StatusFound {
+		t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusFound)
+	}
+	want := "http://github.com/a"
+	if url.String() != want {
+		t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url.String(), want)
+	}
+}
+
+func TestRepositoriesService_GetArchiveLink_StatusMovedPermanently_dontFollowRedirects(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+	mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		http.Redirect(w, r, "http://github.com/a", http.StatusMovedPermanently)
+	})
+	_, resp, _ := client.Repositories.GetArchiveLink(context.Background(), "o", "r", Tarball, &RepositoryContentGetOptions{}, false)
+	if resp.StatusCode != http.StatusMovedPermanently {
+		t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusMovedPermanently)
+	}
+}
+
+func TestRepositoriesService_GetArchiveLink_StatusMovedPermanently_followRedirects(t *testing.T) {
+	client, mux, serverURL, teardown := setup()
+	defer teardown()
+	// Mock a redirect link, which leads to an archive link
+	mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect")
+		http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently)
+	})
+	mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		http.Redirect(w, r, "http://github.com/a", http.StatusFound)
+	})
+	url, resp, err := client.Repositories.GetArchiveLink(context.Background(), "o", "r", Tarball, &RepositoryContentGetOptions{}, true)
 	if err != nil {
 		t.Errorf("Repositories.GetArchiveLink returned error: %v", err)
 	}
diff --git a/github/repos_pages_test.go b/github/repos_pages_test.go
index 62d42470..ec77f717 100644
--- a/github/repos_pages_test.go
+++ b/github/repos_pages_test.go
@@ -10,7 +10,6 @@ import (
 	"fmt"
 	"net/http"
 	"reflect"
-	"strings"
 	"testing"
 )
 
@@ -20,8 +19,7 @@ func TestRepositoriesService_EnablePages(t *testing.T) {
 
 	mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "POST")
-		wantAcceptHeaders := []string{mediaTypeEnablePagesAPIPreview, mediaTypePagesPreview}
-		testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", "))
+		testHeader(t, r, "Accept", mediaTypeEnablePagesAPIPreview)
 		fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h", "source": {"branch":"master", "path":"/"}}`)
 	})
 
@@ -58,7 +56,6 @@ func TestRepositoriesService_GetPagesInfo(t *testing.T) {
 
 	mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
-		testHeader(t, r, "Accept", mediaTypePagesPreview)
 		fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h"}`)
 	})
 
@@ -157,7 +154,6 @@ func TestRepositoriesService_RequestPageBuild(t *testing.T) {
 
 	mux.HandleFunc("/repos/o/r/pages/builds", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "POST")
-		testHeader(t, r, "Accept", mediaTypePagesPreview)
 		fmt.Fprint(w, `{"url":"u","status":"s"}`)
 	})
 
diff --git a/github/repos_test.go b/github/repos_test.go
index 5ed6682c..3b2893bc 100644
--- a/github/repos_test.go
+++ b/github/repos_test.go
@@ -630,6 +630,27 @@ func TestRepositoriesService_Edit_invalidOwner(t *testing.T) {
 	testURLParseError(t, err)
 }
 
+func TestRepositoriesService_GetVulnerabilityAlerts(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/vulnerability-alerts", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", mediaTypeRequiredVulnerabilityAlertsPreview)
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	vulnerabilityAlertsEnabled, _, err := client.Repositories.GetVulnerabilityAlerts(context.Background(), "o", "r")
+	if err != nil {
+		t.Errorf("Repositories.GetVulnerabilityAlerts returned error: %v", err)
+	}
+
+	if want := true; vulnerabilityAlertsEnabled != want {
+		t.Errorf("Repositories.GetVulnerabilityAlerts returned %+v, want %+v", vulnerabilityAlertsEnabled, want)
+	}
+}
+
 func TestRepositoriesService_EnableVulnerabilityAlerts(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
@@ -907,7 +928,7 @@ func TestRepositoriesService_GetBranchProtection(t *testing.T) {
 		},
 		RequiredPullRequestReviews: &PullRequestReviewsEnforcement{
 			DismissStaleReviews: true,
-			DismissalRestrictions: DismissalRestrictions{
+			DismissalRestrictions: &DismissalRestrictions{
 				Users: []*User{
 					{Login: String("u"), ID: Int64(3)},
 				},
@@ -936,6 +957,70 @@ func TestRepositoriesService_GetBranchProtection(t *testing.T) {
 	}
 }
 
+func TestRepositoriesService_GetBranchProtection_noDismissalRestrictions(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/branches/b/protection", func(w http.ResponseWriter, r *http.Request) {
+
+		testMethod(t, r, "GET")
+		// TODO: remove custom Accept header when this API fully launches
+		testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview)
+		fmt.Fprintf(w, `{
+				"required_status_checks":{
+					"strict":true,
+					"contexts":["continuous-integration"]
+				},
+				"required_pull_request_reviews":{
+					"dismiss_stale_reviews":true,
+					"require_code_owner_reviews":true,
+					"required_approving_review_count":1
+					},
+					"enforce_admins":{
+						"url":"/repos/o/r/branches/b/protection/enforce_admins",
+						"enabled":true
+					},
+					"restrictions":{
+						"users":[{"id":1,"login":"u"}],
+						"teams":[{"id":2,"slug":"t"}]
+					}
+				}`)
+	})
+
+	protection, _, err := client.Repositories.GetBranchProtection(context.Background(), "o", "r", "b")
+	if err != nil {
+		t.Errorf("Repositories.GetBranchProtection returned error: %v", err)
+	}
+
+	want := &Protection{
+		RequiredStatusChecks: &RequiredStatusChecks{
+			Strict:   true,
+			Contexts: []string{"continuous-integration"},
+		},
+		RequiredPullRequestReviews: &PullRequestReviewsEnforcement{
+			DismissStaleReviews:          true,
+			DismissalRestrictions:        nil,
+			RequireCodeOwnerReviews:      true,
+			RequiredApprovingReviewCount: 1,
+		},
+		EnforceAdmins: &AdminEnforcement{
+			URL:     String("/repos/o/r/branches/b/protection/enforce_admins"),
+			Enabled: true,
+		},
+		Restrictions: &BranchRestrictions{
+			Users: []*User{
+				{Login: String("u"), ID: Int64(1)},
+			},
+			Teams: []*Team{
+				{Slug: String("t"), ID: Int64(2)},
+			},
+		},
+	}
+	if !reflect.DeepEqual(protection, want) {
+		t.Errorf("Repositories.GetBranchProtection returned %+v, want %+v", protection, want)
+	}
+}
+
 func TestRepositoriesService_UpdateBranchProtection(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
@@ -955,6 +1040,7 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) {
 		Restrictions: &BranchRestrictionsRequest{
 			Users: []string{"u"},
 			Teams: []string{"t"},
+			Apps:  []string{"a"},
 		},
 	}
 
@@ -990,7 +1076,8 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) {
 			},
 			"restrictions":{
 				"users":[{"id":1,"login":"u"}],
-				"teams":[{"id":2,"slug":"t"}]
+				"teams":[{"id":2,"slug":"t"}],
+				"apps":[{"id":3,"slug":"a"}]
 			}
 		}`)
 	})
@@ -1007,7 +1094,7 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) {
 		},
 		RequiredPullRequestReviews: &PullRequestReviewsEnforcement{
 			DismissStaleReviews: true,
-			DismissalRestrictions: DismissalRestrictions{
+			DismissalRestrictions: &DismissalRestrictions{
 				Users: []*User{
 					{Login: String("uu"), ID: Int64(3)},
 				},
@@ -1024,6 +1111,9 @@ func TestRepositoriesService_UpdateBranchProtection(t *testing.T) {
 			Teams: []*Team{
 				{Slug: String("t"), ID: Int64(2)},
 			},
+			Apps: []*App{
+				{Slug: String("a"), ID: Int64(3)},
+			},
 		},
 	}
 	if !reflect.DeepEqual(protection, want) {
@@ -1201,7 +1291,7 @@ func TestRepositoriesService_GetPullRequestReviewEnforcement(t *testing.T) {
 
 	want := &PullRequestReviewsEnforcement{
 		DismissStaleReviews: true,
-		DismissalRestrictions: DismissalRestrictions{
+		DismissalRestrictions: &DismissalRestrictions{
 			Users: []*User{
 				{Login: String("u"), ID: Int64(1)},
 			},
@@ -1257,7 +1347,7 @@ func TestRepositoriesService_UpdatePullRequestReviewEnforcement(t *testing.T) {
 
 	want := &PullRequestReviewsEnforcement{
 		DismissStaleReviews: true,
-		DismissalRestrictions: DismissalRestrictions{
+		DismissalRestrictions: &DismissalRestrictions{
 			Users: []*User{
 				{Login: String("u"), ID: Int64(1)},
 			},
@@ -1281,8 +1371,8 @@ func TestRepositoriesService_DisableDismissalRestrictions(t *testing.T) {
 		testMethod(t, r, "PATCH")
 		// TODO: remove custom Accept header when this API fully launches
 		testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview)
-		testBody(t, r, `{"dismissal_restrictions":[]}`+"\n")
-		fmt.Fprintf(w, `{"dismissal_restrictions":{"users":[],"teams":[]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true,"required_approving_review_count":1}`)
+		testBody(t, r, `{"dismissal_restrictions":{}}`+"\n")
+		fmt.Fprintf(w, `{"dismiss_stale_reviews":true,"require_code_owner_reviews":true,"required_approving_review_count":1}`)
 	})
 
 	enforcement, _, err := client.Repositories.DisableDismissalRestrictions(context.Background(), "o", "r", "b")
@@ -1291,11 +1381,8 @@ func TestRepositoriesService_DisableDismissalRestrictions(t *testing.T) {
 	}
 
 	want := &PullRequestReviewsEnforcement{
-		DismissStaleReviews: true,
-		DismissalRestrictions: DismissalRestrictions{
-			Users: []*User{},
-			Teams: []*Team{},
-		},
+		DismissStaleReviews:          true,
+		DismissalRestrictions:        nil,
 		RequireCodeOwnerReviews:      true,
 		RequiredApprovingReviewCount: 1,
 	}
@@ -1603,6 +1690,85 @@ func TestRepositoriesService_ReplaceAllTopics_emptySlice(t *testing.T) {
 	}
 }
 
+func TestRepositoriesService_ListApps(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/apps", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+	})
+
+	_, _, err := client.Repositories.ListApps(context.Background(), "o", "r", "b")
+	if err != nil {
+		t.Errorf("Repositories.ListApps returned error: %v", err)
+	}
+}
+
+func TestRepositoriesService_ReplaceAppRestrictions(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/apps", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PUT")
+		fmt.Fprint(w, `[{
+				"name": "octocat"
+			}]`)
+	})
+	input := []string{"octocat"}
+	got, _, err := client.Repositories.ReplaceAppRestrictions(context.Background(), "o", "r", "b", input)
+	if err != nil {
+		t.Errorf("Repositories.ReplaceAppRestrictions returned error: %v", err)
+	}
+	want := []*App{
+		{Name: String("octocat")},
+	}
+	if !reflect.DeepEqual(got, want) {
+		t.Errorf("Repositories.ReplaceAppRestrictions returned %+v, want %+v", got, want)
+	}
+}
+
+func TestRepositoriesService_AddAppRestrictions(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/apps", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		fmt.Fprint(w, `[{
+				"name": "octocat"
+			}]`)
+	})
+	input := []string{"octocat"}
+	got, _, err := client.Repositories.AddAppRestrictions(context.Background(), "o", "r", "b", input)
+	if err != nil {
+		t.Errorf("Repositories.AddAppRestrictions returned error: %v", err)
+	}
+	want := []*App{
+		{Name: String("octocat")},
+	}
+	if !reflect.DeepEqual(got, want) {
+		t.Errorf("Repositories.AddAppRestrictions returned %+v, want %+v", got, want)
+	}
+}
+
+func TestRepositoriesService_RemoveAppRestrictions(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/apps", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+		fmt.Fprint(w, `[]`)
+	})
+	input := []string{"octocat"}
+	got, _, err := client.Repositories.RemoveAppRestrictions(context.Background(), "o", "r", "b", input)
+	if err != nil {
+		t.Errorf("Repositories.RemoveAppRestrictions returned error: %v", err)
+	}
+	want := []*App{}
+	if !reflect.DeepEqual(got, want) {
+		t.Errorf("Repositories.RemoveAppRestrictions returned %+v, want %+v", got, want)
+	}
+}
+
 func TestRepositoriesService_Transfer(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
diff --git a/github/search_test.go b/github/search_test.go
index 4b686884..7b607569 100644
--- a/github/search_test.go
+++ b/github/search_test.go
@@ -47,6 +47,37 @@ func TestSearchService_Repositories(t *testing.T) {
 	}
 }
 
+func TestSearchService_Topics(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/search/topics", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testFormValues(t, r, values{
+			"q":        "blah",
+			"page":     "2",
+			"per_page": "2",
+		})
+
+		fmt.Fprint(w, `{"total_count": 4, "incomplete_results": false, "items": [{"name":"blah"},{"name":"blahblah"}]}`)
+	})
+
+	opts := &SearchOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}}
+	result, _, err := client.Search.Topics(context.Background(), "blah", opts)
+	if err != nil {
+		t.Errorf("Search.Topics returned error: %v", err)
+	}
+
+	want := &TopicsSearchResult{
+		Total:             Int(4),
+		IncompleteResults: Bool(false),
+		Topics:            []*TopicResult{{Name: String("blah")}, {Name: String("blahblah")}},
+	}
+	if !reflect.DeepEqual(result, want) {
+		t.Errorf("Search.Topics returned %+v, want %+v", result, want)
+	}
+}
+
 func TestSearchService_Commits(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
diff --git a/github/teams_test.go b/github/teams_test.go
index 6210bb59..97dc325d 100644
--- a/github/teams_test.go
+++ b/github/teams_test.go
@@ -608,7 +608,6 @@ func TestTeamsService_ListIDPGroupsInOrganization(t *testing.T) {
 
 	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",
 		})
@@ -641,7 +640,6 @@ func TestTeamsService_ListIDPGroupsForTeam(t *testing.T) {
 
 	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"}]}`)
 	})
 
@@ -670,7 +668,6 @@ func TestTeamsService_CreateOrUpdateIDPGroupConnections(t *testing.T) {
 
 	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"}]}`)
 	})
 
diff --git a/github/users_projects_test.go b/github/users_projects_test.go
new file mode 100644
index 00000000..b186fbf0
--- /dev/null
+++ b/github/users_projects_test.go
@@ -0,0 +1,68 @@
+// 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 TestUsersService_ListProjects(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/users/u/projects", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
+		testFormValues(t, r, values{"state": "open", "page": "2"})
+		fmt.Fprint(w, `[{"id":1}]`)
+	})
+
+	opt := &ProjectListOptions{State: "open", ListOptions: ListOptions{Page: 2}}
+	projects, _, err := client.Users.ListProjects(context.Background(), "u", opt)
+	if err != nil {
+		t.Errorf("Users.ListProjects returned error: %v", err)
+	}
+
+	want := []*Project{{ID: Int64(1)}}
+	if !reflect.DeepEqual(projects, want) {
+		t.Errorf("Users.ListProjects returned %+v, want %+v", projects, want)
+	}
+}
+
+func TestUsersService_CreateProject(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := &CreateUserProjectOptions{Name: "Project Name", Body: String("Project body.")}
+
+	mux.HandleFunc("/users/projects", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
+
+		v := &CreateUserProjectOptions{}
+		json.NewDecoder(r.Body).Decode(v)
+		if !reflect.DeepEqual(v, input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		fmt.Fprint(w, `{"id":1}`)
+	})
+
+	project, _, err := client.Users.CreateProject(context.Background(), input)
+	if err != nil {
+		t.Errorf("Users.CreateProject returned error: %v", err)
+	}
+
+	want := &Project{ID: Int64(1)}
+	if !reflect.DeepEqual(project, want) {
+		t.Errorf("Users.CreateProject returned %+v, want %+v", project, want)
+	}
+}
diff --git a/scrape/apps_test.go b/scrape/apps_test.go
index 2f8435ad..b895b667 100644
--- a/scrape/apps_test.go
+++ b/scrape/apps_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-github/v28/github"
 )
 
 func Test_AppRestrictionsEnabled(t *testing.T) {
@@ -84,3 +85,21 @@ func Test_ListOAuthApps(t *testing.T) {
 	}
 
 }
+
+func Test_CreateApp(t *testing.T) {
+	client, mux, cleanup := setup()
+	defer cleanup()
+
+	mux.HandleFunc("/apps/settings/new", func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(http.StatusCreated)
+	})
+
+	if _, err := client.CreateApp(&AppManifest{
+		URL: github.String("https://example.com"),
+		HookAttributes: map[string]string{
+			"url": "https://example.com/hook",
+		},
+	}); err != nil {
+		t.Fatalf("CreateApp: %v", err)
+	}
+}