blob: acd8b04056dd09698d088fad7b103e9441343456 [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"
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080014 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
Philipp Schraderacf96232022-03-01 22:03:30 -080015 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_response"
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080016 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070017 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
Philipp Schraderd9096a32022-02-24 17:53:09 -080018 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070019 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
Philipp Schraderd9096a32022-02-24 17:53:09 -080020)
21
Philipp Schraderfae8a7e2022-03-13 22:51:54 -070022// The username to submit the various requests as.
23const DefaultUsername = "debug_cli"
24
Philipp Schraderd9096a32022-02-24 17:53:09 -080025// Use aliases to make the rest of the code more readable.
26type SubmitDataScoutingResponseT = submit_data_scouting_response.SubmitDataScoutingResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080027type RequestAllMatchesResponseT = request_all_matches_response.RequestAllMatchesResponseT
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080028type RequestMatchesForTeamResponseT = request_matches_for_team_response.RequestMatchesForTeamResponseT
Philipp Schraderacf96232022-03-01 22:03:30 -080029type RequestDataScoutingResponseT = request_data_scouting_response.RequestDataScoutingResponseT
Philipp Schraderd3fac192022-03-02 20:35:46 -080030type RefreshMatchListResponseT = refresh_match_list_response.RefreshMatchListResponseT
Philipp Schraderd9096a32022-02-24 17:53:09 -080031
32// A struct that can be used as an `error`. It contains information about the
33// why the server was unhappy and what the corresponding request was.
34type ResponseError struct {
35 Url string
36 StatusCode int
37 ErrorResponse *error_response.ErrorResponse
38}
39
40// Required to implement the `error` interface.
41func (err *ResponseError) Error() string {
42 return fmt.Sprintf(
43 "%s returned %d %s: %s", err.Url, err.StatusCode,
44 http.StatusText(err.StatusCode), err.ErrorResponse.ErrorMessage())
45}
46
47// Parse an `ErrorResponse` message that the server sent back. This happens
48// whenever the status code is something other than 200. If the message is
49// successfully parsed, it's turned into a `ResponseError` which implements the
50// `error` interface.
51func parseErrorResponse(url string, statusCode int, responseBytes []byte) error {
52 getRootErrMessage := ""
53 defer func() {
54 if r := recover(); r != nil {
55 getRootErrMessage = fmt.Sprintf("%v", r)
56 }
57 }()
58 errorMessage := error_response.GetRootAsErrorResponse(responseBytes, 0)
59 if getRootErrMessage != "" {
60 return errors.New(fmt.Sprintf(
61 "Failed to parse response from %s with status %d %s (bytes %v) as ErrorResponse: %s",
62 url, statusCode, http.StatusText(statusCode), responseBytes, getRootErrMessage))
63 }
64
65 return &ResponseError{
66 Url: url,
67 StatusCode: statusCode,
68 ErrorResponse: errorMessage,
69 }
70}
71
72// Performs a POST request with the specified payload. The bytes that the
73// server responds with are returned.
74func performPost(url string, requestBytes []byte) ([]byte, error) {
Philipp Schraderfae8a7e2022-03-13 22:51:54 -070075 req, err := http.NewRequest("POST", url, bytes.NewReader(requestBytes))
76 if err != nil {
77 log.Printf("Failed to create a new POST request to %s: %v", url, err)
78 return nil, err
79 }
80 req.Header.Add("Authorization", "Basic "+
81 base64.StdEncoding.EncodeToString([]byte(DefaultUsername+":")))
82
83 client := &http.Client{}
84 resp, err := client.Do(req)
Philipp Schraderd9096a32022-02-24 17:53:09 -080085 if err != nil {
86 log.Printf("Failed to send POST request to %s: %v", url, err)
87 return nil, err
88 }
89 responseBytes, err := io.ReadAll(resp.Body)
90 if err != nil {
91 log.Printf("Failed to parse response bytes from POST to %s: %v", url, err)
92 return nil, err
93 }
94 if resp.StatusCode != http.StatusOK {
95 return nil, parseErrorResponse(url, resp.StatusCode, responseBytes)
96 }
97 return responseBytes, nil
98}
99
100// Sends a `SubmitDataScouting` message to the server and returns the
101// deserialized response.
102func SubmitDataScouting(server string, requestBytes []byte) (*SubmitDataScoutingResponseT, error) {
103 responseBytes, err := performPost(server+"/requests/submit/data_scouting", requestBytes)
104 if err != nil {
105 return nil, err
106 }
107 log.Printf("Parsing SubmitDataScoutingResponse")
108 response := submit_data_scouting_response.GetRootAsSubmitDataScoutingResponse(responseBytes, 0)
109 return response.UnPack(), nil
110}
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800111
112// Sends a `RequestAllMatches` message to the server and returns the
113// deserialized response.
114func RequestAllMatches(server string, requestBytes []byte) (*RequestAllMatchesResponseT, error) {
115 responseBytes, err := performPost(server+"/requests/request/all_matches", requestBytes)
116 if err != nil {
117 return nil, err
118 }
119 log.Printf("Parsing RequestAllMatchesResponse")
120 response := request_all_matches_response.GetRootAsRequestAllMatchesResponse(responseBytes, 0)
121 return response.UnPack(), nil
122}
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800123
124// Sends a `RequestMatchesForTeam` message to the server and returns the
125// deserialized response.
126func RequestMatchesForTeam(server string, requestBytes []byte) (*RequestMatchesForTeamResponseT, error) {
127 responseBytes, err := performPost(server+"/requests/request/matches_for_team", requestBytes)
128 if err != nil {
129 return nil, err
130 }
131 log.Printf("Parsing RequestMatchesForTeamResponse")
132 response := request_matches_for_team_response.GetRootAsRequestMatchesForTeamResponse(responseBytes, 0)
133 return response.UnPack(), nil
134}
Philipp Schraderacf96232022-03-01 22:03:30 -0800135
136// Sends a `RequestDataScouting` message to the server and returns the
137// deserialized response.
138func RequestDataScouting(server string, requestBytes []byte) (*RequestDataScoutingResponseT, error) {
139 responseBytes, err := performPost(server+"/requests/request/data_scouting", requestBytes)
140 if err != nil {
141 return nil, err
142 }
143 log.Printf("Parsing RequestDataScoutingResponse")
144 response := request_data_scouting_response.GetRootAsRequestDataScoutingResponse(responseBytes, 0)
145 return response.UnPack(), nil
146}
Philipp Schraderd3fac192022-03-02 20:35:46 -0800147
148// Sends a `RefreshMatchList` message to the server and returns the
149// deserialized response.
150func RefreshMatchList(server string, requestBytes []byte) (*RefreshMatchListResponseT, error) {
151 responseBytes, err := performPost(server+"/requests/refresh_match_list", requestBytes)
152 if err != nil {
153 return nil, err
154 }
155 log.Printf("Parsing RefreshMatchListResponse")
156 response := refresh_match_list_response.GetRootAsRefreshMatchListResponse(responseBytes, 0)
157 return response.UnPack(), nil
158}
Alex Perry81f96ba2022-03-13 18:26:19 -0700159
160func SubmitNotes(server string, requestBytes []byte) (*submit_notes_response.SubmitNotesResponseT, error) {
161 responseBytes, err := performPost(server+"/requests/submit/submit_notes", requestBytes)
162 if err != nil {
163 return nil, err
164 }
165
166 response := submit_notes_response.GetRootAsSubmitNotesResponse(responseBytes, 0)
167 return response.UnPack(), nil
168}
169
170func RequestNotes(server string, requestBytes []byte) (*request_notes_for_team_response.RequestNotesForTeamResponseT, error) {
171 responseBytes, err := performPost(server+"/requests/request/notes_for_team", requestBytes)
172 if err != nil {
173 return nil, err
174 }
175
176 response := request_notes_for_team_response.GetRootAsRequestNotesForTeamResponse(responseBytes, 0)
177 return response.UnPack(), nil
178}