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