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