@@ -40,8 +40,14 @@ type Payload struct {
40
40
MergeRequest * MergeRequest `json:"merge_request,omitempty"` // "merge_request" is sent on "note" activity
41
41
}
42
42
43
- func errHandler (w http.ResponseWriter , code int , err error ) {
44
- slog .Error (err .Error ())
43
+ func errHandler (ctx context.Context , w http.ResponseWriter , code int , err error ) {
44
+ switch code {
45
+ case http .StatusOK :
46
+ slogctx .Info (ctx , "Server response" , slog .Int ("response_code" , code ), slog .Any ("response_message" , err ))
47
+
48
+ default :
49
+ slogctx .Error (ctx , "Server response" , slog .Int ("response_code" , code ), slog .Any ("response_message" , err ))
50
+ }
45
51
46
52
w .WriteHeader (code )
47
53
w .Write ([]byte (err .Error ()))
@@ -50,7 +56,7 @@ func errHandler(w http.ResponseWriter, code int, err error) {
50
56
}
51
57
52
58
func Server (cCtx * cli.Context ) error { //nolint:unparam
53
- slogctx .Info (cCtx .Context , "Starting HTTP server" )
59
+ slogctx .Info (cCtx .Context , "Starting HTTP server" , slog . String ( "listen" , cCtx . String ( FlagServerListen )) )
54
60
55
61
mux := http .NewServeMux ()
56
62
@@ -62,42 +68,51 @@ func Server(cCtx *cli.Context) error { //nolint:unparam
62
68
return err
63
69
}
64
70
65
- mux .HandleFunc ("POST /gitlab" , func (writer http.ResponseWriter , reader * http.Request ) {
66
- slogctx . Info ( reader . Context (), "Handling /gitlab request" )
71
+ mux .HandleFunc ("POST /gitlab" , func (w http.ResponseWriter , r * http.Request ) {
72
+ ctx := r . Context ()
67
73
74
+ slogctx .Info (ctx , "Handling /gitlab request" )
75
+
76
+ // Check if the webhook secret is set (and if its matching)
68
77
if len (ourSecret ) > 0 {
69
- theirSecret := reader .Header .Get ("X-Gitlab-Token" )
78
+ theirSecret := r .Header .Get ("X-Gitlab-Token" )
70
79
if ourSecret != theirSecret {
71
- errHandler (writer , http .StatusForbidden , errors .New ("Missing or invalid X-Gitlab-Token header" ))
80
+ errHandler (ctx , w , http .StatusForbidden , errors .New ("Missing or invalid X-Gitlab-Token header" ))
72
81
73
82
return
74
83
}
75
84
}
76
85
77
- // Validate headers
78
- if reader .Header .Get ("Content-Type" ) != "application/json" {
79
- errHandler (writer , http .StatusInternalServerError , errors .New ("not json" ))
86
+ // Validate content type
87
+ if r .Header .Get ("Content-Type" ) != "application/json" {
88
+ errHandler (ctx , w , http .StatusNotAcceptable , errors .New ("The request is not using Content-Type: application/ json" ))
80
89
81
90
return
82
91
}
83
92
84
- body , err := io .ReadAll (reader .Body )
93
+ // Read the POST body of the request
94
+ body , err := io .ReadAll (r .Body )
85
95
if err != nil {
86
- errHandler (writer , http .StatusInternalServerError , err )
96
+ errHandler (ctx , w , http .StatusBadRequest , err )
87
97
88
98
return
89
99
}
90
100
101
+ // Ensure we have content in the POST body
102
+ if len (body ) == 0 {
103
+ errHandler (ctx , w , http .StatusBadRequest , errors .New ("The POST body is empty; expected a JSON payload" ))
104
+ }
105
+
91
106
// Decode request payload
92
107
var payload Payload
93
108
if err := json .NewDecoder (bytes .NewReader (body )).Decode (& payload ); err != nil {
94
- errHandler (writer , http .StatusInternalServerError , err )
109
+ errHandler (ctx , w , http .StatusBadRequest , fmt . Errorf ( "could not decode POST body into Payload struct: %w" , err ) )
95
110
96
111
return
97
112
}
98
113
99
114
// Initialize context
100
- ctx : = state .ContextWithProjectID (reader . Context () , payload .Project .PathWithNamespace )
115
+ ctx = state .ContextWithProjectID (ctx , payload .Project .PathWithNamespace )
101
116
102
117
// Grab event specific information
103
118
var (
@@ -115,46 +130,48 @@ func Server(cCtx *cli.Context) error { //nolint:unparam
115
130
ref = payload .MergeRequest .LastCommit .ID
116
131
117
132
default :
118
- errHandler (writer , http .StatusInternalServerError , fmt .Errorf ("unknown event: %s" , payload .EventType ))
133
+ errHandler (ctx , w , http .StatusInternalServerError , fmt .Errorf ("unknown event type : %s" , payload .EventType ))
119
134
}
120
135
136
+ ctx = slogctx .With (ctx , slog .String ("event_type" , payload .EventType ), slog .String ("merge_request_id" , id ), slog .String ("sha_reference" , ref ))
137
+
121
138
// Get the remote config file
122
139
file , err := client .MergeRequests ().GetRemoteConfig (ctx , cCtx .String (FlagConfigFile ), ref )
123
140
if err != nil {
124
- errHandler (writer , http .StatusOK , err )
141
+ errHandler (ctx , w , http .StatusOK , fmt . Errorf ( "could not read remote config file: %w" , err ) )
125
142
126
143
return
127
144
}
128
145
129
146
// Parse the file
130
147
cfg , err := config .ParseFile (file )
131
148
if err != nil {
132
- errHandler (writer , http .StatusOK , err )
149
+ errHandler (ctx , w , http .StatusOK , fmt . Errorf ( "could not parse config file: %w" , err ) )
133
150
134
151
return
135
152
}
136
153
137
- // Decode request payload
138
- var full any
139
- if err := json .NewDecoder (bytes .NewReader (body )).Decode (& full ); err != nil {
140
- errHandler (writer , http .StatusInternalServerError , err )
154
+ // Decode request payload into 'any' so we have all the details
155
+ var fullEventPayload any
156
+ if err := json .NewDecoder (bytes .NewReader (body )).Decode (& fullEventPayload ); err != nil {
157
+ errHandler (ctx , w , http .StatusInternalServerError , err )
141
158
142
159
return
143
160
}
144
161
145
162
// Process the MR
146
- if err := ProcessMR (ctx , client , cfg , id , full ); err != nil {
147
- errHandler (writer , http .StatusOK , err )
163
+ if err := ProcessMR (ctx , client , cfg , id , fullEventPayload ); err != nil {
164
+ errHandler (ctx , w , http .StatusOK , err )
148
165
149
166
return
150
167
}
151
168
152
- writer .WriteHeader (http .StatusOK )
153
- writer .Write ([]byte ("OK" ))
169
+ w .WriteHeader (http .StatusOK )
170
+ w .Write ([]byte ("OK" ))
154
171
})
155
172
156
173
server := & http.Server {
157
- Addr : "0.0.0.0:3000" ,
174
+ Addr : cCtx . String ( FlagServerListen ) ,
158
175
Handler : http .Handler (mux ),
159
176
ReadTimeout : 5 * time .Second ,
160
177
WriteTimeout : 5 * time .Second ,
0 commit comments