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