1// Copyright 2016 The go-github AUTHORS. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file.
5
6package github
7
8import (
9	"context"
10	"fmt"
11	"time"
12)
13
14// AppsService provides access to the installation related functions
15// in the GitHub API.
16//
17// GitHub API docs: https://developer.github.com/v3/apps/
18type AppsService service
19
20// App represents a GitHub App.
21type App struct {
22	ID          *int64     `json:"id,omitempty"`
23	NodeID      *string    `json:"node_id,omitempty"`
24	Owner       *User      `json:"owner,omitempty"`
25	Name        *string    `json:"name,omitempty"`
26	Description *string    `json:"description,omitempty"`
27	ExternalURL *string    `json:"external_url,omitempty"`
28	HTMLURL     *string    `json:"html_url,omitempty"`
29	CreatedAt   *time.Time `json:"created_at,omitempty"`
30	UpdatedAt   *time.Time `json:"updated_at,omitempty"`
31}
32
33// InstallationToken represents an installation token.
34type InstallationToken struct {
35	Token     *string    `json:"token,omitempty"`
36	ExpiresAt *time.Time `json:"expires_at,omitempty"`
37}
38
39// InstallationPermissions lists the permissions for metadata, contents, issues and single file for an installation.
40type InstallationPermissions struct {
41	Metadata   *string `json:"metadata,omitempty"`
42	Contents   *string `json:"contents,omitempty"`
43	Issues     *string `json:"issues,omitempty"`
44	SingleFile *string `json:"single_file,omitempty"`
45}
46
47// Installation represents a GitHub Apps installation.
48type Installation struct {
49	ID                  *int64                   `json:"id,omitempty"`
50	AppID               *int64                   `json:"app_id,omitempty"`
51	TargetID            *int64                   `json:"target_id,omitempty"`
52	Account             *User                    `json:"account,omitempty"`
53	AccessTokensURL     *string                  `json:"access_tokens_url,omitempty"`
54	RepositoriesURL     *string                  `json:"repositories_url,omitempty"`
55	HTMLURL             *string                  `json:"html_url,omitempty"`
56	TargetType          *string                  `json:"target_type,omitempty"`
57	SingleFileName      *string                  `json:"single_file_name,omitempty"`
58	RepositorySelection *string                  `json:"repository_selection,omitempty"`
59	Events              []string                 `json:"events,omitempty"`
60	Permissions         *InstallationPermissions `json:"permissions,omitempty"`
61	CreatedAt           *Timestamp               `json:"created_at,omitempty"`
62	UpdatedAt           *Timestamp               `json:"updated_at,omitempty"`
63}
64
65func (i Installation) String() string {
66	return Stringify(i)
67}
68
69// Get a single GitHub App. Passing the empty string will get
70// the authenticated GitHub App.
71//
72// Note: appSlug is just the URL-friendly name of your GitHub App.
73// You can find this on the settings page for your GitHub App
74// (e.g., https://github.com/settings/apps/:app_slug).
75//
76// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-github-app
77func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, error) {
78	var u string
79	if appSlug != "" {
80		u = fmt.Sprintf("apps/%v", appSlug)
81	} else {
82		u = "app"
83	}
84
85	req, err := s.client.NewRequest("GET", u, nil)
86	if err != nil {
87		return nil, nil, err
88	}
89
90	// TODO: remove custom Accept header when this API fully launches.
91	req.Header.Set("Accept", mediaTypeIntegrationPreview)
92
93	app := new(App)
94	resp, err := s.client.Do(ctx, req, app)
95	if err != nil {
96		return nil, resp, err
97	}
98
99	return app, resp, nil
100}
101
102// ListInstallations lists the installations that the current GitHub App has.
103//
104// GitHub API docs: https://developer.github.com/v3/apps/#find-installations
105func (s *AppsService) ListInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) {
106	u, err := addOptions("app/installations", opt)
107	if err != nil {
108		return nil, nil, err
109	}
110
111	req, err := s.client.NewRequest("GET", u, nil)
112	if err != nil {
113		return nil, nil, err
114	}
115
116	// TODO: remove custom Accept header when this API fully launches.
117	req.Header.Set("Accept", mediaTypeIntegrationPreview)
118
119	var i []*Installation
120	resp, err := s.client.Do(ctx, req, &i)
121	if err != nil {
122		return nil, resp, err
123	}
124
125	return i, resp, nil
126}
127
128// GetInstallation returns the specified installation.
129//
130// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-installation
131func (s *AppsService) GetInstallation(ctx context.Context, id int64) (*Installation, *Response, error) {
132	return s.getInstallation(ctx, fmt.Sprintf("app/installations/%v", id))
133}
134
135// ListUserInstallations lists installations that are accessible to the authenticated user.
136//
137// GitHub API docs: https://developer.github.com/v3/apps/#list-installations-for-user
138func (s *AppsService) ListUserInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) {
139	u, err := addOptions("user/installations", opt)
140	if err != nil {
141		return nil, nil, err
142	}
143
144	req, err := s.client.NewRequest("GET", u, nil)
145	if err != nil {
146		return nil, nil, err
147	}
148
149	// TODO: remove custom Accept header when this API fully launches.
150	req.Header.Set("Accept", mediaTypeIntegrationPreview)
151
152	var i struct {
153		Installations []*Installation `json:"installations"`
154	}
155	resp, err := s.client.Do(ctx, req, &i)
156	if err != nil {
157		return nil, resp, err
158	}
159
160	return i.Installations, resp, nil
161}
162
163// CreateInstallationToken creates a new installation token.
164//
165// GitHub API docs: https://developer.github.com/v3/apps/#create-a-new-installation-token
166func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64) (*InstallationToken, *Response, error) {
167	u := fmt.Sprintf("installations/%v/access_tokens", id)
168
169	req, err := s.client.NewRequest("POST", u, nil)
170	if err != nil {
171		return nil, nil, err
172	}
173
174	// TODO: remove custom Accept header when this API fully launches.
175	req.Header.Set("Accept", mediaTypeIntegrationPreview)
176
177	t := new(InstallationToken)
178	resp, err := s.client.Do(ctx, req, t)
179	if err != nil {
180		return nil, resp, err
181	}
182
183	return t, resp, nil
184}
185
186// FindOrganizationInstallation finds the organization's installation information.
187//
188// GitHub API docs: https://developer.github.com/v3/apps/#find-organization-installation
189func (s *AppsService) FindOrganizationInstallation(ctx context.Context, org string) (*Installation, *Response, error) {
190	return s.getInstallation(ctx, fmt.Sprintf("orgs/%v/installation", org))
191}
192
193// FindRepositoryInstallation finds the repository's installation information.
194//
195// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation
196func (s *AppsService) FindRepositoryInstallation(ctx context.Context, owner, repo string) (*Installation, *Response, error) {
197	return s.getInstallation(ctx, fmt.Sprintf("repos/%v/%v/installation", owner, repo))
198}
199
200// FindUserInstallation finds the user's installation information.
201//
202// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation
203func (s *AppsService) FindUserInstallation(ctx context.Context, user string) (*Installation, *Response, error) {
204	return s.getInstallation(ctx, fmt.Sprintf("users/%v/installation", user))
205}
206
207func (s *AppsService) getInstallation(ctx context.Context, url string) (*Installation, *Response, error) {
208	req, err := s.client.NewRequest("GET", url, nil)
209	if err != nil {
210		return nil, nil, err
211	}
212
213	// TODO: remove custom Accept header when this API fully launches.
214	req.Header.Set("Accept", mediaTypeIntegrationPreview)
215
216	i := new(Installation)
217	resp, err := s.client.Do(ctx, req, i)
218	if err != nil {
219		return nil, resp, err
220	}
221
222	return i, resp, nil
223}
224