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