scouting: Add a debug CLI for the webserver
This CLI should let us debug the webserver easily. It simulates calls
that the web page can make. Create a JSON version of the flatbuffer
message you want to send and pass it as a file to the correct option
on the `cli` binary. There's nothing really implemented yet because
the webserver doesn't have a whole lot implemented either.
Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
Change-Id: If396dd8dc3b1e24515cb2d5765b3d5f233066cda
diff --git a/scouting/webserver/requests/debug/debug.go b/scouting/webserver/requests/debug/debug.go
new file mode 100644
index 0000000..dd70c1b
--- /dev/null
+++ b/scouting/webserver/requests/debug/debug.go
@@ -0,0 +1,87 @@
+package debug
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+
+ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
+ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
+)
+
+// Use aliases to make the rest of the code more readable.
+type SubmitDataScoutingResponseT = submit_data_scouting_response.SubmitDataScoutingResponseT
+
+// A struct that can be used as an `error`. It contains information about the
+// why the server was unhappy and what the corresponding request was.
+type ResponseError struct {
+ Url string
+ StatusCode int
+ ErrorResponse *error_response.ErrorResponse
+}
+
+// Required to implement the `error` interface.
+func (err *ResponseError) Error() string {
+ return fmt.Sprintf(
+ "%s returned %d %s: %s", err.Url, err.StatusCode,
+ http.StatusText(err.StatusCode), err.ErrorResponse.ErrorMessage())
+}
+
+// Parse an `ErrorResponse` message that the server sent back. This happens
+// whenever the status code is something other than 200. If the message is
+// successfully parsed, it's turned into a `ResponseError` which implements the
+// `error` interface.
+func parseErrorResponse(url string, statusCode int, responseBytes []byte) error {
+ getRootErrMessage := ""
+ defer func() {
+ if r := recover(); r != nil {
+ getRootErrMessage = fmt.Sprintf("%v", r)
+ }
+ }()
+ errorMessage := error_response.GetRootAsErrorResponse(responseBytes, 0)
+ if getRootErrMessage != "" {
+ return errors.New(fmt.Sprintf(
+ "Failed to parse response from %s with status %d %s (bytes %v) as ErrorResponse: %s",
+ url, statusCode, http.StatusText(statusCode), responseBytes, getRootErrMessage))
+ }
+
+ return &ResponseError{
+ Url: url,
+ StatusCode: statusCode,
+ ErrorResponse: errorMessage,
+ }
+}
+
+// Performs a POST request with the specified payload. The bytes that the
+// server responds with are returned.
+func performPost(url string, requestBytes []byte) ([]byte, error) {
+ resp, err := http.Post(url, "application/octet-stream", bytes.NewReader(requestBytes))
+ if err != nil {
+ log.Printf("Failed to send POST request to %s: %v", url, err)
+ return nil, err
+ }
+ responseBytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ log.Printf("Failed to parse response bytes from POST to %s: %v", url, err)
+ return nil, err
+ }
+ if resp.StatusCode != http.StatusOK {
+ return nil, parseErrorResponse(url, resp.StatusCode, responseBytes)
+ }
+ return responseBytes, nil
+}
+
+// Sends a `SubmitDataScouting` message to the server and returns the
+// deserialized response.
+func SubmitDataScouting(server string, requestBytes []byte) (*SubmitDataScoutingResponseT, error) {
+ responseBytes, err := performPost(server+"/requests/submit/data_scouting", requestBytes)
+ if err != nil {
+ return nil, err
+ }
+ log.Printf("Parsing SubmitDataScoutingResponse")
+ response := submit_data_scouting_response.GetRootAsSubmitDataScoutingResponse(responseBytes, 0)
+ return response.UnPack(), nil
+}