blob: 118fafb9403a13f6164875f72541334cd1236aac [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 Schrader30005e42022-03-06 13:53:58 -0800152 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(),
Philipp Schrader36df73a2022-03-17 23:27:24 -0700159 Climbing: int32(request.ClimbLevel()),
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700160 CollectedBy: username,
Philipp Schrader30005e42022-03-06 13:53:58 -0800161 }
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800162
Philipp Schraderfee07e12022-03-17 22:19:47 -0700163 // Do some error checking.
164 if stats.StartingQuadrant < 1 || stats.StartingQuadrant > 4 {
165 respondWithError(w, http.StatusBadRequest, fmt.Sprint(
166 "Invalid starting_quadrant field value of ", stats.StartingQuadrant))
167 return
168 }
169
Philipp Schrader30005e42022-03-06 13:53:58 -0800170 err = handler.db.AddToStats(stats)
171 if err != nil {
172 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to submit datascouting data: ", err))
Philipp Schraderfee07e12022-03-17 22:19:47 -0700173 return
Philipp Schrader30005e42022-03-06 13:53:58 -0800174 }
175
176 builder := flatbuffers.NewBuilder(50 * 1024)
177 builder.Finish((&SubmitDataScoutingResponseT{}).Pack(builder))
178 w.Write(builder.FinishedBytes())
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800179}
180
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800181// TODO(phil): Can we turn this into a generic?
182func parseRequestAllMatches(w http.ResponseWriter, buf []byte) (*RequestAllMatches, bool) {
183 success := true
184 defer func() {
185 if r := recover(); r != nil {
186 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
187 success = false
188 }
189 }()
190 result := request_all_matches.GetRootAsRequestAllMatches(buf, 0)
191 return result, success
192}
193
194// Handles a RequestAllMaches request.
195type requestAllMatchesHandler struct {
196 db Database
197}
198
199func (handler requestAllMatchesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
200 requestBytes, err := io.ReadAll(req.Body)
201 if err != nil {
202 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
203 return
204 }
205
206 _, success := parseRequestAllMatches(w, requestBytes)
207 if !success {
208 return
209 }
210
211 matches, err := handler.db.ReturnMatches()
212 if err != nil {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -0700213 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Failed to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800214 return
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800215 }
216
217 var response RequestAllMatchesResponseT
218 for _, match := range matches {
219 response.MatchList = append(response.MatchList, &request_all_matches_response.MatchT{
220 MatchNumber: match.MatchNumber,
221 Round: match.Round,
222 CompLevel: match.CompLevel,
223 R1: match.R1,
224 R2: match.R2,
225 R3: match.R3,
226 B1: match.B1,
227 B2: match.B2,
228 B3: match.B3,
229 })
230 }
231
232 builder := flatbuffers.NewBuilder(50 * 1024)
233 builder.Finish((&response).Pack(builder))
234 w.Write(builder.FinishedBytes())
235}
236
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800237// TODO(phil): Can we turn this into a generic?
238func parseRequestMatchesForTeam(w http.ResponseWriter, buf []byte) (*RequestMatchesForTeam, bool) {
239 success := true
240 defer func() {
241 if r := recover(); r != nil {
242 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
243 success = false
244 }
245 }()
246 result := request_matches_for_team.GetRootAsRequestMatchesForTeam(buf, 0)
247 return result, success
248}
249
250// Handles a RequestMatchesForTeam request.
251type requestMatchesForTeamHandler struct {
252 db Database
253}
254
255func (handler requestMatchesForTeamHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
256 requestBytes, err := io.ReadAll(req.Body)
257 if err != nil {
258 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
259 return
260 }
261
262 request, success := parseRequestMatchesForTeam(w, requestBytes)
263 if !success {
264 return
265 }
266
267 matches, err := handler.db.QueryMatches(request.Team())
268 if err != nil {
269 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800270 return
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800271 }
272
273 var response RequestAllMatchesResponseT
274 for _, match := range matches {
275 response.MatchList = append(response.MatchList, &request_all_matches_response.MatchT{
276 MatchNumber: match.MatchNumber,
277 Round: match.Round,
278 CompLevel: match.CompLevel,
279 R1: match.R1,
280 R2: match.R2,
281 R3: match.R3,
282 B1: match.B1,
283 B2: match.B2,
284 B3: match.B3,
285 })
286 }
287
288 builder := flatbuffers.NewBuilder(50 * 1024)
289 builder.Finish((&response).Pack(builder))
290 w.Write(builder.FinishedBytes())
291}
292
Philipp Schraderacf96232022-03-01 22:03:30 -0800293// TODO(phil): Can we turn this into a generic?
294func parseRequestDataScouting(w http.ResponseWriter, buf []byte) (*RequestDataScouting, bool) {
295 success := true
296 defer func() {
297 if r := recover(); r != nil {
298 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
299 success = false
300 }
301 }()
302 result := request_data_scouting.GetRootAsRequestDataScouting(buf, 0)
303 return result, success
304}
305
306// Handles a RequestDataScouting request.
307type requestDataScoutingHandler struct {
308 db Database
309}
310
311func (handler requestDataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
312 requestBytes, err := io.ReadAll(req.Body)
313 if err != nil {
314 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
315 return
316 }
317
318 _, success := parseRequestDataScouting(w, requestBytes)
319 if !success {
320 return
321 }
322
323 stats, err := handler.db.ReturnStats()
324 if err != nil {
325 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to query database: ", err))
Philipp Schrader2e7eb0002022-03-02 22:52:39 -0800326 return
Philipp Schraderacf96232022-03-01 22:03:30 -0800327 }
328
329 var response RequestDataScoutingResponseT
330 for _, stat := range stats {
331 response.StatsList = append(response.StatsList, &request_data_scouting_response.StatsT{
Philipp Schraderfee07e12022-03-17 22:19:47 -0700332 Team: stat.TeamNumber,
333 Match: stat.MatchNumber,
334 StartingQuadrant: stat.StartingQuadrant,
335 AutoBall1: stat.AutoBallPickedUp[0],
336 AutoBall2: stat.AutoBallPickedUp[1],
337 AutoBall3: stat.AutoBallPickedUp[2],
338 AutoBall4: stat.AutoBallPickedUp[3],
339 AutoBall5: stat.AutoBallPickedUp[4],
340 MissedShotsAuto: stat.ShotsMissedAuto,
341 UpperGoalAuto: stat.UpperGoalAuto,
342 LowerGoalAuto: stat.LowerGoalAuto,
343 MissedShotsTele: stat.ShotsMissed,
344 UpperGoalTele: stat.UpperGoalShots,
345 LowerGoalTele: stat.LowerGoalShots,
346 DefenseRating: stat.PlayedDefense,
Philipp Schrader36df73a2022-03-17 23:27:24 -0700347 ClimbLevel: request_data_scouting_response.ClimbLevel(stat.Climbing),
Philipp Schraderfee07e12022-03-17 22:19:47 -0700348 CollectedBy: stat.CollectedBy,
Philipp Schraderacf96232022-03-01 22:03:30 -0800349 })
350 }
351
352 builder := flatbuffers.NewBuilder(50 * 1024)
353 builder.Finish((&response).Pack(builder))
354 w.Write(builder.FinishedBytes())
355}
356
Philipp Schraderd3fac192022-03-02 20:35:46 -0800357// TODO(phil): Can we turn this into a generic?
358func parseRefreshMatchList(w http.ResponseWriter, buf []byte) (*RefreshMatchList, bool) {
359 success := true
360 defer func() {
361 if r := recover(); r != nil {
362 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse RefreshMatchList: %v", r))
363 success = false
364 }
365 }()
366 result := refresh_match_list.GetRootAsRefreshMatchList(buf, 0)
367 return result, success
368}
369
370func parseTeamKey(teamKey string) (int, error) {
371 // TBA prefixes teams with "frc". Not sure why. Get rid of that.
372 teamKey = strings.TrimPrefix(teamKey, "frc")
373 return strconv.Atoi(teamKey)
374}
375
376// Parses the alliance data from the specified match and returns the three red
377// teams and the three blue teams.
378func parseTeamKeys(match *scraping.Match) ([3]int32, [3]int32, error) {
379 redKeys := match.Alliances.Red.TeamKeys
380 blueKeys := match.Alliances.Blue.TeamKeys
381
382 if len(redKeys) != 3 || len(blueKeys) != 3 {
383 return [3]int32{}, [3]int32{}, errors.New(fmt.Sprintf(
384 "Found %d red teams and %d blue teams.", len(redKeys), len(blueKeys)))
385 }
386
387 var red [3]int32
388 for i, key := range redKeys {
389 team, err := parseTeamKey(key)
390 if err != nil {
391 return [3]int32{}, [3]int32{}, errors.New(fmt.Sprintf(
392 "Failed to parse red %d team '%s' as integer: %v", i+1, key, err))
393 }
394 red[i] = int32(team)
395 }
396 var blue [3]int32
397 for i, key := range blueKeys {
398 team, err := parseTeamKey(key)
399 if err != nil {
400 return [3]int32{}, [3]int32{}, errors.New(fmt.Sprintf(
401 "Failed to parse blue %d team '%s' as integer: %v", i+1, key, err))
402 }
403 blue[i] = int32(team)
404 }
405 return red, blue, nil
406}
407
408type refreshMatchListHandler struct {
409 db Database
410 scrape ScrapeMatchList
411}
412
413func (handler refreshMatchListHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
414 requestBytes, err := io.ReadAll(req.Body)
415 if err != nil {
416 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
417 return
418 }
419
420 request, success := parseRefreshMatchList(w, requestBytes)
421 if !success {
422 return
423 }
424
425 matches, err := handler.scrape(request.Year(), string(request.EventCode()))
426 if err != nil {
427 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to scrape match list: ", err))
428 return
429 }
430
431 for _, match := range matches {
432 // Make sure the data is valid.
433 red, blue, err := parseTeamKeys(&match)
434 if err != nil {
435 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf(
436 "TheBlueAlliance data for match %d is malformed: %v", match.MatchNumber, err))
437 return
438 }
439 // Add the match to the database.
Philipp Schrader7365d322022-03-06 16:40:08 -0800440 err = handler.db.AddToMatch(db.Match{
Philipp Schraderd3fac192022-03-02 20:35:46 -0800441 MatchNumber: int32(match.MatchNumber),
442 // TODO(phil): What does Round mean?
443 Round: 1,
444 CompLevel: match.CompLevel,
445 R1: red[0],
446 R2: red[1],
447 R3: red[2],
448 B1: blue[0],
449 B2: blue[1],
450 B3: blue[2],
451 })
Philipp Schrader7365d322022-03-06 16:40:08 -0800452 if err != nil {
453 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf(
454 "Failed to add match %d to the database: %v", match.MatchNumber, err))
455 return
456 }
Philipp Schraderd3fac192022-03-02 20:35:46 -0800457 }
458
459 var response RefreshMatchListResponseT
460 builder := flatbuffers.NewBuilder(1024)
461 builder.Finish((&response).Pack(builder))
462 w.Write(builder.FinishedBytes())
463}
464
Alex Perry81f96ba2022-03-13 18:26:19 -0700465func parseSubmitNotes(w http.ResponseWriter, buf []byte) (*SubmitNotes, bool) {
466 success := true
467 defer func() {
468 if r := recover(); r != nil {
469 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse RefreshMatchList: %v", r))
470 success = false
471 }
472 }()
473 result := submit_notes.GetRootAsSubmitNotes(buf, 0)
474 return result, success
475}
476
477type submitNoteScoutingHandler struct {
478 db Database
479}
480
481func (handler submitNoteScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
482 requestBytes, err := io.ReadAll(req.Body)
483 if err != nil {
484 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
485 return
486 }
487
488 request, success := parseSubmitNotes(w, requestBytes)
489 if !success {
490 return
491 }
492
493 err = handler.db.AddNotes(db.NotesData{
494 TeamNumber: request.Team(),
495 Notes: []string{string(request.Notes())},
496 })
497 if err != nil {
498 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to insert notes: %v", err))
499 return
500 }
501
502 var response SubmitNotesResponseT
503 builder := flatbuffers.NewBuilder(10)
504 builder.Finish((&response).Pack(builder))
505 w.Write(builder.FinishedBytes())
506}
507
508func parseRequestNotesForTeam(w http.ResponseWriter, buf []byte) (*RequestNotesForTeam, bool) {
509 success := true
510 defer func() {
511 if r := recover(); r != nil {
512 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse RefreshMatchList: %v", r))
513 success = false
514 }
515 }()
516 result := request_notes_for_team.GetRootAsRequestNotesForTeam(buf, 0)
517 return result, success
518}
519
520type requestNotesForTeamHandler struct {
521 db Database
522}
523
524func (handler requestNotesForTeamHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
525 requestBytes, err := io.ReadAll(req.Body)
526 if err != nil {
527 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
528 return
529 }
530
531 request, success := parseRequestNotesForTeam(w, requestBytes)
532 if !success {
533 return
534 }
535
536 notesData, err := handler.db.QueryNotes(request.Team())
537 if err != nil {
538 respondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query notes: %v", err))
539 return
540 }
541
542 var response RequestNotesForTeamResponseT
543 for _, data := range notesData.Notes {
544 response.Notes = append(response.Notes, &request_notes_for_team_response.NoteT{data})
545 }
546
547 builder := flatbuffers.NewBuilder(1024)
548 builder.Finish((&response).Pack(builder))
549 w.Write(builder.FinishedBytes())
550}
551
Philipp Schraderd3fac192022-03-02 20:35:46 -0800552func HandleRequests(db Database, scrape ScrapeMatchList, scoutingServer server.ScoutingServer) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800553 scoutingServer.HandleFunc("/requests", unknown)
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800554 scoutingServer.Handle("/requests/submit/data_scouting", submitDataScoutingHandler{db})
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800555 scoutingServer.Handle("/requests/request/all_matches", requestAllMatchesHandler{db})
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800556 scoutingServer.Handle("/requests/request/matches_for_team", requestMatchesForTeamHandler{db})
Philipp Schraderacf96232022-03-01 22:03:30 -0800557 scoutingServer.Handle("/requests/request/data_scouting", requestDataScoutingHandler{db})
Philipp Schraderd3fac192022-03-02 20:35:46 -0800558 scoutingServer.Handle("/requests/refresh_match_list", refreshMatchListHandler{db, scrape})
Alex Perry81f96ba2022-03-13 18:26:19 -0700559 scoutingServer.Handle("/requests/submit/submit_notes", submitNoteScoutingHandler{db})
560 scoutingServer.Handle("/requests/request/notes_for_team", requestNotesForTeamHandler{db})
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800561}