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