blob: 44c0b60c91499f3eb6bda5b4d692e1ebd36067d9 [file] [log] [blame]
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08001package requests
2
3import (
4 "fmt"
5 "io"
6 "net/http"
7
Philipp Schrader8747f1b2022-02-23 23:56:22 -08008 "github.com/frc971/971-Robot-Code/scouting/db"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08009 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080010 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches"
11 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080012 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team"
13 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080014 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting"
15 _ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
16 "github.com/frc971/971-Robot-Code/scouting/webserver/server"
17 flatbuffers "github.com/google/flatbuffers/go"
18)
19
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080020type SubmitDataScouting = submit_data_scouting.SubmitDataScouting
21type RequestAllMatches = request_all_matches.RequestAllMatches
22type RequestAllMatchesResponseT = request_all_matches_response.RequestAllMatchesResponseT
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080023type RequestMatchesForTeam = request_matches_for_team.RequestMatchesForTeam
24type RequestMatchesForTeamResponseT = request_matches_for_team_response.RequestMatchesForTeamResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080025
Philipp Schrader8747f1b2022-02-23 23:56:22 -080026// The interface we expect the database abstraction to conform to.
27// We use an interface here because it makes unit testing easier.
28type Database interface {
29 AddToMatch(db.Match) error
30 AddToStats(db.Stats) error
31 ReturnMatches() ([]db.Match, error)
32 ReturnStats() ([]db.Stats, error)
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080033 QueryMatches(int32) ([]db.Match, error)
Philipp Schrader8747f1b2022-02-23 23:56:22 -080034 QueryStats(int) ([]db.Stats, error)
35}
36
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080037// Handles unknown requests. Just returns a 404.
38func unknown(w http.ResponseWriter, req *http.Request) {
39 w.WriteHeader(http.StatusNotFound)
40}
41
42func respondWithError(w http.ResponseWriter, statusCode int, errorMessage string) {
43 builder := flatbuffers.NewBuilder(1024)
44 builder.Finish((&error_response.ErrorResponseT{
45 ErrorMessage: errorMessage,
46 }).Pack(builder))
47 w.WriteHeader(statusCode)
48 w.Write(builder.FinishedBytes())
49}
50
51func respondNotImplemented(w http.ResponseWriter) {
52 respondWithError(w, http.StatusNotImplemented, "")
53}
54
55// TODO(phil): Can we turn this into a generic?
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080056func parseSubmitDataScouting(w http.ResponseWriter, buf []byte) (*SubmitDataScouting, bool) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080057 success := true
58 defer func() {
59 if r := recover(); r != nil {
60 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
61 success = false
62 }
63 }()
64 result := submit_data_scouting.GetRootAsSubmitDataScouting(buf, 0)
65 return result, success
66}
67
68// Handles a SubmitDataScouting request.
Philipp Schrader8747f1b2022-02-23 23:56:22 -080069type submitDataScoutingHandler struct {
70 db Database
71}
72
73func (handler submitDataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080074 requestBytes, err := io.ReadAll(req.Body)
75 if err != nil {
76 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
77 return
78 }
79
80 _, success := parseSubmitDataScouting(w, requestBytes)
81 if !success {
82 return
83 }
84
85 // TODO(phil): Actually handle the request.
Philipp Schrader8747f1b2022-02-23 23:56:22 -080086 // We have access to the database via "handler.db" here. For example:
87 // stats := handler.db.ReturnStats()
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080088
89 respondNotImplemented(w)
90}
91
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080092// TODO(phil): Can we turn this into a generic?
93func parseRequestAllMatches(w http.ResponseWriter, buf []byte) (*RequestAllMatches, bool) {
94 success := true
95 defer func() {
96 if r := recover(); r != nil {
97 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
98 success = false
99 }
100 }()
101 result := request_all_matches.GetRootAsRequestAllMatches(buf, 0)
102 return result, success
103}
104
105// Handles a RequestAllMaches request.
106type requestAllMatchesHandler struct {
107 db Database
108}
109
110func (handler requestAllMatchesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
111 requestBytes, err := io.ReadAll(req.Body)
112 if err != nil {
113 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
114 return
115 }
116
117 _, success := parseRequestAllMatches(w, requestBytes)
118 if !success {
119 return
120 }
121
122 matches, err := handler.db.ReturnMatches()
123 if err != nil {
124 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to query database: ", err))
125 }
126
127 var response RequestAllMatchesResponseT
128 for _, match := range matches {
129 response.MatchList = append(response.MatchList, &request_all_matches_response.MatchT{
130 MatchNumber: match.MatchNumber,
131 Round: match.Round,
132 CompLevel: match.CompLevel,
133 R1: match.R1,
134 R2: match.R2,
135 R3: match.R3,
136 B1: match.B1,
137 B2: match.B2,
138 B3: match.B3,
139 })
140 }
141
142 builder := flatbuffers.NewBuilder(50 * 1024)
143 builder.Finish((&response).Pack(builder))
144 w.Write(builder.FinishedBytes())
145}
146
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800147// TODO(phil): Can we turn this into a generic?
148func parseRequestMatchesForTeam(w http.ResponseWriter, buf []byte) (*RequestMatchesForTeam, bool) {
149 success := true
150 defer func() {
151 if r := recover(); r != nil {
152 respondWithError(w, http.StatusBadRequest, fmt.Sprintf("Failed to parse SubmitDataScouting: %v", r))
153 success = false
154 }
155 }()
156 result := request_matches_for_team.GetRootAsRequestMatchesForTeam(buf, 0)
157 return result, success
158}
159
160// Handles a RequestMatchesForTeam request.
161type requestMatchesForTeamHandler struct {
162 db Database
163}
164
165func (handler requestMatchesForTeamHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
166 requestBytes, err := io.ReadAll(req.Body)
167 if err != nil {
168 respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
169 return
170 }
171
172 request, success := parseRequestMatchesForTeam(w, requestBytes)
173 if !success {
174 return
175 }
176
177 matches, err := handler.db.QueryMatches(request.Team())
178 if err != nil {
179 respondWithError(w, http.StatusInternalServerError, fmt.Sprint("Faled to query database: ", err))
180 }
181
182 var response RequestAllMatchesResponseT
183 for _, match := range matches {
184 response.MatchList = append(response.MatchList, &request_all_matches_response.MatchT{
185 MatchNumber: match.MatchNumber,
186 Round: match.Round,
187 CompLevel: match.CompLevel,
188 R1: match.R1,
189 R2: match.R2,
190 R3: match.R3,
191 B1: match.B1,
192 B2: match.B2,
193 B3: match.B3,
194 })
195 }
196
197 builder := flatbuffers.NewBuilder(50 * 1024)
198 builder.Finish((&response).Pack(builder))
199 w.Write(builder.FinishedBytes())
200}
201
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800202func HandleRequests(db Database, scoutingServer server.ScoutingServer) {
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800203 scoutingServer.HandleFunc("/requests", unknown)
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800204 scoutingServer.Handle("/requests/submit/data_scouting", submitDataScoutingHandler{db})
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800205 scoutingServer.Handle("/requests/request/all_matches", requestAllMatchesHandler{db})
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800206 scoutingServer.Handle("/requests/request/matches_for_team", requestMatchesForTeamHandler{db})
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800207}