Add Pit Scouting Tab

Signed-off-by: Emily Markova <emily.markova@gmail.com>
Change-Id: Iede446546e20f2915bb53e134050b5025976da36
diff --git a/scouting/webserver/static/BUILD b/scouting/webserver/static/BUILD
index 0cf22f3..4b40c76 100644
--- a/scouting/webserver/static/BUILD
+++ b/scouting/webserver/static/BUILD
@@ -6,7 +6,10 @@
     importpath = "github.com/frc971/971-Robot-Code/scouting/webserver/static",
     target_compatible_with = ["@platforms//cpu:x86_64"],
     visibility = ["//visibility:public"],
-    deps = ["//scouting/webserver/server"],
+    deps = [
+        "//scouting/db",
+        "//scouting/webserver/server",
+    ],
 )
 
 go_test(
@@ -19,5 +22,8 @@
     ],
     embed = [":static"],
     target_compatible_with = ["@platforms//cpu:x86_64"],
-    deps = ["//scouting/webserver/server"],
+    deps = [
+        "//scouting/db",
+        "//scouting/webserver/server",
+    ],
 )
diff --git a/scouting/webserver/static/static.go b/scouting/webserver/static/static.go
index fbad476..8e2e400 100644
--- a/scouting/webserver/static/static.go
+++ b/scouting/webserver/static/static.go
@@ -7,12 +7,14 @@
 	"fmt"
 	"io"
 	"log"
+	"mime"
 	"net/http"
 	"os"
 	"path/filepath"
 	"strings"
 	"time"
 
+	"github.com/frc971/971-Robot-Code/scouting/db"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/server"
 )
 
@@ -28,6 +30,10 @@
 	"X-Accel-Expires": "0",
 }
 
