1// Copyright (c) 2015 Ableton AG, Berlin. 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 travis
7
8import (
9	"context"
10	"fmt"
11	"net/http"
12	"net/url"
13)
14
15// RequestsService handles communication with the requests
16// related methods of the Travis CI API.
17type RequestsService struct {
18	client *Client
19}
20
21// Request represents a Travis CI request.
22// They can be used to see if and why a GitHub even has or has not triggered a new build.
23//
24// // Travis CI API docs: https://developer.travis-ci.com/resource/request#standard-representation
25type Request struct {
26	// Value uniquely identifying the request
27	Id uint `json:"id,omitempty"`
28	// The state of a request (eg. whether it has been processed or not)
29	State string `json:"state,omitempty"`
30	// The result of the request (eg. rejected or approved)
31	Result string `json:"result,omitempty"`
32	// Travis-ci status message attached to the request.
33	Message string `json:"message,omitempty"`
34	// GitHub user or organization the request belongs to
35	Repository *Repository `json:"repository,omitempty"`
36	// Name of the branch requested to be built
37	BranchName string `json:"branch_name,omitempty"`
38	// The commit the request is associated with
39	Commit *Commit `json:"commit,omitempty"`
40	// The request's builds
41	Builds []*Build `json:"builds,omitempty"`
42	// GitHub user or organization the request belongs to
43	Owner *Owner `json:"owner,omitempty"`
44	// When Travis CI created the request
45	CreatedAt string `json:"created_at,omitempty"`
46	// Origin of request (push, pull request, api)
47	EventType string `json:"event_type,omitempty"`
48	*Metadata
49}
50
51// ListRequestsOption specifies options for
52// FindRequests request.
53type ListRequestsOption struct {
54	// How many requests to include in the response
55	Limit int `url:"limit,omitempty"`
56	// How many requests to skip before the first entry in the response
57	Offset int `url:"offset,omitempty"`
58}
59
60// RequestBody specifies body for
61// creating request.
62type RequestBody struct {
63	// Build configuration (as parsed from .travis.yml)
64	Config interface{} `json:"config,omitempty"`
65	// Travis-ci status message attached to the request
66	Message string `json:"message,omitempty"`
67	// Branch requested to be built
68	Branch string `json:"branch,omitempty"`
69	// Travis token associated with webhook on GitHub (DEPRECATED)
70	Token string `json:"token,omitempty"`
71}
72
73type getRequestsResponse struct {
74	Requests []*Request `json:"requests"`
75}
76
77// FindByRepoId fetches request of given repository id and request id
78//
79// Travis CI API docs: https://developer.travis-ci.com/resource/request#find
80func (rs *RequestsService) FindByRepoId(ctx context.Context, repoId uint, id uint) (*Request, *http.Response, error) {
81	u, err := urlWithOptions(fmt.Sprintf("/repo/%d/request/%d", repoId, id), nil)
82	if err != nil {
83		return nil, nil, err
84	}
85
86	req, err := rs.client.NewRequest(http.MethodGet, u, nil, nil)
87	if err != nil {
88		return nil, nil, err
89	}
90
91	var request Request
92	resp, err := rs.client.Do(ctx, req, &request)
93	if err != nil {
94		return nil, resp, err
95	}
96
97	return &request, resp, err
98}
99
100// FindByRepoSlug fetches request of given repository slug and request id
101//
102// Travis CI API docs: https://developer.travis-ci.com/resource/request#find
103func (rs *RequestsService) FindByRepoSlug(ctx context.Context, repoSlug string, id uint) (*Request, *http.Response, error) {
104	u, err := urlWithOptions(fmt.Sprintf("/repo/%s/request/%d", url.QueryEscape(repoSlug), id), nil)
105	if err != nil {
106		return nil, nil, err
107	}
108
109	req, err := rs.client.NewRequest(http.MethodGet, u, nil, nil)
110	if err != nil {
111		return nil, nil, err
112	}
113
114	var request Request
115	resp, err := rs.client.Do(ctx, req, &request)
116	if err != nil {
117		return nil, resp, err
118	}
119
120	return &request, resp, err
121}
122
123// Create endpoints actually returns following form of response.
124// It is different from standard nor minimal representation of a request.
125// So far, I'm not going to create a special struct to parse it, and
126// just use the minimal representation of request.
127//
128//{
129//  "@type":              "pending",
130//  "remaining_requests": 1,
131//  "repository":         {
132//    "@type":            "repository",
133//    "@href":            "/repo/1",
134//    "@representation":  "minimal",
135//    "id":               1,
136//    "name":             "test",
137//    "slug":             "owner/repo"
138//  },
139//  "request":            {
140//    "repository":       {
141//      "id":             1,
142//      "owner_name":     "owner",
143//      "name":           "repo"
144//    },
145//    "user":             {
146//      "id":             1
147//    },
148//    "id":               1,
149//    "message":          "Override the commit message: this is an api request",
150//    "branch":           "master",
151//    "config":           { }
152//  },
153//  "resource_type":      "request"
154//}
155type createRequestResponse struct {
156	Request Request `json:"request"`
157}
158
159// ListByRepoId fetches requests of given repository id
160//
161// Travis CI API docs: https://developer.travis-ci.com/resource/requests#find
162func (rs *RequestsService) ListByRepoId(ctx context.Context, repoId uint, opt *ListRequestsOption) ([]*Request, *http.Response, error) {
163	u, err := urlWithOptions(fmt.Sprintf("/repo/%d/requests", repoId), opt)
164	if err != nil {
165		return nil, nil, err
166	}
167
168	req, err := rs.client.NewRequest(http.MethodGet, u, nil, nil)
169	if err != nil {
170		return nil, nil, err
171	}
172
173	var getRequestsResponse getRequestsResponse
174	resp, err := rs.client.Do(ctx, req, &getRequestsResponse)
175	if err != nil {
176		return nil, resp, err
177	}
178
179	return getRequestsResponse.Requests, resp, err
180}
181
182// ListByRepoSlug fetches requests of given repository slug
183//
184// Travis CI API docs: https://developer.travis-ci.com/resource/requests#find
185func (rs *RequestsService) ListByRepoSlug(ctx context.Context, repoSlug string, opt *ListRequestsOption) ([]*Request, *http.Response, error) {
186	u, err := urlWithOptions(fmt.Sprintf("/repo/%s/requests", url.QueryEscape(repoSlug)), opt)
187	if err != nil {
188		return nil, nil, err
189	}
190
191	req, err := rs.client.NewRequest(http.MethodGet, u, nil, nil)
192	if err != nil {
193		return nil, nil, err
194	}
195
196	var getRequestsResponse getRequestsResponse
197	resp, err := rs.client.Do(ctx, req, &getRequestsResponse)
198	if err != nil {
199		return nil, resp, err
200	}
201
202	return getRequestsResponse.Requests, resp, err
203}
204
205// CreateByRepoId create requests of given repository id and provided options
206//
207// Travis CI API docs: https://developer.travis-ci.com/resource/requests#create
208func (rs *RequestsService) CreateByRepoId(ctx context.Context, repoId uint, request *RequestBody) (*Request, *http.Response, error) {
209	u, err := urlWithOptions(fmt.Sprintf("/repo/%d/requests", repoId), nil)
210	if err != nil {
211		return nil, nil, err
212	}
213
214	req, err := rs.client.NewRequest(http.MethodPost, u, request, nil)
215	if err != nil {
216		return nil, nil, err
217	}
218
219	var createRequestResponse createRequestResponse
220	resp, err := rs.client.Do(ctx, req, &createRequestResponse)
221	if err != nil {
222		return nil, resp, err
223	}
224
225	return &createRequestResponse.Request, resp, err
226}
227
228// CreateByRepoSlug create requests of given repository slug and provided options
229//
230// Travis CI API docs: https://developer.travis-ci.com/resource/requests#create
231func (rs *RequestsService) CreateByRepoSlug(ctx context.Context, repoSlug string, request *RequestBody) (*Request, *http.Response, error) {
232	u, err := urlWithOptions(fmt.Sprintf("/repo/%s/requests", url.QueryEscape(repoSlug)), nil)
233	if err != nil {
234		return nil, nil, err
235	}
236
237	req, err := rs.client.NewRequest(http.MethodPost, u, request, nil)
238	if err != nil {
239		return nil, nil, err
240	}
241
242	var createRequestResponse createRequestResponse
243	resp, err := rs.client.Do(ctx, req, &createRequestResponse)
244	if err != nil {
245		return nil, resp, err
246	}
247
248	return &createRequestResponse.Request, resp, err
249}
250