Repository: google/go-github
Pull request #1180 (2019-05-31 12:57:36)

Add missing permissions to InstallationPermissions
This adds the remaining GitHub App installation permissions defined in https://developer.github.com/v3/apps/permissions/#metadata-permissions that are related to GitHub App installations.

These do not include permissions such as `emails` or `gpg_keys` as these are user permissions.

I think I found the relevant test(s) to update, but if there are more I can add please let me know.

✨ Thanks ✨ 

codecov[bot]: # [Codecov](https://codecov.io/gh/google/go-github/pull/1180?src=pr&el=h1) Report
> Merging [#1180](https://codecov.io/gh/google/go-github/pull/1180?src=pr&el=desc) into [master](https://codecov.io/gh/google/go-github/commit/8354c0739a78b30158359e98fe14ca3bb823159c?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

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

```diff
@@           Coverage Diff           @@
##           master    #1180   +/-   ##
=======================================
  Coverage   70.25%   70.25%           
=======================================
  Files          84       84           
  Lines        5867     5867           
=======================================
  Hits         4122     4122           
  Misses        956      956           
  Partials      789      789
```


| [Impacted Files](https://codecov.io/gh/google/go-github/pull/1180?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [github/apps.go](https://codecov.io/gh/google/go-github/pull/1180/diff?src=pr&el=tree#diff-Z2l0aHViL2FwcHMuZ28=) | `65.51% <ø> (ø)` | :arrow_up: |

------

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

gmlewis: Thank you, @tarebyte!

I'm looking around the GitHub API v3 Developer docs, and am having troubles finding these added fields. Where did you find these?
Are they really all strings?
If you have a documentation URL, could you please add it to a comment before the expanded struct?
tarebyte: > I'm looking around the GitHub API v3 Developer docs, and am having troubles finding these added fields. Where did you find these?

The permissions are listed here: https://developer.github.com/v3/apps/permissions

> Are they really all strings?

Yup! The string is either `read`/`write`/`admin` and if it's not one of those then the field isn't part of the JSON response.

An example of this would be the `permissions` key in this response https://developer.github.com/v3/apps/#get-an-installation

> If you have a documentation URL, could you please add it to a comment before the expanded struct?

Happy to, is https://developer.github.com/v3/apps/permissions/ sufficient?
gmlewis: Hmmm... I'm still confused. Let's take an example.
Where did you find `organization_pre_receive_hooks` ?
tarebyte: > Where did you find the string `organization_pre_receive_hooks` ?

That's an Enterprise only permission, it's located in https://developer.github.com/enterprise/2.17/v3/apps/permissions/
gmlewis: Ah!  Great, thanks.  Then can you please add the two URLs in the comment?
Something like:
```go
// Hook names taken from:
//   https://developer.github.com/v3/apps/permissions/
//   https://developer.github.com/enterprise/2.17/v3/apps/permissions/
``
Thank you!
tarebyte: > Ah! Great, thanks. Then can you please add the two URLs in the comment?

Done in https://github.com/google/go-github/pull/1180/commits/a8d3df256cd539b723943af1740a67447c601179
gmlewis: @itsdalmo - since you reviewed the code, can I please get an LGTM from you and we can merge this?
tarebyte: @itsdalmo, those are all user permissions that aren’t granted to an app installation.

These are only requested and granted when a user goes through the authorization flow and the GitHub App gets a “user to server” token.
tarebyte: Ooo actually I think `packages` might be a installation permission.

I’ll double check.
gmlewis: It looks like we have two LGTMs but also some more commits coming in.

Just FYI, @tarebyte, we try not to use force pushes for PRs in this repo as it makes it more challenging for the reviewers to see what changed, but no big deal. Sometimes we have to use them to clear up the CLA checker and make it happy. :smile:

So I'm just checking in with you on the status... are we good to merge, or to you think there are other changes that need making first?
tarebyte: Good to know thanks!

This is good to go from my perspective, the missing packages permission was all I needed to add.
gmlewis: Thank you, @tarebyte and @itsdalmo!
Merging.

Patch:
diff --git a/github/apps.go b/github/apps.go
index 80dfb3c8..f675a9ab 100644
--- a/github/apps.go
+++ b/github/apps.go
@@ -36,12 +36,36 @@ type InstallationToken struct {
 	ExpiresAt *time.Time `json:"expires_at,omitempty"`
 }
 
-// InstallationPermissions lists the permissions for metadata, contents, issues and single file for an installation.
+// InstallationPermissions lists the repository and organization permissions for an installation.
+//
+// Permission names taken from:
+//   https://developer.github.com/v3/apps/permissions/
+//   https://developer.github.com/enterprise/v3/apps/permissions/
 type InstallationPermissions struct {
-	Metadata   *string `json:"metadata,omitempty"`
-	Contents   *string `json:"contents,omitempty"`
-	Issues     *string `json:"issues,omitempty"`
-	SingleFile *string `json:"single_file,omitempty"`
+	Administration              *string `json:"administration,omitempty"`
+	Checks                      *string `json:"checks,omitempty"`
+	Contents                    *string `json:"contents,omitempty"`
+	ContentReferences           *string `json:"content_references,omitempty"`
+	Deployments                 *string `json:"deployments,omitempty"`
+	Issues                      *string `json:"issues,omitempty"`
+	Metadata                    *string `json:"metadata,omitempty"`
+	Members                     *string `json:"members,omitempty"`
+	OrganizationAdministration  *string `json:"organization_administration,omitempty"`
+	OrganizationHooks           *string `json:"organization_hooks,omitempty"`
+	OrganizationPlan            *string `json:"organization_plan,omitempty"`
+	OrganizationPreReceiveHooks *string `json:"organization_pre_receive_hooks,omitempty"`
+	OrganizationProjects        *string `json:"organization_projects,omitempty"`
+	OrganizationUserBlocking    *string `json:"organization_user_blocking,omitempty"`
+	Packages                    *string `json:"packages,omitempty"`
+	Pages                       *string `json:"pages,omitempty"`
+	PullRequests                *string `json:"pull_requests,omitempty"`
+	RepositoryHooks             *string `json:"repository_hooks,omitempty"`
+	RepositoryProjects          *string `json:"repository_projects,omitempty"`
+	RepositoryPreReceiveHooks   *string `json:"repository_pre_receive_hooks,omitempty"`
+	SingleFile                  *string `json:"single_file,omitempty"`
+	Statuses                    *string `json:"statuses,omitempty"`
+	TeamDiscussions             *string `json:"team_discussions,omitempty"`
+	VulnerabilityAlerts         *string `json:"vulnerability_alerts,omitempty"`
 }
 
 // Installation represents a GitHub Apps installation.
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 9cd33103..0bb206bd 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -3628,6 +3628,30 @@ func (i *InstallationEvent) GetSender() *User {
 	return i.Sender
 }
 
+// GetAdministration returns the Administration field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetAdministration() string {
+	if i == nil || i.Administration == nil {
+		return ""
+	}
+	return *i.Administration
+}
+
+// GetChecks returns the Checks field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetChecks() string {
+	if i == nil || i.Checks == nil {
+		return ""
+	}
+	return *i.Checks
+}
+
+// GetContentReferences returns the ContentReferences field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetContentReferences() string {
+	if i == nil || i.ContentReferences == nil {
+		return ""
+	}
+	return *i.ContentReferences
+}
+
 // GetContents returns the Contents field if it's non-nil, zero value otherwise.
 func (i *InstallationPermissions) GetContents() string {
 	if i == nil || i.Contents == nil {
@@ -3636,6 +3660,14 @@ func (i *InstallationPermissions) GetContents() string {
 	return *i.Contents
 }
 
+// GetDeployments returns the Deployments field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetDeployments() string {
+	if i == nil || i.Deployments == nil {
+		return ""
+	}
+	return *i.Deployments
+}
+
 // GetIssues returns the Issues field if it's non-nil, zero value otherwise.
 func (i *InstallationPermissions) GetIssues() string {
 	if i == nil || i.Issues == nil {
@@ -3644,6 +3676,14 @@ func (i *InstallationPermissions) GetIssues() string {
 	return *i.Issues
 }
 
+// GetMembers returns the Members field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetMembers() string {
+	if i == nil || i.Members == nil {
+		return ""
+	}
+	return *i.Members
+}
+
 // GetMetadata returns the Metadata field if it's non-nil, zero value otherwise.
 func (i *InstallationPermissions) GetMetadata() string {
 	if i == nil || i.Metadata == nil {
@@ -3652,6 +3692,102 @@ func (i *InstallationPermissions) GetMetadata() string {
 	return *i.Metadata
 }
 
+// GetOrganizationAdministration returns the OrganizationAdministration field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetOrganizationAdministration() string {
+	if i == nil || i.OrganizationAdministration == nil {
+		return ""
+	}
+	return *i.OrganizationAdministration
+}
+
+// GetOrganizationHooks returns the OrganizationHooks field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetOrganizationHooks() string {
+	if i == nil || i.OrganizationHooks == nil {
+		return ""
+	}
+	return *i.OrganizationHooks
+}
+
+// GetOrganizationPlan returns the OrganizationPlan field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetOrganizationPlan() string {
+	if i == nil || i.OrganizationPlan == nil {
+		return ""
+	}
+	return *i.OrganizationPlan
+}
+
+// GetOrganizationPreReceiveHooks returns the OrganizationPreReceiveHooks field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetOrganizationPreReceiveHooks() string {
+	if i == nil || i.OrganizationPreReceiveHooks == nil {
+		return ""
+	}
+	return *i.OrganizationPreReceiveHooks
+}
+
+// GetOrganizationProjects returns the OrganizationProjects field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetOrganizationProjects() string {
+	if i == nil || i.OrganizationProjects == nil {
+		return ""
+	}
+	return *i.OrganizationProjects
+}
+
+// GetOrganizationUserBlocking returns the OrganizationUserBlocking field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetOrganizationUserBlocking() string {
+	if i == nil || i.OrganizationUserBlocking == nil {
+		return ""
+	}
+	return *i.OrganizationUserBlocking
+}
+
+// GetPackages returns the Packages field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetPackages() string {
+	if i == nil || i.Packages == nil {
+		return ""
+	}
+	return *i.Packages
+}
+
+// GetPages returns the Pages field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetPages() string {
+	if i == nil || i.Pages == nil {
+		return ""
+	}
+	return *i.Pages
+}
+
+// GetPullRequests returns the PullRequests field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetPullRequests() string {
+	if i == nil || i.PullRequests == nil {
+		return ""
+	}
+	return *i.PullRequests
+}
+
+// GetRepositoryHooks returns the RepositoryHooks field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetRepositoryHooks() string {
+	if i == nil || i.RepositoryHooks == nil {
+		return ""
+	}
+	return *i.RepositoryHooks
+}
+
+// GetRepositoryPreReceiveHooks returns the RepositoryPreReceiveHooks field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetRepositoryPreReceiveHooks() string {
+	if i == nil || i.RepositoryPreReceiveHooks == nil {
+		return ""
+	}
+	return *i.RepositoryPreReceiveHooks
+}
+
+// GetRepositoryProjects returns the RepositoryProjects field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetRepositoryProjects() string {
+	if i == nil || i.RepositoryProjects == nil {
+		return ""
+	}
+	return *i.RepositoryProjects
+}
+
 // GetSingleFile returns the SingleFile field if it's non-nil, zero value otherwise.
 func (i *InstallationPermissions) GetSingleFile() string {
 	if i == nil || i.SingleFile == nil {
@@ -3660,6 +3796,30 @@ func (i *InstallationPermissions) GetSingleFile() string {
 	return *i.SingleFile
 }
 
+// GetStatuses returns the Statuses field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetStatuses() string {
+	if i == nil || i.Statuses == nil {
+		return ""
+	}
+	return *i.Statuses
+}
+
+// GetTeamDiscussions returns the TeamDiscussions field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetTeamDiscussions() string {
+	if i == nil || i.TeamDiscussions == nil {
+		return ""
+	}
+	return *i.TeamDiscussions
+}
+
+// GetVulnerabilityAlerts returns the VulnerabilityAlerts field if it's non-nil, zero value otherwise.
+func (i *InstallationPermissions) GetVulnerabilityAlerts() string {
+	if i == nil || i.VulnerabilityAlerts == nil {
+		return ""
+	}
+	return *i.VulnerabilityAlerts
+}
+
 // GetAction returns the Action field if it's non-nil, zero value otherwise.
 func (i *InstallationRepositoriesEvent) GetAction() string {
 	if i == nil || i.Action == nil {


Test patch:
diff --git a/github/apps_test.go b/github/apps_test.go
index 8456662b..8d5b27d3 100644
--- a/github/apps_test.go
+++ b/github/apps_test.go
@@ -73,10 +73,30 @@ func TestAppsService_ListInstallations(t *testing.T) {
                                    "target_id":1,
                                    "target_type": "Organization",
                                    "permissions": {
-                                       "metadata": "read",
+                                       "administration": "read",
+                                       "checks": "read",
                                        "contents": "read",
+                                       "content_references": "read",
+                                       "deployments": "read",
                                        "issues": "write",
-                                       "single_file": "write"
+                                       "metadata": "read",
+                                       "members": "read",
+                                       "organization_administration": "write",
+                                       "organization_hooks": "write",
+                                       "organization_plan": "read",
+                                       "organization_pre_receive_hooks": "write",
+                                       "organization_projects": "read",
+                                       "organization_user_blocking": "write",
+                                       "packages": "read",
+                                       "pages": "read",
+                                       "pull_requests": "write",
+                                       "repository_hooks": "write",
+                                       "repository_projects": "read",
+                                       "repository_pre_receive_hooks": "read",
+                                       "single_file": "write",
+                                       "statuses": "write",
+                                       "team_discussions": "read",
+                                       "vulnerability_alerts": "read"
                                    },
                                   "events": [
                                       "push",
@@ -104,10 +124,30 @@ func TestAppsService_ListInstallations(t *testing.T) {
 		SingleFileName:      String("config.yml"),
 		RepositorySelection: String("selected"),
 		Permissions: &InstallationPermissions{
-			Metadata:   String("read"),
-			Contents:   String("read"),
-			Issues:     String("write"),
-			SingleFile: String("write")},
+			Administration:              String("read"),
+			Checks:                      String("read"),
+			Contents:                    String("read"),
+			ContentReferences:           String("read"),
+			Deployments:                 String("read"),
+			Issues:                      String("write"),
+			Metadata:                    String("read"),
+			Members:                     String("read"),
+			OrganizationAdministration:  String("write"),
+			OrganizationHooks:           String("write"),
+			OrganizationPlan:            String("read"),
+			OrganizationPreReceiveHooks: String("write"),
+			OrganizationProjects:        String("read"),
+			OrganizationUserBlocking:    String("write"),
+			Packages:                    String("read"),
+			Pages:                       String("read"),
+			PullRequests:                String("write"),
+			RepositoryHooks:             String("write"),
+			RepositoryProjects:          String("read"),
+			RepositoryPreReceiveHooks:   String("read"),
+			SingleFile:                  String("write"),
+			Statuses:                    String("write"),
+			TeamDiscussions:             String("read"),
+			VulnerabilityAlerts:         String("read")},
 		Events:    []string{"push", "pull_request"},
 		CreatedAt: &date,
 		UpdatedAt: &date,