blob: f005f0d8ac3547c58bdfbf053af4c8c6811d7581 [file] [log] [blame]
Philipp Schraderd9096a32022-02-24 17:53:09 -08001package debug
2
3import (
4 "bytes"
5 "errors"
6 "fmt"
7 "io"
8 "log"
9 "net/http"
10
11 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
Philipp Schraderd3fac192022-03-02 20:35:46 -080012 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/refresh_match_list_response"
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080013 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
Philipp Schraderacf96232022-03-01 22:03:30 -080014 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_response"
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080015 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070016 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_notes_for_team_response"
Philipp Schraderd9096a32022-02-24 17:53:09 -080017 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
Alex Perry81f96ba2022-03-13 18:26:19 -070018 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_notes_response"
Philipp Schraderd9096a32022-02-24 17:53:09 -080019)
20
21// Use aliases to make the rest of the code more readable.
22type SubmitDataScoutingResponseT = submit_data_scouting_response.SubmitDataScoutingResponseT
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080023type RequestAllMatchesResponseT = request_all_matches_response.RequestAllMatchesResponseT
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080024type RequestMatchesForTeamResponseT = request_matches_for_team_response.RequestMatchesForTeamResponseT
Philipp Schraderacf96232022-03-01 22:03:30 -080025type RequestDataScoutingResponseT = request_data_scouting_response.RequestDataScoutingResponseT
Philipp Schraderd3fac192022-03-02 20:35:46 -080026type RefreshMatchListResponseT = refresh_match_list_response.RefreshMatchListResponseT
Philipp Schraderd9096a32022-02-24 17:53:09 -080027
28// A struct that can be used as an `error`. It contains information about the
29// why the server was unhappy and what the corresponding request was.
30type ResponseError struct {
31 Url string
32 StatusCode int
33 ErrorResponse *error_response.ErrorResponse
34}
35
36// Required to implement the `error` interface.
37func (err *ResponseError) Error() string {
38 return fmt.Sprintf(
39 "%s returned %d %s: %s", err.Url, err.StatusCode,
40 http.StatusText(err.StatusCode), err.ErrorResponse.ErrorMessage())
41}
42
43// Parse an `ErrorResponse` message that the server sent back. This happens
44// whenever the status code is something other than 200. If the message is
45// successfully parsed, it's turned into a `ResponseError` which implements the
46// `error` interface.
47func parseErrorResponse(url string, statusCode int, responseBytes []byte) error {
48 getRootErrMessage := ""
49 defer func() {
50 if r := recover(); r != nil {
51 getRootErrMessage = fmt.Sprintf("%v", r)
52 }
53 }()
54 errorMessage := error_response.GetRootAsErrorResponse(responseBytes, 0)
55 if getRootErrMessage != "" {
56 return errors.New(fmt.Sprintf(
57 "Failed to parse response from %s with status %d %s (bytes %v) as ErrorResponse: %s",
58 url, statusCode, http.StatusText(statusCode), responseBytes, getRootErrMessage))
59 }
60
61 return &ResponseError{
62 Url: url,
63 StatusCode: statusCode,
64 ErrorResponse: errorMessage,
65 }
66}
67
68// Performs a POST request with the specified payload. The bytes that the
69// server responds with are returned.
70func performPost(url string, requestBytes []byte) ([]byte, error) {
71 resp, err := http.Post(url, "application/octet-stream", bytes.NewReader(requestBytes))
72 if err != nil {
73 log.Printf("Failed to send POST request to %s: %v", url, err)
74 return nil, err
75 }
76 responseBytes, err := io.ReadAll(resp.Body)
77 if err != nil {
78 log.Printf("Failed to parse response bytes from POST to %s: %v", url, err)
79 return nil, err
80 }
81 if resp.StatusCode != http.StatusOK {
82 return nil, parseErrorResponse(url, resp.StatusCode, responseBytes)
83 }
84 return responseBytes, nil
85}
86
87// Sends a `SubmitDataScouting` message to the server and returns the
88// deserialized response.
89func SubmitDataScouting(server string, requestBytes []byte) (*SubmitDataScoutingResponseT, error) {
90 responseBytes, err := performPost(server+"/requests/submit/data_scouting", requestBytes)
91 if err != nil {
92 return nil, err
93 }
94 log.Printf("Parsing SubmitDataScoutingResponse")
95 response := submit_data_scouting_response.GetRootAsSubmitDataScoutingResponse(responseBytes, 0)
96 return response.UnPack(), nil
97}
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080098
99// Sends a `RequestAllMatches` message to the server and returns the
100// deserialized response.
101func RequestAllMatches(server string, requestBytes []byte) (*RequestAllMatchesResponseT, error) {
102 responseBytes, err := performPost(server+"/requests/request/all_matches", requestBytes)
103 if err != nil {
104 return nil, err
105 }
106 log.Printf("Parsing RequestAllMatchesResponse")
107 response := request_all_matches_response.GetRootAsRequestAllMatchesResponse(responseBytes, 0)
108 return response.UnPack(), nil
109}
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800110
111// Sends a `RequestMatchesForTeam` message to the server and returns the
112// deserialized response.
113func RequestMatchesForTeam(server string, requestBytes []byte) (*RequestMatchesForTeamResponseT, error) {
114 responseBytes, err := performPost(server+"/requests/request/matches_for_team", requestBytes)
115 if err != nil {
116 return nil, err
117 }
118 log.Printf("Parsing RequestMatchesForTeamResponse")
119 response := request_matches_for_team_response.GetRootAsRequestMatchesForTeamResponse(responseBytes, 0)
120 return response.UnPack(), nil
121}
Philipp Schraderacf96232022-03-01 22:03:30 -0800122
123// Sends a `RequestDataScouting` message to the server and returns the
124// deserialized response.
125func RequestDataScouting(server string, requestBytes []byte) (*RequestDataScoutingResponseT, error) {
126 responseBytes, err := performPost(server+"/requests/request/data_scouting", requestBytes)
127 if err != nil {
128 return nil, err
129 }
130 log.Printf("Parsing RequestDataScoutingResponse")
131 response := request_data_scouting_response.GetRootAsRequestDataScoutingResponse(responseBytes, 0)
132 return response.UnPack(), nil
133}
Philipp Schraderd3fac192022-03-02 20:35:46 -0800134
135// Sends a `RefreshMatchList` message to the server and returns the
136// deserialized response.
137func RefreshMatchList(server string, requestBytes []byte) (*RefreshMatchListResponseT, error) {
138 responseBytes, err := performPost(server+"/requests/refresh_match_list", requestBytes)
139 if err != nil {
140 return nil, err
141 }
142 log.Printf("Parsing RefreshMatchListResponse")
143 response := refresh_match_list_response.GetRootAsRefreshMatchListResponse(responseBytes, 0)
144 return response.UnPack(), nil
145}
Alex Perry81f96ba2022-03-13 18:26:19 -0700146
147func SubmitNotes(server string, requestBytes []byte) (*submit_notes_response.SubmitNotesResponseT, error) {
148 responseBytes, err := performPost(server+"/requests/submit/submit_notes", requestBytes)
149 if err != nil {
150 return nil, err
151 }
152
153 response := submit_notes_response.GetRootAsSubmitNotesResponse(responseBytes, 0)
154 return response.UnPack(), nil
155}
156
157func RequestNotes(server string, requestBytes []byte) (*request_notes_for_team_response.RequestNotesForTeamResponseT, error) {
158 responseBytes, err := performPost(server+"/requests/request/notes_for_team", requestBytes)
159 if err != nil {
160 return nil, err
161 }
162
163 response := request_notes_for_team_response.GetRootAsRequestNotesForTeamResponse(responseBytes, 0)
164 return response.UnPack(), nil
165}