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