blob: acb9dd4f938cb449606e15b43a24a7ead01f2322 [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"
Emily Markova290147d2023-03-03 22:40:06 -080013 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_2023_data_scouting_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"
Alex Perry81f96ba2022-03-13 18:26:19 -070017 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
Milo Lin1d59f0c2022-06-22 20:30:58 -070018 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_shift_schedule_response"
Sabina Leaver9b4eb312023-02-20 19:58:17 -080019 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_actions_response"
Filip Kujawa210a03b2022-11-24 14:41:11 -080020 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_driver_ranking_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070021 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
Milo Lin1d59f0c2022-06-22 20:30:58 -070022 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_shift_schedule_response"
Philipp Schrader86bf2b92022-03-30 20:57:30 -070023 flatbuffers "github.com/google/flatbuffers/go"
Philipp Schraderd9096a32022-02-24 17:53:09 -080024)
25
Philipp Schraderfae8a7e2022-03-13 22:51:54 -070026// The username to submit the various requests as.
27const DefaultUsername = "debug_cli"
28
Philipp Schraderd9096a32022-02-24 17:53:09 -080029// A struct that can be used as an `error`. It contains information about the
30// why the server was unhappy and what the corresponding request was.
31type ResponseError struct {
32 Url string
33 StatusCode int
34 ErrorResponse *error_response.ErrorResponse
35}
36
37// Required to implement the `error` interface.
38func (err *ResponseError) Error() string {
39 return fmt.Sprintf(
40 "%s returned %d %s: %s", err.Url, err.StatusCode,
41 http.StatusText(err.StatusCode), err.ErrorResponse.ErrorMessage())
42}
43
44// Parse an `ErrorResponse` message that the server sent back. This happens
45// whenever the status code is something other than 200. If the message is
46// successfully parsed, it's turned into a `ResponseError` which implements the
47// `error` interface.
48func parseErrorResponse(url string, statusCode int, responseBytes []byte) error {
49 getRootErrMessage := ""
50 defer func() {
51 if r := recover(); r != nil {
52 getRootErrMessage = fmt.Sprintf("%v", r)
53 }
54 }()
55 errorMessage := error_response.GetRootAsErrorResponse(responseBytes, 0)
56 if getRootErrMessage != "" {
57 return errors.New(fmt.Sprintf(
58 "Failed to parse response from %s with status %d %s (bytes %v) as ErrorResponse: %s",
59 url, statusCode, http.StatusText(statusCode), responseBytes, getRootErrMessage))
60 }
61
62 return &ResponseError{
63 Url: url,
64 StatusCode: statusCode,
65 ErrorResponse: errorMessage,
66 }
67}
68
69// Performs a POST request with the specified payload. The bytes that the
70// server responds with are returned.
71func performPost(url string, requestBytes []byte) ([]byte, error) {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -070072 req, err := http.NewRequest("POST", url, bytes.NewReader(requestBytes))
73 if err != nil {
74 log.Printf("Failed to create a new POST request to %s: %v", url, err)
75 return nil, err
76 }
77 req.Header.Add("Authorization", "Basic "+
78 base64.StdEncoding.EncodeToString([]byte(DefaultUsername+":")))
79
80 client := &http.Client{}
81 resp, err := client.Do(req)
Philipp Schraderd9096a32022-02-24 17:53:09 -080082 if err != nil {
83 log.Printf("Failed to send POST request to %s: %v", url, err)
84 return nil, err
85 }
86 responseBytes, err := io.ReadAll(resp.Body)
87 if err != nil {
88 log.Printf("Failed to parse response bytes from POST to %s: %v", url, err)
89 return nil, err
90 }
91 if resp.StatusCode != http.StatusOK {
92 return nil, parseErrorResponse(url, resp.StatusCode, responseBytes)
93 }
94 return responseBytes, nil
95}
96
Philipp Schrader86bf2b92022-03-30 20:57:30 -070097// Sends a message to the server and returns the deserialized response.
98// The first generic argument must be specified.
99func sendMessage[FbT interface{}, Fb interface{ UnPack() *FbT }](url string, requestBytes []byte, parser func([]byte, flatbuffers.UOffsetT) Fb) (*FbT, error) {
100 responseBytes, err := performPost(url, requestBytes)
101 if err != nil {
102 return nil, err
103 }
104 response := parser(responseBytes, 0)
105 return response.UnPack(), nil
106}
107
Philipp Schrader20440f82022-03-16 20:07:09 -0700108func RequestAllMatches(server string, requestBytes []byte) (*request_all_matches_response.RequestAllMatchesResponseT, error) {
Philipp Schrader86bf2b92022-03-30 20:57:30 -0700109 return sendMessage[request_all_matches_response.RequestAllMatchesResponseT](
110 server+"/requests/request/all_matches", requestBytes,
111 request_all_matches_response.GetRootAsRequestAllMatchesResponse)
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800112}
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800113
Filip Kujawaf882e022022-12-14 13:14:08 -0800114func RequestAllDriverRankings(server string, requestBytes []byte) (*request_all_driver_rankings_response.RequestAllDriverRankingsResponseT, error) {
115 return sendMessage[request_all_driver_rankings_response.RequestAllDriverRankingsResponseT](
116 server+"/requests/request/all_driver_rankings", requestBytes,
117 request_all_driver_rankings_response.GetRootAsRequestAllDriverRankingsResponse)
118}
119
Emily Markova290147d2023-03-03 22:40:06 -0800120func Request2023DataScouting(server string, requestBytes []byte) (*request_2023_data_scouting_response.Request2023DataScoutingResponseT, error) {
121 return sendMessage[request_2023_data_scouting_response.Request2023DataScoutingResponseT](
122 server+"/requests/request/2023_data_scouting", requestBytes,
123 request_2023_data_scouting_response.GetRootAsRequest2023DataScoutingResponse)
124}
125
Alex Perry81f96ba2022-03-13 18:26:19 -0700126func SubmitNotes(server string, requestBytes []byte) (*submit_notes_response.SubmitNotesResponseT, error) {
Philipp Schrader86bf2b92022-03-30 20:57:30 -0700127 return sendMessage[submit_notes_response.SubmitNotesResponseT](
128 server+"/requests/submit/submit_notes", requestBytes,
129 submit_notes_response.GetRootAsSubmitNotesResponse)
Alex Perry81f96ba2022-03-13 18:26:19 -0700130}
131
132func RequestNotes(server string, requestBytes []byte) (*request_notes_for_team_response.RequestNotesForTeamResponseT, error) {
Philipp Schrader86bf2b92022-03-30 20:57:30 -0700133 return sendMessage[request_notes_for_team_response.RequestNotesForTeamResponseT](
134 server+"/requests/request/notes_for_team", requestBytes,
135 request_notes_for_team_response.GetRootAsRequestNotesForTeamResponse)
Alex Perry81f96ba2022-03-13 18:26:19 -0700136}
Milo Lin1d59f0c2022-06-22 20:30:58 -0700137
Filip Kujawaf882e022022-12-14 13:14:08 -0800138func RequestAllNotes(server string, requestBytes []byte) (*request_all_notes_response.RequestAllNotesResponseT, error) {
139 return sendMessage[request_all_notes_response.RequestAllNotesResponseT](
140 server+"/requests/request/all_notes", requestBytes,
141 request_all_notes_response.GetRootAsRequestAllNotesResponse)
142}
143
Milo Lin1d59f0c2022-06-22 20:30:58 -0700144func RequestShiftSchedule(server string, requestBytes []byte) (*request_shift_schedule_response.RequestShiftScheduleResponseT, error) {
145 return sendMessage[request_shift_schedule_response.RequestShiftScheduleResponseT](
146 server+"/requests/request/shift_schedule", requestBytes,
147 request_shift_schedule_response.GetRootAsRequestShiftScheduleResponse)
148}
149
150func SubmitShiftSchedule(server string, requestBytes []byte) (*submit_shift_schedule_response.SubmitShiftScheduleResponseT, error) {
151 return sendMessage[submit_shift_schedule_response.SubmitShiftScheduleResponseT](
152 server+"/requests/submit/shift_schedule", requestBytes,
153 submit_shift_schedule_response.GetRootAsSubmitShiftScheduleResponse)
154}
Filip Kujawa210a03b2022-11-24 14:41:11 -0800155
156func SubmitDriverRanking(server string, requestBytes []byte) (*submit_driver_ranking_response.SubmitDriverRankingResponseT, error) {
157 return sendMessage[submit_driver_ranking_response.SubmitDriverRankingResponseT](
158 server+"/requests/submit/submit_driver_ranking", requestBytes,
159 submit_driver_ranking_response.GetRootAsSubmitDriverRankingResponse)
160}
Sabina Leaver9b4eb312023-02-20 19:58:17 -0800161
162func SubmitActions(server string, requestBytes []byte) (*submit_actions_response.SubmitActionsResponseT, error) {
163 return sendMessage[submit_actions_response.SubmitActionsResponseT](
164 server+"/requests/submit/submit_actions", requestBytes,
165 submit_actions_response.GetRootAsSubmitActionsResponse)
166}