blob: 2554941bf5e42c470c27d54f7fee55cd8eac8cc8 [file] [log] [blame]
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08001package requests
2
3import (
Philipp Schraderfae8a7e2022-03-13 22:51:54 -07004 "encoding/base64"
Philipp Schraderd3fac192022-03-02 20:35:46 -08005 "errors"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08006 "fmt"
7 "io"
Philipp Schraderfae8a7e2022-03-13 22:51:54 -07008 "log"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08009 "net/http"
Philipp Schraderd3fac192022-03-02 20:35:46 -080010 "strconv"
11 "strings"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080012
Philipp Schrader8747f1b2022-02-23 23:56:22 -080013 "github.com/frc971/971-Robot-Code/scouting/db"
Philipp Schraderd3fac192022-03-02 20:35:46 -080014 "github.com/frc971/971-Robot-Code/scouting/scraping"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080015 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
Philipp Schraderd3fac192022-03-02 20:35:46 -080016 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/refresh_match_list"
17 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/refresh_match_list_response"
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080018 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches"
19 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
Philipp Schraderacf96232022-03-01 22:03:30 -080020 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting"
21 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_response"
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080022 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team"
23 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070024 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team"
25 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080026 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting"
Philipp Schrader30005e42022-03-06 13:53:58 -080027 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070028 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes"
29 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080030 "github.com/frc971/971-Robot-Code/scouting/webserver/server"
31 flatbuffers "github.com/google/flatbuffers/go"
32)
33
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080034type SubmitDataScouting = submit_data_scouting.SubmitDataScouting
Philipp Schrader30005e42022-03-06 13:53:58 -080035type SubmitDataScoutingResponseT = submit_data_scouting_response.SubmitDataScoutingResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080036type RequestAllMatches = request_all_matches.RequestAllMatches
37type RequestAllMatchesResponseT = request_all_matches_response.RequestAllMatchesResponseT
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080038type RequestMatchesForTeam = request_matches_for_team.RequestMatchesForTeam
39type RequestMatchesForTeamResponseT = request_matches_for_team_response.RequestMatchesForTeamResponseT
Philipp Schraderacf96232022-03-01 22:03:30 -080040type RequestDataScouting = request_data_scouting.RequestDataScouting
41type RequestDataScoutingResponseT = request_data_scouting_response.RequestDataScoutingResponseT
Philipp Schraderd3fac192022-03-02 20:35:46 -080042type RefreshMatchList = refresh_match_list.RefreshMatchList
43type RefreshMatchListResponseT = refresh_match_list_response.RefreshMatchListResponseT
Alex Perry81f96ba2022-03-13 18:26:19 -070044type SubmitNotes = submit_notes.SubmitNotes
45type SubmitNotesResponseT = submit_notes_response.SubmitNotesResponseT
46type RequestNotesForTeam = request_notes_for_team.RequestNotesForTeam
47type RequestNotesForTeamResponseT = request_notes_for_team_response.RequestNotesForTeamResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080048
Philipp Schrader8747f1b2022-02-23 23:56:22 -080049// The interface we expect the database abstraction to conform to.
50// We use an interface here because it makes unit testing easier.
51type Database interface {
52 AddToMatch(db.Match) error
53 AddToStats(db.Stats) error
54 ReturnMatches() ([]db.Match, error)
55 ReturnStats() ([]db.Stats, error)
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080056 QueryMatches(int32) ([]db.Match, error)
Philipp Schrader8747f1b2022-02-23 23:56:22 -080057 QueryStats(int) ([]db.Stats, error)
Alex Perry81f96ba2022-03-13 18:26:19 -070058 QueryNotes(int32) (db.NotesData, error)
59 AddNotes(db.NotesData) error
Philipp Schrader8747f1b2022-02-23 23:56:22 -080060}
61
Philipp Schraderd3fac192022-03-02 20:35:46 -080062type ScrapeMatchList func(int32, string) ([]scraping.Match, error)
63
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080064// Handles unknown requests. Just returns a 404.
65func unknown(w http.ResponseWriter, req *http.Request) {
66 w.WriteHeader(http.StatusNotFound)
67}
68
69func respondWithError(w http.ResponseWriter, statusCode int, errorMessage string) {
70 builder := flatbuffers.NewBuilder(1024)
71 builder.Finish((&error_response.ErrorResponseT{
72 ErrorMessage: errorMessage,
73 }).Pack(builder))
74 w.WriteHeader(statusCode)
75 w.Write(builder.FinishedBytes())
76}
77
78func respondNotImplemented(w http.ResponseWriter) {
79 respondWithError(w, http.StatusNotImplemented, "")
80}
81
82// TODO(phil): Can we turn this into a generic?
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080083func parseSubmitDataScouting(w http.ResponseWriter, buf []byte) (*SubmitDataScouting, bool) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080084 success := true
85 defer func() {
86 if r := recover(); r != nil {
87 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
88 success = false
89 }
90 }()
91 result := submit_data_scouting.GetRootAsSubmitDataScouting(buf, 0)
92 return result, success
93}
94
Philipp Schraderfae8a7e2022-03-13 22:51:54 -070095// Parses the authorization information that the browser inserts into the
96// headers. The authorization follows this format:
97//
98// req.Headers["Authorization"] = []string{"Basic <base64 encoded username:password>"}
99func parseUsername(req *http.Request) string {
100 auth, ok := req.Header["Authorization"]
101 if !ok {
102 return "unknown"
103 }
104
105 parts := strings.Split(auth[0], " ")
106 if !(len(parts) == 2 && parts[0] == "Basic") {
107 return "unknown"
108 }
109
110 info, err := base64.StdEncoding.DecodeString(parts[1])
111 if err != nil {
112 log.Println("ERROR: Failed to parse Basic authentication.")
113 return "unknown"
114 }
115
116 loginParts := strings.Split(string(info), ":")
117 if len(loginParts) != 2 {
118 return "unknown"
119 }
120 return loginParts[0]
121}
122
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800123// Handles a SubmitDataScouting request.
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800124type submitDataScoutingHandler struct {
125 db Database
126}
127
128func (handler submitDataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700129 // Get the username of the person submitting the data.
130 username := parseUsername(req)
131 log.Println("Got data scouting data from", username)
132
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800133 requestBytes, err := io.ReadAll(req.Body)
134 if err != nil {
135 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
136 return
137 }
138
Philipp Schrader30005e42022-03-06 13:53:58 -0800139 request, success := parseSubmitDataScouting(w, requestBytes)
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800140 if !success {
141 return
142 }
143
Philipp Schrader30005e42022-03-06 13:53:58 -0800144 stats := db.Stats{
Philipp Schraderfee07e12022-03-17 22:19:47 -0700145 TeamNumber: request.Team(),
146 MatchNumber: request.Match(),
147 StartingQuadrant: request.StartingQuadrant(),
148 AutoBallPickedUp: [5]bool{
149 request.AutoBall1(), request.AutoBall2(), request.AutoBall3(),
150 request.AutoBall4(), request.AutoBall5(),
151 },
Philipp Schraderfa45d742022-03-18 19:29:05 -0700152 ShotsMissedAuto: request.MissedShotsAuto(),
153 UpperGoalAuto: request.UpperGoalAuto(),
154 LowerGoalAuto: request.LowerGoalAuto(),
155 ShotsMissed: request.MissedShotsTele(),
156 UpperGoalShots: request.UpperGoalTele(),
157 LowerGoalShots: request.LowerGoalTele(),
158 PlayedDefense: request.DefenseRating(),
159 DefenseReceivedScore: request.DefenseReceivedRating(),
160 Climbing: int32(request.ClimbLevel()),
161 CollectedBy: username,
162 Comment: string(request.Comment()),
Philipp Schrader30005e42022-03-06 13:53:58 -0800163 }
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800164
Philipp Schraderfee07e12022-03-17 22:19:47 -0700165 // Do some error checking.
166 if stats.StartingQuadrant < 1 || stats.StartingQuadrant > 4 {
167 respondWithError(w, http.StatusBadRequest, fmt.Sprint(
168 "Invalid starting_quadrant field value of ", stats.StartingQuadrant))
169 return
170 }
171
Philipp Schrader30005e42022-03-06 13:53:58 -0800172 err = handler.db.AddToStats(stats)
173 if err != nil {
174 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit datascouting data: ", err))
Philipp Schraderfee07e12022-03-17 22:19:47 -0700175 return
Philipp Schrader30005e42022-03-06 13:53:58 -0800176 }
177
178 builder := flatbuffers.NewBuilder(50 * 1024)
179 builder.Finish((&SubmitDataScoutingResponseT{}).Pack(builder))
180 w.Write(builder.FinishedBytes())
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800181}
182
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800183// TODO(phil): Can we turn this into a generic?
184func parseRequestAllMatches(w http.ResponseWriter, buf []byte) (*RequestAllMatches, bool) {
185 success := true
186 defer func() {
187 if r := recover(); r != nil {
188 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
189 success = false
190 }
191 }()
192 result := request_all_matches.GetRootAsRequestAllMatches(buf, 0)
193 return result, success
194}
195
196// Handles a RequestAllMaches request.
197type requestAllMatchesHandler struct {
198 db Database
199}
200
201func (handler requestAllMatchesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
202 requestBytes, err := io.ReadAll(req.Body)
203 if err != nil {
204 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
205 return
206 }
207
208 _, success := parseRequestAllMatches(w, requestBytes)
209 if !success {
210 return
211 }
212
213 matches, err := handler.db.ReturnMatches()
214 if err != nil {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700215 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800216 return
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800217 }
218
219 var response RequestAllMatchesResponseT
220 for _, match := range matches {
221 response.MatchList = append(response.MatchList, &request_all_matches_response.MatchT{
222 MatchNumber: match.MatchNumber,
223 Round: match.Round,
224 CompLevel: match.CompLevel,
225 R1: match.R1,
226 R2: match.R2,
227 R3: match.R3,
228 B1: match.B1,
229 B2: match.B2,
230 B3: match.B3,
231 })
232 }
233
234 builder := flatbuffers.NewBuilder(50 * 1024)
235 builder.Finish((&response).Pack(builder))
236 w.Write(builder.FinishedBytes())
237}
238
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800239// TODO(phil): Can we turn this into a generic?
240func parseRequestMatchesForTeam(w http.ResponseWriter, buf []byte) (*RequestMatchesForTeam, bool) {
241 success := true
242 defer func() {
243 if r := recover(); r != nil {
244 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
245 success = false
246 }
247 }()
248 result := request_matches_for_team.GetRootAsRequestMatchesForTeam(buf, 0)
249 return result, success
250}
251
252// Handles a RequestMatchesForTeam request.
253type requestMatchesForTeamHandler struct {
254 db Database
255}
256
257func (handler requestMatchesForTeamHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
258 requestBytes, err := io.ReadAll(req.Body)
259 if err != nil {
260 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
261 return
262 }
263
264 request, success := parseRequestMatchesForTeam(w, requestBytes)
265 if !success {
266 return
267 }
268
269 matches, err := handler.db.QueryMatches(request.Team())
270 if err != nil {
271 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800272 return
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800273 }
274
275 var response RequestAllMatchesResponseT
276 for _, match := range matches {
277 response.MatchList = append(response.MatchList, &request_all_matches_response.MatchT{
278 MatchNumber: match.MatchNumber,
279 Round: match.Round,
280 CompLevel: match.CompLevel,
281 R1: match.R1,
282 R2: match.R2,
283 R3: match.R3,
284 B1: match.B1,
285 B2: match.B2,
286 B3: match.B3,
287 })
288 }
289
290 builder := flatbuffers.NewBuilder(50 * 1024)
291 builder.Finish((&response).Pack(builder))
292 w.Write(builder.FinishedBytes())
293}
294
Philipp Schraderacf96232022-03-01 22:03:30 -0800295// TODO(phil): Can we turn this into a generic?
296func parseRequestDataScouting(w http.ResponseWriter, buf []byte) (*RequestDataScouting, bool) {
297 success := true
298 defer func() {
299 if r := recover(); r != nil {
300 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
301 success = false
302 }
303 }()
304 result := request_data_scouting.GetRootAsRequestDataScouting(buf, 0)
305 return result, success
306}
307
308// Handles a RequestDataScouting request.
309type requestDataScoutingHandler struct {
310 db Database
311}
312
313func (handler requestDataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
314 requestBytes, err := io.ReadAll(req.Body)
315 if err != nil {
316 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
317 return
318 }
319
320 _, success := parseRequestDataScouting(w, requestBytes)
321 if !success {
322 return
323 }
324
325 stats, err := handler.db.ReturnStats()
326 if err != nil {
327 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800328 return
Philipp Schraderacf96232022-03-01 22:03:30 -0800329 }
330
331 var response RequestDataScoutingResponseT
332 for _, stat := range stats {
333 response.StatsList = append(response.StatsList, &request_data_scouting_response.StatsT{
Philipp Schraderfa45d742022-03-18 19:29:05 -0700334 Team: stat.TeamNumber,
335 Match: stat.MatchNumber,
336 StartingQuadrant: stat.StartingQuadrant,
337 AutoBall1: stat.AutoBallPickedUp[0],
338 AutoBall2: stat.AutoBallPickedUp[1],
339 AutoBall3: stat.AutoBallPickedUp[2],
340 AutoBall4: stat.AutoBallPickedUp[3],
341 AutoBall5: stat.AutoBallPickedUp[4],
342 MissedShotsAuto: stat.ShotsMissedAuto,
343 UpperGoalAuto: stat.UpperGoalAuto,
344 LowerGoalAuto: stat.LowerGoalAuto,
345 MissedShotsTele: stat.ShotsMissed,
346 UpperGoalTele: stat.UpperGoalShots,
347 LowerGoalTele: stat.LowerGoalShots,
348 DefenseRating: stat.PlayedDefense,
349 DefenseReceivedRating: stat.DefenseReceivedScore,
350 ClimbLevel: request_data_scouting_response.ClimbLevel(stat.Climbing),
351 CollectedBy: stat.CollectedBy,
352 Comment: stat.Comment,
Philipp Schraderacf96232022-03-01 22:03:30 -0800353 })
354 }
355
356 builder := flatbuffers.NewBuilder(50 * 1024)
357 builder.Finish((&response).Pack(builder))
358 w.Write(builder.FinishedBytes())
359}
360
Philipp Schraderd3fac192022-03-02 20:35:46 -0800361// TODO(phil): Can we turn this into a generic?
362func parseRefreshMatchList(w http.ResponseWriter, buf []byte) (*RefreshMatchList, bool) {
363 success := true
364 defer func() {
365 if r := recover(); r != nil {
366 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse RefreshMatchList: %v", r))
367 success = false
368 }
369 }()
370 result := refresh_match_list.GetRootAsRefreshMatchList(buf, 0)
371 return result, success
372}
373
374func parseTeamKey(teamKey string) (int, error) {
375 // TBA prefixes teams with "frc". Not sure why. Get rid of that.
376 teamKey = strings.TrimPrefix(teamKey, "frc")
377 return strconv.Atoi(teamKey)
378}
379
380// Parses the alliance data from the specified match and returns the three red
381// teams and the three blue teams.
382func parseTeamKeys(match *scraping.Match) ([3]int32, [3]int32, error) {
383 redKeys := match.Alliances.Red.TeamKeys
384 blueKeys := match.Alliances.Blue.TeamKeys
385
386 if len(redKeys) != 3 || len(blueKeys) != 3 {
387 return [3]int32{}, [3]int32{}, errors.New(fmt.Sprintf(
388 "Found %d red teams and %d blue teams.", len(redKeys), len(blueKeys)))
389 }
390
391 var red [3]int32
392 for i, key := range redKeys {
393 team, err := parseTeamKey(key)
394 if err != nil {
395 return [3]int32{}, [3]int32{}, errors.New(fmt.Sprintf(
396 "Failed to parse red %d team '%s' as integer: %v", i+1, key, err))
397 }
398 red[i] = int32(team)
399 }
400 var blue [3]int32
401 for i, key := range blueKeys {
402 team, err := parseTeamKey(key)
403 if err != nil {
404 return [3]int32{}, [3]int32{}, errors.New(fmt.Sprintf(
405 "Failed to parse blue %d team '%s' as integer: %v", i+1, key, err))
406 }
407 blue[i] = int32(team)
408 }
409 return red, blue, nil
410}
411
412type refreshMatchListHandler struct {
413 db Database
414 scrape ScrapeMatchList
415}
416
417func (handler refreshMatchListHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
418 requestBytes, err := io.ReadAll(req.Body)
419 if err != nil {
420 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
421 return
422 }
423
424 request, success := parseRefreshMatchList(w, requestBytes)
425 if !success {
426 return
427 }
428
429 matches, err := handler.scrape(request.Year(), string(request.EventCode()))
430 if err != nil {
431 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to scrape match list: ", err))
432 return
433 }
434
435 for _, match := range matches {
436 // Make sure the data is valid.
437 red, blue, err := parseTeamKeys(&match)
438 if err != nil {
439 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf(
440 "TheBlueAlliance data for match %d is malformed: %v", match.MatchNumber, err))
441 return
442 }
443 // Add the match to the database.
Philipp Schrader7365d322022-03-06 16:40:08 -0800444 err = handler.db.AddToMatch(db.Match{
Philipp Schraderd3fac192022-03-02 20:35:46 -0800445 MatchNumber: int32(match.MatchNumber),
446 // TODO(phil): What does Round mean?
447 Round: 1,
448 CompLevel: match.CompLevel,
449 R1: red[0],
450 R2: red[1],
451 R3: red[2],
452 B1: blue[0],
453 B2: blue[1],
454 B3: blue[2],
455 })
Philipp Schrader7365d322022-03-06 16:40:08 -0800456 if err != nil {
457 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf(
458 "Failed to add match %d to the database: %v", match.MatchNumber, err))
459 return
460 }
Philipp Schraderd3fac192022-03-02 20:35:46 -0800461 }
462
463 var response RefreshMatchListResponseT
464 builder := flatbuffers.NewBuilder(1024)
465 builder.Finish((&response).Pack(builder))
466 w.Write(builder.FinishedBytes())
467}
468
Alex Perry81f96ba2022-03-13 18:26:19 -0700469func parseSubmitNotes(w http.ResponseWriter, buf []byte) (*SubmitNotes, bool) {
470 success := true
471 defer func() {
472 if r := recover(); r != nil {
473 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse RefreshMatchList: %v", r))
474 success = false
475 }
476 }()
477 result := submit_notes.GetRootAsSubmitNotes(buf, 0)
478 return result, success
479}
480
481type submitNoteScoutingHandler struct {
482 db Database
483}
484
485func (handler submitNoteScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
486 requestBytes, err := io.ReadAll(req.Body)
487 if err != nil {
488 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
489 return
490 }
491
492 request, success := parseSubmitNotes(w, requestBytes)
493 if !success {
494 return
495 }
496
497 err = handler.db.AddNotes(db.NotesData{
498 TeamNumber: request.Team(),
499 Notes: []string{string(request.Notes())},
500 })
501 if err != nil {
502 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert notes: %v", err))
503 return
504 }
505
506 var response SubmitNotesResponseT
507 builder := flatbuffers.NewBuilder(10)
508 builder.Finish((&response).Pack(builder))
509 w.Write(builder.FinishedBytes())
510}
511
512func parseRequestNotesForTeam(w http.ResponseWriter, buf []byte) (*RequestNotesForTeam, bool) {
513 success := true
514 defer func() {
515 if r := recover(); r != nil {
516 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse RefreshMatchList: %v", r))
517 success = false
518 }
519 }()
520 result := request_notes_for_team.GetRootAsRequestNotesForTeam(buf, 0)
521 return result, success
522}
523
524type requestNotesForTeamHandler struct {
525 db Database
526}
527
528func (handler requestNotesForTeamHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
529 requestBytes, err := io.ReadAll(req.Body)
530 if err != nil {
531 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
532 return
533 }
534
535 request, success := parseRequestNotesForTeam(w, requestBytes)
536 if !success {
537 return
538 }
539
540 notesData, err := handler.db.QueryNotes(request.Team())
541 if err != nil {
542 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query notes: %v", err))
543 return
544 }
545
546 var response RequestNotesForTeamResponseT
547 for _, data := range notesData.Notes {
548 response.Notes = append(response.Notes, &request_notes_for_team_response.NoteT{data})
549 }
550
551 builder := flatbuffers.NewBuilder(1024)
552 builder.Finish((&response).Pack(builder))
553 w.Write(builder.FinishedBytes())
554}
555
Philipp Schraderd3fac192022-03-02 20:35:46 -0800556func HandleRequests(db Database, scrape ScrapeMatchList, scoutingServer server.ScoutingServer) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800557 scoutingServer.HandleFunc("/requests", unknown)
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800558 scoutingServer.Handle("/requests/submit/data_scouting", submitDataScoutingHandler{db})
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800559 scoutingServer.Handle("/requests/request/all_matches", requestAllMatchesHandler{db})
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800560 scoutingServer.Handle("/requests/request/matches_for_team", requestMatchesForTeamHandler{db})
Philipp Schraderacf96232022-03-01 22:03:30 -0800561 scoutingServer.Handle("/requests/request/data_scouting", requestDataScoutingHandler{db})
Philipp Schraderd3fac192022-03-02 20:35:46 -0800562 scoutingServer.Handle("/requests/refresh_match_list", refreshMatchListHandler{db, scrape})
Alex Perry81f96ba2022-03-13 18:26:19 -0700563 scoutingServer.Handle("/requests/submit/submit_notes", submitNoteScoutingHandler{db})
564 scoutingServer.Handle("/requests/request/notes_for_team", requestNotesForTeamHandler{db})
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800565}