/
api.go
250 lines (230 loc) · 8.94 KB
/
api.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
package api
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/google/go-github/github"
"github.com/google/uuid"
"github.com/jackc/pgx/v4"
log "github.com/sirupsen/logrus"
)
// function used to start new authentication service
func New() *GoGetGitAPI {
// read environment variables from config into local variables and connect persistence
router := gin.New()
service := GoGetGitAPI{router}
// configure GET routes used for server
service.router.GET("/go-get-git/health", service.HealthCheck)
service.router.GET("/go-get-git/registry", service.GetRegistryEntries)
service.router.GET("/go-get-git/registry/:entryId", service.GetRegistryEntry)
service.router.GET("/go-get-git/hooks", service.GetHookEntries)
service.router.GET("/go-get-git/hooks/:entryId", service.GetHookEntriesById)
service.router.GET("/go-get-git/hook/:hookId", service.GetHookEntry)
// configure POST routes used for server
service.router.POST("/go-get-git/registry", service.CreateRegistryEntry)
service.router.POST("/go-get-git/webhook", service.HandleGitWebHook)
// configure DELETE routes used for server
service.router.DELETE("/go-get-git/registry/:entryId", service.RemoveRegistryEntry)
return &service
}
func getUser(ctx *gin.Context) string {
return ctx.Request.Header.Get("X-Authenticated-Userid")
}
type GoGetGitAPI struct {
router *gin.Engine
}
func(api GoGetGitAPI) Run() {
// configure environment variables and connect persistence
ConfigureService()
ConnectPersistence()
connection := fmt.Sprintf("%s:%d", ListenAddress, ListenPort)
log.Info(fmt.Sprintf("starting new go-get-git service at %s", connection))
api.router.Run(connection)
}
// function used as basic health check
func(api GoGetGitAPI) HealthCheck(ctx *gin.Context) {
StandardHTTP.Success(ctx)
}
// API Handler used to create new registry entries
func(api GoGetGitAPI) CreateRegistryEntry(ctx *gin.Context) {
log.Debug(fmt.Sprintf("received request to create registry entry for user %s", getUser(ctx)))
var requestBody NewRegistryEntry
err := ctx.ShouldBind(&requestBody)
if err != nil {
log.Error(fmt.Sprintf("received invalid request body"))
StandardHTTP.InvalidRequestBody(ctx)
return
}
log.Debug(fmt.Sprintf("processing request with body %+v", requestBody))
// create new repo entry in database
entryId, err := persistence.createRepoEntry(getUser(ctx), requestBody)
if err != nil {
log.Error(fmt.Sprintf("received invalid request body"))
StandardHTTP.InternalServerError(ctx)
return
}
// create new applications and process event
err = processNewApplicationEvent(ctx, entryId, getUser(ctx), requestBody.RepoName, requestBody.RepoUrl)
if err != nil {
log.Error(fmt.Errorf("unable to process new application: %v", err))
StandardHTTP.InternalServerError(ctx)
return
}
// create new git hook on git server
body, err := createGitWebHook(requestBody.RepoOwner, requestBody.RepoName, requestBody.RepoAccessToken)
if err != nil {
log.Error(fmt.Errorf("unable to create git hook: %v", err))
StandardHTTP.InvalidRequest(ctx)
return
}
// create new hook entry in database
_, err = persistence.createHookEntry(entryId, body)
if err != nil {
log.Error(fmt.Sprintf("received invalid request body"))
StandardHTTP.InternalServerError(ctx)
return
}
response := gin.H{"http_code": 200, "success": true, "message": "successfully registered new repo"}
ctx.JSON(200, response)
}
// API Handler used to get specific registry entry
func(api GoGetGitAPI) GetRegistryEntry(ctx *gin.Context) {
entryId, err := uuid.Parse(ctx.Param("entryId"))
if err != nil {
log.Error(fmt.Sprintf("received invalid uuid %s", ctx.Param("entryId")))
StandardHTTP.InvalidRequest(ctx)
return
}
// get repo entry from database
entry, err := persistence.getRepoEntry(entryId)
if err != nil {
switch err {
// return 404 if no data entry exists
case pgx.ErrNoRows:
StandardHTTP.NotFound(ctx)
return
// by default, return internal server error
default:
StandardHTTP.InternalServerError(ctx)
return
}
}
ctx.JSON(200, gin.H{ "http_code": 200, "success": true, "payload": entry})
}
// API Hander used to get user registry entries
func(api GoGetGitAPI) GetRegistryEntries(ctx *gin.Context) {
log.Debug(fmt.Sprintf("received request for registry entries from user %s", getUser(ctx)))
// retrieve repo entries from database
entries, err := persistence.getAllRepoEntries()
if err != nil {
switch err {
case pgx.ErrNoRows:
ctx.JSON(200, gin.H{ "http_code": 200, "success": true, "payload": []GitRepoEntry{}})
return
default:
StandardHTTP.InternalServerError(ctx)
return
}
}
ctx.JSON(200, gin.H{ "http_code": 200, "success": true, "payload": entries})
}
// API Handler used to remove registry entry
func(api GoGetGitAPI) RemoveRegistryEntry(ctx *gin.Context) {
StandardHTTP.FeatureNotSupported(ctx)
}
// API route used to handle git hooks. Note that only Git Hooks
// that contain pushes to the master repositrory are handled and
// sent over the message bus
func(api GoGetGitAPI) HandleGitWebHook(ctx *gin.Context) {
log.Info("received new git hook trigger")
// validate git hook request
payload, err := github.ValidatePayload(ctx.Request, []byte(GitHookSecret))
if err != nil {
log.Error(fmt.Errorf("unable to validate hook signature: %v", err))
StandardHTTP.Forbidden(ctx)
return
}
// parse event
event, err := github.ParseWebHook(github.WebHookType(ctx.Request), payload)
if err != nil {
log.Error(fmt.Errorf("unable to parse webhook: %v", err))
StandardHTTP.InternalServerError(ctx)
return
}
log.Info(fmt.Sprintf("received event hook %+v", event))
// check event type matches Push Event, and then check that push referse to master branch
switch e := event.(type) {
case *github.PushEvent:
if isMasterPushEvent(e) {
processGitPushEvent(ctx, e)
} else {
log.Info(fmt.Sprintf("received push event to non-master branch %s", *e.Ref))
}
default:
log.Info(fmt.Sprintf("received non-push type event %v", e))
}
StandardHTTP.Success(ctx)
}
// API Route used to retrieve a particular hook entry by Hook ID
func(api GoGetGitAPI) GetHookEntry(ctx *gin.Context) {
// parse hook ID into UUID format and return 400 if invalid
hookId, err := uuid.Parse(ctx.Param("hookId"))
if err != nil {
log.Error(fmt.Sprintf("received invalid uuid %s", ctx.Param("entryId")))
StandardHTTP.InvalidRequest(ctx)
return
}
log.Debug(fmt.Sprintf("received request for hook with hook ID %s", hookId))
// get hook entry from database. note that 404 if found if entry doesnt exist
entry, err := persistence.getHookEntry(hookId)
if err != nil {
switch err {
case pgx.ErrNoRows:
StandardHTTP.NotFound(ctx)
return
default:
StandardHTTP.InternalServerError(ctx)
return
}
}
ctx.JSON(200, gin.H{ "http_code": 200, "success": true, "payload": entry})
}
// API Route used to retrieve all hook entries currently stored in database
func(api GoGetGitAPI) GetHookEntries(ctx *gin.Context) {
log.Debug(fmt.Sprintf("received request to fetch all hook entries from user %s", getUser(ctx)))
// retrieve list of hook entries from database and return
entries, err := persistence.getAllHookEntries()
if err != nil {
switch err {
case pgx.ErrNoRows:
ctx.JSON(200, gin.H{ "http_code": 200, "success": true, "payload": []GitHookEntry{}})
return
default:
StandardHTTP.InternalServerError(ctx)
return
}
}
ctx.JSON(200, gin.H{ "http_code": 200, "success": true, "payload": entries})
}
// API route used to retrieve all git hook entries that belong
// to a particular parent ID
func(api GoGetGitAPI) GetHookEntriesById(ctx *gin.Context) {
entryId, err := uuid.Parse(ctx.Param("entryId"))
if err != nil {
log.Error(fmt.Sprintf("received invalid uuid %s", ctx.Param("entryId")))
StandardHTTP.InvalidRequest(ctx)
return
}
log.Debug(fmt.Sprintf("received request for hook entries for entry ID %s", entryId))
entries, err := persistence.getAllHookEntriesByEntryId(entryId)
if err != nil {
switch err {
case pgx.ErrNoRows:
ctx.JSON(200, gin.H{ "http_code": 200, "success": true, "payload": []GitHookEntry{}})
return
default:
StandardHTTP.InternalServerError(ctx)
return
}
}
ctx.JSON(200, gin.H{ "http_code": 200, "success": true, "payload": entries})
}