+type ScoutingDatabase interface {
+	QueryPitImageByChecksum(checksum string) (db.PitImage, error)
+}
+
 func MaybeNoCache(h http.Handler) http.Handler {
 	fn := func(w http.ResponseWriter, r *http.Request) {
 		// We force the browser not to cache index.html so that
@@ -98,7 +104,7 @@
 	return shaSums
 }
 
-func HandleShaUrl(directory string, h http.Handler) http.Handler {
+func HandleShaUrl(directory string, h http.Handler, scoutingDatabase ScoutingDatabase) http.Handler {
 	shaSums := findAllFileShas(directory)
 
 	fn := func(w http.ResponseWriter, r *http.Request) {
@@ -132,9 +138,20 @@
 			// We found a file with this checksum. Serve that file.
 			r.URL.Path = path
 		} else {
-			// No file with this checksum found.
-			w.WriteHeader(http.StatusNotFound)
-			return
+			pitImage, err := scoutingDatabase.QueryPitImageByChecksum(hash)
+			if err == nil {
+				if parts[3] != pitImage.ImagePath {
+					log.Println("Got ", parts[3], "expected", pitImage.ImagePath)
+					w.WriteHeader(http.StatusBadRequest)
+					return
+				}
+				w.Header().Add("Content-Type", mime.TypeByExtension(pitImage.ImagePath))
+				w.Write(pitImage.ImageData)
+				return
+			} else { // No file with this checksum found.
+				w.WriteHeader(http.StatusNotFound)
+				return
+			}
 		}
 
 		h.ServeHTTP(w, r)
@@ -143,11 +160,10 @@
 	return http.HandlerFunc(fn)
 }
 
-// Serve pages in the specified directory.
-func ServePages(scoutingServer server.ScoutingServer, directory string) {
+func ServePages(scoutingServer server.ScoutingServer, directory string, scoutingDatabase ScoutingDatabase) {
 	// Serve the / endpoint given a folder of pages.
 	scoutingServer.Handle("/", MaybeNoCache(http.FileServer(http.Dir(directory))))
 
 	// Also serve files in a checksum-addressable manner.
-	scoutingServer.Handle("/sha256/", HandleShaUrl(directory, http.FileServer(http.Dir(directory))))
+	scoutingServer.Handle("/sha256/", HandleShaUrl(directory, http.FileServer(http.Dir(directory)), scoutingDatabase))
 }
diff --git a/scouting/webserver/static/static_test.go b/scouting/webserver/static/static_test.go
index 1d036a0..a68d973 100644
--- a/scouting/webserver/static/static_test.go
+++ b/scouting/webserver/static/static_test.go
@@ -1,14 +1,30 @@
 package static
 
 import (
+	"errors"
 	"fmt"
 	"io/ioutil"
 	"net/http"
 	"testing"
 
+	"github.com/frc971/971-Robot-Code/scouting/db"
 	"github.com/frc971/971-Robot-Code/scouting/webserver/server"
 )
 
+type MockDatabase struct {
+	images []db.PitImage
+}
+
+func (database *MockDatabase) QueryPitImageByChecksum(checksum string) (db.PitImage, error) {
+	for _, data := range database.images {
+		if data.CheckSum == checksum {
+			return data, nil
+		}
+	}
+
+	return db.PitImage{}, errors.New("Could not find pit image")
+}
+
 func expectEqual(t *testing.T, actual string, expected string) {
 	if actual != expected {
 		t.Error("Expected ", actual, " to equal ", expected)
@@ -16,6 +32,15 @@
 }
 
 func TestServing(t *testing.T) {
+	database := MockDatabase{
+		images: []db.PitImage{
+			{
+				TeamNumber: "971", CheckSum: "3yi32rhewd23",
+				ImagePath: "abc.png", ImageData: []byte("hello"),
+			},
+		},
+	}
+
 	cases := []struct {
 		// The path to request from the server.
 		path string
@@ -26,10 +51,11 @@
 		{"/", "<h1>This is the index</h1>\n"},
 		{"/root.txt", "Hello, this is the root page!"},
 		{"/page.txt", "Hello from a page!"},
+		{"/sha256/3yi32rhewd23/abc.png", "hello"},
 	}
 
 	scoutingServer := server.NewScoutingServer()
-	ServePages(scoutingServer, "test_pages")
+	ServePages(scoutingServer, "test_pages", &database)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -43,8 +69,9 @@
 // Makes sure that requesting / sets the proper headers so it doesn't get
 // cached.
 func TestDisallowedCache(t *testing.T) {
+	database := MockDatabase{}
 	scoutingServer := server.NewScoutingServer()
-	ServePages(scoutingServer, "test_pages")
+	ServePages(scoutingServer, "test_pages", &database)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -61,8 +88,9 @@
 // Makes sure that requesting anything other than / doesn't set the "do not
 // cache" headers.
 func TestAllowedCache(t *testing.T) {
+	database := MockDatabase{}
 	scoutingServer := server.NewScoutingServer()
-	ServePages(scoutingServer, "test_pages")
+	ServePages(scoutingServer, "test_pages", &database)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
@@ -77,11 +105,23 @@
 }
 
 func TestSha256(t *testing.T) {
+	database := MockDatabase{
+		images: []db.PitImage{
+			{
+				TeamNumber: "971", CheckSum: "3yi32rhewd23",
+				ImagePath: "abc.png", ImageData: []byte{32, 54, 23, 00},
+			},
+		},
+	}
 	scoutingServer := server.NewScoutingServer()
-	ServePages(scoutingServer, "test_pages")
+	ServePages(scoutingServer, "test_pages", &database)
 	scoutingServer.Start(8080)
 	defer scoutingServer.Stop()
 
+	//Validate receiving the correct byte sequence from image request.
+	byteDataReceived := getData("sha256/3yi32rhewd23/abc.png", t)
+	expectEqual(t, string(byteDataReceived), string([]byte{32, 54, 23, 00}))
+
 	// Validate a valid checksum.
 	dataReceived := getData("sha256/553b9b29647a112136986cf93c57b988d1f12dc43d3b774f14a24e58d272dbff/root.txt", t)
 	expectEqual(t, dataReceived, "Hello, this is the root page!")