blob: 08cf67c86bba0312d1e7964b39468de33efba82e [file] [log] [blame]
Philipp Schraderd9096a32022-02-24 17:53:09 -08001package debug
2
3import (
4 "bytes"
Philipp Schraderfae8a7e2022-03-13 22:51:54 -07005 "encoding/base64"
Philipp Schraderd9096a32022-02-24 17:53:09 -08006 "errors"
7 "fmt"
8 "io"
9 "log"
10 "net/http"
11
12 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
Philipp Schraderd3fac192022-03-02 20:35:46 -080013 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/refresh_match_list_response"
Filip Kujawaf882e022022-12-14 13:14:08 -080014 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_driver_rankings_response"
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080015 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
Filip Kujawaf882e022022-12-14 13:14:08 -080016 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_notes_response"
Philipp Schraderacf96232022-03-01 22:03:30 -080017 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_response"
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080018 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070019 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
Milo Lin1d59f0c2022-06-22 20:30:58 -070020 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule_response"
Philipp Schraderd9096a32022-02-24 17:53:09 -080021 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
Filip Kujawa210a03b2022-11-24 14:41:11 -080022 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070023 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
Milo Lin1d59f0c2022-06-22 20:30:58 -070024 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule_response"
Philipp Schrader86bf2b92022-03-30 20:57:30 -070025 flatbuffers "github.com/google/flatbuffers/go"
Philipp Schraderd9096a32022-02-24 17:53:09 -080026)
27
Philipp Schraderfae8a7e2022-03-13 22:51:54 -070028// The username to submit the various requests as.
29const DefaultUsername = "debug_cli"
30
Philipp Schraderd9096a32022-02-24 17:53:09 -080031// A struct that can be used as an `error`. It contains information about the
32// why the server was unhappy and what the corresponding request was.
33type ResponseError struct {
34 Url string
35 StatusCode int
36 ErrorResponse *error_response.ErrorResponse
37}
38
39// Required to implement the `error` interface.
40func (err *ResponseError) Error() string {
41 return fmt.Sprintf(
42 "%s returned %d %s: %s", err.Url, err.StatusCode,
43 http.StatusText(err.StatusCode), err.ErrorResponse.ErrorMessage())
44}
45
46// Parse an `ErrorResponse` message that the server sent back. This happens
47// whenever the status code is something other than 200. If the message is
48// successfully parsed, it's turned into a `ResponseError` which implements the
49// `error` interface.
50func parseErrorResponse(url string, statusCode int, responseBytes []byte) error {
51 getRootErrMessage := ""
52 defer func() {
53 if r := recover(); r != nil {
54 getRootErrMessage = fmt.Sprintf("%v", r)
55 }
56 }()
57 errorMessage := error_response.GetRootAsErrorResponse(responseBytes, 0)
58 if getRootErrMessage != "" {
59 return errors.New(fmt.Sprintf(
60 "Failed to parse response from %s with status %d %s (bytes %v) as ErrorResponse: %s",
61 url, statusCode, http.StatusText(statusCode), responseBytes, getRootErrMessage))
62 }
63
64 return &ResponseError{
65 Url: url,
66 StatusCode: statusCode,
67 ErrorResponse: errorMessage,
68 }
69}
70
71// Performs a POST request with the specified payload. The bytes that the
72// server responds with are returned.
73func performPost(url string, requestBytes []byte) ([]byte, error) {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -070074 req, err := http.NewRequest("POST", url, bytes.NewReader(requestBytes))
75 if err != nil {
76 log.Printf("Failed to create a new POST request to %s: %v", url, err)
77 return nil, err
78 }
79 req.Header.Add("Authorization", "Basic "+
80 base64.StdEncoding.EncodeToString([]byte(DefaultUsername+":")))
81
82 client := &http.Client{}
83 resp, err := client.Do(req)
Philipp Schraderd9096a32022-02-24 17:53:09 -080084 if err != nil {
85 log.Printf("Failed to send POST request to %s: %v", url, err)
86 return nil, err
87 }
88 responseBytes, err := io.ReadAll(resp.Body)
89 if err != nil {
90 log.Printf("Failed to parse response bytes from POST to %s: %v", url, err)
91 return nil, err
92 }
93 if resp.StatusCode != http.StatusOK {
94 return nil, parseErrorResponse(url, resp.StatusCode, responseBytes)
95 }
96 return responseBytes, nil
97}
98
Philipp Schrader86bf2b92022-03-30 20:57:30 -070099// Sends a message to the server and returns the deserialized response.
100// The first generic argument must be specified.
101func sendMessage[FbT interface{}, Fb interface{ UnPack() *FbT }](url string, requestBytes []byte, parser func([]byte, flatbuffers.UOffsetT) Fb) (*FbT, error) {
102 responseBytes, err := performPost(url, requestBytes)
103 if err != nil {
104 return nil, err
105 }
106 response := parser(responseBytes, 0)
107 return response.UnPack(), nil
108}
109
Philipp Schrader20440f82022-03-16 20:07:09 -0700110func SubmitDataScouting(server string, requestBytes []byte) (*submit_data_scouting_response.SubmitDataScoutingResponseT, error) {
Philipp Schrader86bf2b92022-03-30 20:57:30 -0700111 return sendMessage[submit_data_scouting_response.SubmitDataScoutingResponseT](
112 server+"/requests/submit/data_scouting", requestBytes,
113 submit_data_scouting_response.GetRootAsSubmitDataScoutingResponse)
Philipp Schraderd9096a32022-02-24 17:53:09 -0800114}
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800115
Philipp Schrader20440f82022-03-16 20:07:09 -0700116func RequestAllMatches(server string, requestBytes []byte) (*request_all_matches_response.RequestAllMatchesResponseT, error) {
Philipp Schrader86bf2b92022-03-30 20:57:30 -0700117 return sendMessage[request_all_matches_response.RequestAllMatchesResponseT](
118 server+"/requests/request/all_matches", requestBytes,
119 request_all_matches_response.GetRootAsRequestAllMatchesResponse)
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800120}
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800121
Filip Kujawaf882e022022-12-14 13:14:08 -0800122func RequestAllDriverRankings(server string, requestBytes []byte) (*request_all_driver_rankings_response.RequestAllDriverRankingsResponseT, error) {
123 return sendMessage[request_all_driver_rankings_response.RequestAllDriverRankingsResponseT](
124 server+"/requests/request/all_driver_rankings", requestBytes,
125 request_all_driver_rankings_response.GetRootAsRequestAllDriverRankingsResponse)
126}
127
Philipp Schrader20440f82022-03-16 20:07:09 -0700128func RequestMatchesForTeam(server string, requestBytes []byte) (*request_matches_for_team_response.RequestMatchesForTeamResponseT, error) {
Philipp Schrader86bf2b92022-03-30 20:57:30 -0700129 return sendMessage[request_matches_for_team_response.RequestMatchesForTeamResponseT](
130 server+"/requests/request/matches_for_team", requestBytes,
131 request_matches_for_team_response.GetRootAsRequestMatchesForTeamResponse)
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800132}
Philipp Schraderacf96232022-03-01 22:03:30 -0800133
Philipp Schrader20440f82022-03-16 20:07:09 -0700134func RequestDataScouting(server string, requestBytes []byte) (*request_data_scouting_response.RequestDataScoutingResponseT, error) {
Philipp Schrader86bf2b92022-03-30 20:57:30 -0700135 return sendMessage[request_data_scouting_response.RequestDataScoutingResponseT](
136 server+"/requests/request/data_scouting", requestBytes,
137 request_data_scouting_response.GetRootAsRequestDataScoutingResponse)
Philipp Schraderacf96232022-03-01 22:03:30 -0800138}
Philipp Schraderd3fac192022-03-02 20:35:46 -0800139
Philipp Schrader20440f82022-03-16 20:07:09 -0700140func RefreshMatchList(server string, requestBytes []byte) (*refresh_match_list_response.RefreshMatchListResponseT, error) {
Philipp Schrader86bf2b92022-03-30 20:57:30 -0700141 return sendMessage[refresh_match_list_response.RefreshMatchListResponseT](
142 server+"/requests/refresh_match_list", requestBytes,
143 refresh_match_list_response.GetRootAsRefreshMatchListResponse)
Philipp Schraderd3fac192022-03-02 20:35:46 -0800144}
Alex Perry81f96ba2022-03-13 18:26:19 -0700145
146func SubmitNotes(server string, requestBytes []byte) (*submit_notes_response.SubmitNotesResponseT, error) {
Philipp Schrader86bf2b92022-03-30 20:57:30 -0700147 return sendMessage[submit_notes_response.SubmitNotesResponseT](
148 server+"/requests/submit/submit_notes", requestBytes,
149 submit_notes_response.GetRootAsSubmitNotesResponse)
Alex Perry81f96ba2022-03-13 18:26:19 -0700150}
151
152func RequestNotes(server string, requestBytes []byte) (*request_notes_for_team_response.RequestNotesForTeamResponseT, error) {
Philipp Schrader86bf2b92022-03-30 20:57:30 -0700153 return sendMessage[request_notes_for_team_response.RequestNotesForTeamResponseT](
154 server+"/requests/request/notes_for_team", requestBytes,
155 request_notes_for_team_response.GetRootAsRequestNotesForTeamResponse)
Alex Perry81f96ba2022-03-13 18:26:19 -0700156}
Milo Lin1d59f0c2022-06-22 20:30:58 -0700157
Filip Kujawaf882e022022-12-14 13:14:08 -0800158func RequestAllNotes(server string, requestBytes []byte) (*request_all_notes_response.RequestAllNotesResponseT, error) {
159 return sendMessage[request_all_notes_response.RequestAllNotesResponseT](
160 server+"/requests/request/all_notes", requestBytes,
161 request_all_notes_response.GetRootAsRequestAllNotesResponse)
162}
163
Milo Lin1d59f0c2022-06-22 20:30:58 -0700164func RequestShiftSchedule(server string, requestBytes []byte) (*request_shift_schedule_response.RequestShiftScheduleResponseT, error) {
165 return sendMessage[request_shift_schedule_response.RequestShiftScheduleResponseT](
166 server+"/requests/request/shift_schedule", requestBytes,
167 request_shift_schedule_response.GetRootAsRequestShiftScheduleResponse)
168}
169
170func SubmitShiftSchedule(server string, requestBytes []byte) (*submit_shift_schedule_response.SubmitShiftScheduleResponseT, error) {
171 return sendMessage[submit_shift_schedule_response.SubmitShiftScheduleResponseT](
172 server+"/requests/submit/shift_schedule", requestBytes,
173 submit_shift_schedule_response.GetRootAsSubmitShiftScheduleResponse)
174}
Filip Kujawa210a03b2022-11-24 14:41:11 -0800175
176func SubmitDriverRanking(server string, requestBytes []byte) (*submit_driver_ranking_response.SubmitDriverRankingResponseT, error) {
177 return sendMessage[submit_driver_ranking_response.SubmitDriverRankingResponseT](
178 server+"/requests/submit/submit_driver_ranking", requestBytes,
179 submit_driver_ranking_response.GetRootAsSubmitDriverRankingResponse)
180}