scouting: Allow the requests handler access to the database

This patch makes it so the requests handlers actually have access to
the database. In the unit tests we use a mock database, but in the
`webserver` binary we use the real one.

Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
Change-Id: Iaf5b2c039a275dd6ddfbff173aa6ad59cf3988b5
diff --git a/scouting/webserver/BUILD b/scouting/webserver/BUILD
index 7d42bf9..66db8e8 100644
--- a/scouting/webserver/BUILD
+++ b/scouting/webserver/BUILD
@@ -7,6 +7,7 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     visibility = ["//visibility:private"],
     deps = [
+        "//scouting/db",
         "//scouting/webserver/requests",
         "//scouting/webserver/server",
         "//scouting/webserver/static",
diff --git a/scouting/webserver/main.go b/scouting/webserver/main.go
index 2baf36d..668104b 100644
--- a/scouting/webserver/main.go
+++ b/scouting/webserver/main.go
@@ -3,10 +3,12 @@
 import (
 	"flag"
 	"fmt"
+	"log"
 	"os"
 	"os/signal"
 	"syscall"
 
+	"github.com/frc971/971-Robot-Code/scouting/db"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/requests"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/server"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/static"
@@ -17,9 +19,14 @@
 	dirPtr := flag.String("directory", ".", "The directory to serve at /.")
 	flag.Parse()
 
+	database, err := db.NewDatabase()
+	if err != nil {
+		log.Fatal("Failed to connect to database: ", err)
+	}
+
 	scoutingServer := server.NewScoutingServer()
 	static.ServePages(scoutingServer, *dirPtr)
-	requests.HandleRequests(scoutingServer)
+	requests.HandleRequests(database, scoutingServer)
 	scoutingServer.Start(*portPtr)
 	fmt.Println("Serving", *dirPtr, "on port", *portPtr)
 
diff --git a/scouting/webserver/requests/BUILD b/scouting/webserver/requests/BUILD
index 456730e..15e4c05 100644
--- a/scouting/webserver/requests/BUILD
+++ b/scouting/webserver/requests/BUILD
@@ -7,6 +7,7 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     visibility = ["//visibility:public"],
     deps = [
+        "//scouting/db",
         "//scouting/webserver/requests/messages:error_response_go_fbs",
         "//scouting/webserver/requests/messages:submit_data_scouting_go_fbs",
         "//scouting/webserver/requests/messages:submit_data_scouting_response_go_fbs",
@@ -21,6 +22,7 @@
     embed = [":requests"],
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
+        "//scouting/db",
         "//scouting/webserver/requests/messages:error_response_go_fbs",
         "//scouting/webserver/requests/messages:submit_data_scouting_go_fbs",
         "//scouting/webserver/requests/messages:submit_data_scouting_response_go_fbs",
diff --git a/scouting/webserver/requests/requests.go b/scouting/webserver/requests/requests.go
index 4839675..2462ccb 100644
--- a/scouting/webserver/requests/requests.go
+++ b/scouting/webserver/requests/requests.go
@@ -5,6 +5,7 @@
 	"io"
 	"net/http"
 
+	"github.com/frc971/971-Robot-Code/scouting/db"
 	"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"
 	_ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
@@ -12,6 +13,17 @@
 	flatbuffers "github.com/google/flatbuffers/go"
 )
 
+// The interface we expect the database abstraction to conform to.
+// We use an interface here because it makes unit testing easier.
+type Database interface {
+	AddToMatch(db.Match) error
+	AddToStats(db.Stats) error
+	ReturnMatches() ([]db.Match, error)
+	ReturnStats() ([]db.Stats, error)
+	QueryMatches(int) ([]db.Match, error)
+	QueryStats(int) ([]db.Stats, error)
+}
+
 // Handles unknown requests. Just returns a 404.
 func unknown(w http.ResponseWriter, req *http.Request) {
 	w.WriteHeader(http.StatusNotFound)
@@ -44,7 +56,11 @@
 }
 
 // Handles a SubmitDataScouting request.
-func submitDataScouting(w http.ResponseWriter, req *http.Request) {
+type submitDataScoutingHandler struct {
+	db Database
+}
+
+func (handler submitDataScoutingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 	requestBytes, err := io.ReadAll(req.Body)
 	if err != nil {
 		respondWithError(w, http.StatusBadRequest, fmt.Sprint("Failed to read request bytes:", err))
@@ -57,11 +73,13 @@
 	}
 
 	// TODO(phil): Actually handle the request.
+	// We have access to the database via "handler.db" here. For example:
+	// stats := handler.db.ReturnStats()
 
 	respondNotImplemented(w)
 }
 
-func HandleRequests(scoutingServer server.ScoutingServer) {
+func HandleRequests(db Database, scoutingServer server.ScoutingServer) {
 	scoutingServer.HandleFunc("/requests", unknown)
-	scoutingServer.HandleFunc("/requests/submit/data_scouting", submitDataScouting)
+	scoutingServer.Handle("/requests/submit/data_scouting", submitDataScoutingHandler{db})
 }
diff --git a/scouting/webserver/requests/requests_test.go b/scouting/webserver/requests/requests_test.go
index 11d728c..6f62ce3 100644
--- a/scouting/webserver/requests/requests_test.go
+++ b/scouting/webserver/requests/requests_test.go
@@ -6,6 +6,7 @@
 	"net/http"
 	"testing"
 
+	"github.com/frc971/971-Robot-Code/scouting/db"
 	"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"
 	_ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
@@ -15,8 +16,9 @@
 
 // Validates that an unhandled address results in a 404.
 func Test404(t *testing.T) {
+	db := MockDatabase{}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(scoutingServer)
+	HandleRequests(&db, scoutingServer)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -31,8 +33,9 @@
 
 // Validates that we can submit new data scouting data.
 func TestSubmitDataScoutingError(t *testing.T) {
+	db := MockDatabase{}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(scoutingServer)
+	HandleRequests(&db, scoutingServer)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -58,8 +61,9 @@
 
 // Validates that we can submit new data scouting data.
 func TestSubmitDataScouting(t *testing.T) {
+	db := MockDatabase{}
 	scoutingServer := server.NewScoutingServer()
-	HandleRequests(scoutingServer)
+	HandleRequests(&db, scoutingServer)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -79,3 +83,32 @@
 	}
 	// TODO(phil): We have nothing to validate yet. Fix that.
 }
+
+// A mocked database we can use for testing. Add functionality to this as
+// needed for your tests.
+
+type MockDatabase struct{}
+
+func (database *MockDatabase) AddToMatch(db.Match) error {
+	return nil
+}
+
+func (database *MockDatabase) AddToStats(db.Stats) error {
+	return nil
+}
+
+func (database *MockDatabase) ReturnMatches() ([]db.Match, error) {
+	return []db.Match{}, nil
+}
+
+func (database *MockDatabase) ReturnStats() ([]db.Stats, error) {
+	return []db.Stats{}, nil
+}
+
+func (database *MockDatabase) QueryMatches(int) ([]db.Match, error) {
+	return []db.Match{}, nil
+}
+
+func (database *MockDatabase) QueryStats(int) ([]db.Stats, error) {
+	return []db.Stats{}, nil
+}