mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Refactor partner API clients into separate package.
This commit is contained in:
@@ -60,12 +60,13 @@ func TestArenaMatchFlow(t *testing.T) {
|
||||
|
||||
db.CreateTeam(&model.Team{Id: 254})
|
||||
err := mainArena.AssignTeam(254, "B3")
|
||||
assert.Nil(t, err)
|
||||
dummyDs := &DriverStationConnection{TeamId: 254}
|
||||
mainArena.AllianceStations["B3"].DsConn = dummyDs
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Check pre-match state and packet timing.
|
||||
assert.Equal(t, preMatch, mainArena.MatchState)
|
||||
mainArena.lastDsPacketTime = mainArena.lastDsPacketTime.Add(-300 * time.Millisecond)
|
||||
mainArena.Update()
|
||||
assert.Equal(t, true, mainArena.AllianceStations["B3"].DsConn.Auto)
|
||||
assert.Equal(t, false, mainArena.AllianceStations["B3"].DsConn.Enabled)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2017 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Data for use in tests in this package and others.
|
||||
// Helper methods for use in tests in this package and others.
|
||||
|
||||
package game
|
||||
|
||||
5
main.go
5
main.go
@@ -5,6 +5,7 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/Team254/cheesy-arena/partner"
|
||||
"log"
|
||||
"math/rand"
|
||||
"time"
|
||||
@@ -14,11 +15,15 @@ const eventDbPath = "./event.db"
|
||||
|
||||
var db *model.Database
|
||||
var eventSettings *model.EventSettings
|
||||
var tbaClient *partner.TbaClient
|
||||
var stemTvClient *partner.StemTvClient
|
||||
|
||||
// Main entry point for the application.
|
||||
func main() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
initDb()
|
||||
tbaClient = partner.NewTbaClient(eventSettings.TbaEventCode, eventSettings.TbaSecretId, eventSettings.TbaSecret)
|
||||
stemTvClient = partner.NewStemTvClient(eventSettings.StemTvEventCode)
|
||||
|
||||
// Run the webserver and DS packet listener in goroutines and use the main one for the arena state machine.
|
||||
go ServeWebInterface()
|
||||
|
||||
@@ -508,12 +508,12 @@ func CommitMatchScore(match *model.Match, matchResult *model.MatchResult, loadTo
|
||||
if eventSettings.TbaPublishingEnabled && match.Type != "practice" {
|
||||
// Publish asynchronously to The Blue Alliance.
|
||||
go func() {
|
||||
err = PublishMatches()
|
||||
err = tbaClient.PublishMatches(db)
|
||||
if err != nil {
|
||||
log.Printf("Failed to publish matches: %s", err.Error())
|
||||
}
|
||||
if match.Type == "qualification" {
|
||||
err = PublishRankings()
|
||||
err = tbaClient.PublishRankings(db)
|
||||
if err != nil {
|
||||
log.Printf("Failed to publish rankings: %s", err.Error())
|
||||
}
|
||||
@@ -524,7 +524,7 @@ func CommitMatchScore(match *model.Match, matchResult *model.MatchResult, loadTo
|
||||
if eventSettings.StemTvPublishingEnabled && match.Type != "practice" {
|
||||
// Publish asynchronously to STEMtv.
|
||||
go func() {
|
||||
err = PublishMatchVideoSplit(match, time.Now())
|
||||
err = stemTvClient.PublishMatchVideoSplit(match, time.Now())
|
||||
if err != nil {
|
||||
log.Printf("Failed to publish match video split to STEMtv: %s", err.Error())
|
||||
}
|
||||
|
||||
@@ -156,8 +156,8 @@ func TestCommitMatch(t *testing.T) {
|
||||
assert.Equal(t, "T", match.Winner)
|
||||
|
||||
// Verify TBA and STEMtv publishing by checking the log for the expected failure messages.
|
||||
tbaBaseUrl = "fakeurl"
|
||||
stemTvBaseUrl = "fakeurl"
|
||||
tbaClient.BaseUrl = "fakeUrl"
|
||||
stemTvClient.BaseUrl = "fakeUrl"
|
||||
eventSettings.TbaPublishingEnabled = true
|
||||
eventSettings.StemTvPublishingEnabled = true
|
||||
var writer bytes.Buffer
|
||||
|
||||
@@ -5,21 +5,14 @@ package model
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const testDbPath = "test.db"
|
||||
|
||||
func TestOpenUnreachableDatabase(t *testing.T) {
|
||||
_, err := OpenDatabase("..", "nonexistentdir/test.db")
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func setupTestDb(t *testing.T) *Database {
|
||||
os.Remove(filepath.Join("..", testDbPath))
|
||||
db, err := OpenDatabase("..", testDbPath)
|
||||
assert.Nil(t, err)
|
||||
return db
|
||||
return SetupTestDb(t, "model", "..")
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright 2017 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Data for use in tests in this package and others.
|
||||
|
||||
package model
|
||||
|
||||
import "github.com/Team254/cheesy-arena/game"
|
||||
|
||||
func BuildTestMatchResult(matchId int, playNumber int) *MatchResult {
|
||||
matchResult := &MatchResult{MatchId: matchId, PlayNumber: playNumber, MatchType: "qualification"}
|
||||
matchResult.RedScore = game.TestScore1()
|
||||
matchResult.BlueScore = game.TestScore2()
|
||||
matchResult.RedCards = map[string]string{"1868": "yellow"}
|
||||
matchResult.BlueCards = map[string]string{}
|
||||
return matchResult
|
||||
}
|
||||
|
||||
func BuildTestAlliances(db *Database) {
|
||||
db.CreateAllianceTeam(&AllianceTeam{0, 2, 0, 1718})
|
||||
db.CreateAllianceTeam(&AllianceTeam{0, 1, 3, 74})
|
||||
db.CreateAllianceTeam(&AllianceTeam{0, 1, 1, 469})
|
||||
db.CreateAllianceTeam(&AllianceTeam{0, 1, 0, 254})
|
||||
db.CreateAllianceTeam(&AllianceTeam{0, 1, 2, 2848})
|
||||
db.CreateAllianceTeam(&AllianceTeam{0, 2, 1, 2451})
|
||||
}
|
||||
43
model/test_helpers.go
Normal file
43
model/test_helpers.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2017 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Helper methods for use in tests in this package and others.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/game"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const testDbPath = "%s_test.db"
|
||||
|
||||
func SetupTestDb(t *testing.T, uniqueName, baseDir string) *Database {
|
||||
dbPath := fmt.Sprintf(testDbPath, uniqueName)
|
||||
os.Remove(filepath.Join(baseDir, dbPath))
|
||||
database, err := OpenDatabase(baseDir, dbPath)
|
||||
assert.Nil(t, err)
|
||||
return database
|
||||
}
|
||||
|
||||
func BuildTestMatchResult(matchId int, playNumber int) *MatchResult {
|
||||
matchResult := &MatchResult{MatchId: matchId, PlayNumber: playNumber, MatchType: "qualification"}
|
||||
matchResult.RedScore = game.TestScore1()
|
||||
matchResult.BlueScore = game.TestScore2()
|
||||
matchResult.RedCards = map[string]string{"1868": "yellow"}
|
||||
matchResult.BlueCards = map[string]string{}
|
||||
return matchResult
|
||||
}
|
||||
|
||||
func BuildTestAlliances(database *Database) {
|
||||
database.CreateAllianceTeam(&AllianceTeam{0, 2, 0, 1718})
|
||||
database.CreateAllianceTeam(&AllianceTeam{0, 1, 3, 74})
|
||||
database.CreateAllianceTeam(&AllianceTeam{0, 1, 1, 469})
|
||||
database.CreateAllianceTeam(&AllianceTeam{0, 1, 0, 254})
|
||||
database.CreateAllianceTeam(&AllianceTeam{0, 1, 2, 2848})
|
||||
database.CreateAllianceTeam(&AllianceTeam{0, 2, 1, 2451})
|
||||
}
|
||||
35
partner/stemtv.go
Normal file
35
partner/stemtv.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2016 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Methods for publishing match video split information to STEMtv.
|
||||
|
||||
package partner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type StemTvClient struct {
|
||||
BaseUrl string
|
||||
eventCode string
|
||||
}
|
||||
|
||||
const (
|
||||
stemTvBaseUrl = "http://stemtv.io"
|
||||
preMatchPaddingSec = 5
|
||||
postScoreDisplayPaddingSec = 10
|
||||
)
|
||||
|
||||
func NewStemTvClient(eventCode string) *StemTvClient {
|
||||
return &StemTvClient{stemTvBaseUrl, eventCode}
|
||||
}
|
||||
|
||||
func (client *StemTvClient) PublishMatchVideoSplit(match *model.Match, scoreDisplayTime time.Time) error {
|
||||
url := fmt.Sprintf("%s/event/api/v1.0/%s/%s/split/%d,%d", client.BaseUrl, client.eventCode, match.TbaCode(),
|
||||
match.StartedAt.Unix()-preMatchPaddingSec, scoreDisplayTime.Unix()+postScoreDisplayPaddingSec)
|
||||
_, err := http.Get(url)
|
||||
return err
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2016 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
|
||||
package main
|
||||
package partner
|
||||
|
||||
import (
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
@@ -13,19 +13,17 @@ import (
|
||||
)
|
||||
|
||||
func TestPublishMatchVideoSplit(t *testing.T) {
|
||||
setupTest(t)
|
||||
|
||||
eventSettings.StemTvEventCode = "my_event_code"
|
||||
|
||||
// Mock the STEMtv server.
|
||||
stemTvServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "/event/api/v1.0/my_event_code/qm254/split/981187501,981187690", r.URL.String())
|
||||
}))
|
||||
defer stemTvServer.Close()
|
||||
stemTvBaseUrl = stemTvServer.URL
|
||||
|
||||
client := NewStemTvClient("my_event_code")
|
||||
client.BaseUrl = stemTvServer.URL
|
||||
|
||||
matchStartedTime, _ := time.Parse("2006-01-02 15:04:05 -0700", "2001-02-03 04:05:06 -0400")
|
||||
match := &model.Match{Type: "qualification", DisplayName: "254", StartedAt: matchStartedTime}
|
||||
scoreDisplayTime, _ := time.Parse("2006-01-02 15:04:05 -0700", "2001-02-03 04:08:00 -0400")
|
||||
assert.Nil(t, PublishMatchVideoSplit(match, scoreDisplayTime))
|
||||
assert.Nil(t, client.PublishMatchVideoSplit(match, scoreDisplayTime))
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// Methods for publishing data to and retrieving data from The Blue Alliance.
|
||||
|
||||
package main
|
||||
package partner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -17,17 +17,15 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Distinct endpoints are necessary for testing.
|
||||
var tbaBaseUrl = "https://www.thebluealliance.com"
|
||||
var tbaTeamBaseUrl = tbaBaseUrl
|
||||
var tbaTeamRobotsBaseUrl = tbaBaseUrl
|
||||
var tbaTeamAwardsBaseUrl = tbaBaseUrl
|
||||
var tbaEventBaseUrl = tbaBaseUrl
|
||||
type TbaClient struct {
|
||||
BaseUrl string
|
||||
eventCode string
|
||||
secretId string
|
||||
secret string
|
||||
eventNamesCache map[string]string
|
||||
}
|
||||
|
||||
// Cache of event codes to names.
|
||||
var tbaEventNames = make(map[string]string)
|
||||
|
||||
// MODELS
|
||||
const tbaBaseUrl = "https://www.thebluealliance.com"
|
||||
|
||||
type TbaMatch struct {
|
||||
CompLevel string `json:"comp_level"`
|
||||
@@ -114,10 +112,14 @@ type TbaEvent struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// DATA RETRIEVAL
|
||||
func getTeamFromTba(teamNumber int) (*TbaTeam, error) {
|
||||
url := fmt.Sprintf("%s/api/v2/team/%s", tbaTeamBaseUrl, getTbaTeam(teamNumber))
|
||||
resp, err := getTbaRequest(url)
|
||||
func NewTbaClient(eventCode, secretId, secret string) *TbaClient {
|
||||
return &TbaClient{BaseUrl: tbaBaseUrl, eventCode: eventCode, secretId: secretId, secret: secret,
|
||||
eventNamesCache: make(map[string]string)}
|
||||
}
|
||||
|
||||
func (client *TbaClient) GetTeam(teamNumber int) (*TbaTeam, error) {
|
||||
path := fmt.Sprintf("/api/v2/team/%s", getTbaTeam(teamNumber))
|
||||
resp, err := client.getRequest(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -135,9 +137,9 @@ func getTeamFromTba(teamNumber int) (*TbaTeam, error) {
|
||||
return &teamData, err
|
||||
}
|
||||
|
||||
func getRobotNameFromTba(teamNumber int, year int) (string, error) {
|
||||
url := fmt.Sprintf("%s/api/v2/team/frc%d/history/robots", tbaTeamRobotsBaseUrl, teamNumber)
|
||||
resp, err := getTbaRequest(url)
|
||||
func (client *TbaClient) GetRobotName(teamNumber int, year int) (string, error) {
|
||||
path := fmt.Sprintf("/api/v2/team/%s/history/robots", getTbaTeam(teamNumber))
|
||||
resp, err := client.getRequest(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -160,9 +162,9 @@ func getRobotNameFromTba(teamNumber int, year int) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func getTeamAwardsFromTba(teamNumber int) ([]*TbaAward, error) {
|
||||
url := fmt.Sprintf("%s/api/v2/team/%s/history/awards", tbaTeamAwardsBaseUrl, getTbaTeam(teamNumber))
|
||||
resp, err := getTbaRequest(url)
|
||||
func (client *TbaClient) GetTeamAwards(teamNumber int) ([]*TbaAward, error) {
|
||||
path := fmt.Sprintf("/api/v2/team/%s/history/awards", getTbaTeam(teamNumber))
|
||||
resp, err := client.getRequest(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -181,46 +183,21 @@ func getTeamAwardsFromTba(teamNumber int) ([]*TbaAward, error) {
|
||||
}
|
||||
|
||||
for _, award := range awards {
|
||||
if _, ok := tbaEventNames[award.EventKey]; !ok {
|
||||
tbaEventNames[award.EventKey], err = getEventNameFromTba(award.EventKey)
|
||||
if _, ok := client.eventNamesCache[award.EventKey]; !ok {
|
||||
client.eventNamesCache[award.EventKey], err = client.getEventName(award.EventKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
award.EventName = tbaEventNames[award.EventKey]
|
||||
award.EventName = client.eventNamesCache[award.EventKey]
|
||||
}
|
||||
|
||||
return awards, nil
|
||||
}
|
||||
|
||||
func getEventNameFromTba(eventCode string) (string, error) {
|
||||
url := fmt.Sprintf("%s/api/v2/event/%s", tbaEventBaseUrl, eventCode)
|
||||
resp, err := getTbaRequest(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Get the response and handle errors
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var event TbaEvent
|
||||
err = json.Unmarshal(body, &event)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return event.Name, err
|
||||
}
|
||||
|
||||
// PUBLISHING
|
||||
|
||||
// Uploads the event team list to The Blue Alliance.
|
||||
func PublishTeams() error {
|
||||
teams, err := db.GetAllTeams()
|
||||
func (client *TbaClient) PublishTeams(database *model.Database) error {
|
||||
teams, err := database.GetAllTeams()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -235,7 +212,7 @@ func PublishTeams() error {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := postTbaRequest("team_list", "update", jsonBody)
|
||||
resp, err := client.postRequest("team_list", "update", jsonBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -248,12 +225,12 @@ func PublishTeams() error {
|
||||
}
|
||||
|
||||
// Uploads the qualification and elimination match schedule and results to The Blue Alliance.
|
||||
func PublishMatches() error {
|
||||
qualMatches, err := db.GetMatchesByType("qualification")
|
||||
func (client *TbaClient) PublishMatches(database *model.Database) error {
|
||||
qualMatches, err := database.GetMatchesByType("qualification")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
elimMatches, err := db.GetMatchesByType("elimination")
|
||||
elimMatches, err := database.GetMatchesByType("elimination")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -271,7 +248,7 @@ func PublishMatches() error {
|
||||
|
||||
// Fill in scores if the match has been played.
|
||||
if match.Status == "complete" {
|
||||
matchResult, err := db.GetMatchResultForMatch(match.Id)
|
||||
matchResult, err := database.GetMatchResultForMatch(match.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -298,7 +275,7 @@ func PublishMatches() error {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := postTbaRequest("matches", "update", jsonBody)
|
||||
resp, err := client.postRequest("matches", "update", jsonBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -311,8 +288,8 @@ func PublishMatches() error {
|
||||
}
|
||||
|
||||
// Uploads the team standings to The Blue Alliance.
|
||||
func PublishRankings() error {
|
||||
rankings, err := db.GetAllRankings()
|
||||
func (client *TbaClient) PublishRankings(database *model.Database) error {
|
||||
rankings, err := database.GetAllRankings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -332,7 +309,7 @@ func PublishRankings() error {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := postTbaRequest("rankings", "update", jsonBody)
|
||||
resp, err := client.postRequest("rankings", "update", jsonBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -345,8 +322,8 @@ func PublishRankings() error {
|
||||
}
|
||||
|
||||
// Uploads the alliances selection results to The Blue Alliance.
|
||||
func PublishAlliances() error {
|
||||
alliances, err := db.GetAllAlliances()
|
||||
func (client *TbaClient) PublishAlliances(database *model.Database) error {
|
||||
alliances, err := database.GetAllAlliances()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -363,7 +340,7 @@ func PublishAlliances() error {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := postTbaRequest("alliance_selections", "update", jsonBody)
|
||||
resp, err := client.postRequest("alliance_selections", "update", jsonBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -376,8 +353,8 @@ func PublishAlliances() error {
|
||||
}
|
||||
|
||||
// Clears out the existing match data on The Blue Alliance for the event.
|
||||
func DeletePublishedMatches() error {
|
||||
resp, err := postTbaRequest("matches", "delete_all", []byte(eventSettings.TbaEventCode))
|
||||
func (client *TbaClient) DeletePublishedMatches() error {
|
||||
resp, err := client.postRequest("matches", "delete_all", []byte(client.eventCode))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -389,38 +366,61 @@ func DeletePublishedMatches() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *TbaClient) getEventName(eventCode string) (string, error) {
|
||||
path := fmt.Sprintf("/api/v2/event/%s", eventCode)
|
||||
resp, err := client.getRequest(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Get the response and handle errors
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var event TbaEvent
|
||||
err = json.Unmarshal(body, &event)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return event.Name, err
|
||||
}
|
||||
|
||||
// Converts an integer team number into the "frcXXXX" format TBA expects.
|
||||
func getTbaTeam(team int) string {
|
||||
return fmt.Sprintf("frc%d", team)
|
||||
}
|
||||
|
||||
// HELPERS
|
||||
// Sends a GET request to the TBA API.
|
||||
func (client *TbaClient) getRequest(path string) (*http.Response, error) {
|
||||
url := client.BaseUrl + path
|
||||
|
||||
// Signs the request and sends it to the TBA API.
|
||||
func postTbaRequest(resource string, action string, body []byte) (*http.Response, error) {
|
||||
path := fmt.Sprintf("/api/trusted/v1/event/%s/%s/%s", eventSettings.TbaEventCode, resource, action)
|
||||
signature := fmt.Sprintf("%x", md5.Sum(append([]byte(eventSettings.TbaSecret+path), body...)))
|
||||
|
||||
client := &http.Client{}
|
||||
request, err := http.NewRequest("POST", fmt.Sprintf("%s%s", tbaBaseUrl, path), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Header.Add("X-TBA-Auth-Id", eventSettings.TbaSecretId)
|
||||
request.Header.Add("X-TBA-Auth-Sig", signature)
|
||||
return client.Do(request)
|
||||
}
|
||||
|
||||
// Sends a GET request to the TBA API
|
||||
func getTbaRequest(url string) (*http.Response, error) {
|
||||
// Make an HTTP GET request with the TBA auth headers
|
||||
client := &http.Client{}
|
||||
// Make an HTTP GET request with the TBA auth headers.
|
||||
httpClient := &http.Client{}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-TBA-App-Id", "cheesy-arena:cheesy-fms:v0.1")
|
||||
return client.Do(req)
|
||||
return httpClient.Do(req)
|
||||
}
|
||||
|
||||
// Signs the request and sends it to the TBA API.
|
||||
func (client *TbaClient) postRequest(resource string, action string, body []byte) (*http.Response, error) {
|
||||
path := fmt.Sprintf("/api/trusted/v1/event/%s/%s/%s", client.eventCode, resource, action)
|
||||
signature := fmt.Sprintf("%x", md5.Sum(append([]byte(client.secret+path), body...)))
|
||||
|
||||
httpClient := &http.Client{}
|
||||
request, err := http.NewRequest("POST", client.BaseUrl+path, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Header.Add("X-TBA-Auth-Id", client.secretId)
|
||||
request.Header.Add("X-TBA-Auth-Sig", signature)
|
||||
return httpClient.Do(request)
|
||||
}
|
||||
|
||||
func createTbaScoringBreakdown(match *model.Match, matchResult *model.MatchResult, alliance string) *TbaScoreBreakdown {
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2014 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
|
||||
package main
|
||||
package partner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -17,13 +17,10 @@ import (
|
||||
)
|
||||
|
||||
func TestPublishTeams(t *testing.T) {
|
||||
setupTest(t)
|
||||
database := setupTestDb(t)
|
||||
|
||||
eventSettings.TbaEventCode = "my_event_code"
|
||||
eventSettings.TbaSecretId = "my_secret_id"
|
||||
eventSettings.TbaSecret = "my_secret"
|
||||
db.CreateTeam(&model.Team{Id: 254})
|
||||
db.CreateTeam(&model.Team{Id: 1114})
|
||||
database.CreateTeam(&model.Team{Id: 254})
|
||||
database.CreateTeam(&model.Team{Id: 1114})
|
||||
|
||||
// Mock the TBA server.
|
||||
tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -35,21 +32,22 @@ func TestPublishTeams(t *testing.T) {
|
||||
assert.Equal(t, "f5c022fde6d1186ea0719fe28ab6cc63", r.Header["X-Tba-Auth-Sig"][0])
|
||||
}))
|
||||
defer tbaServer.Close()
|
||||
tbaBaseUrl = tbaServer.URL
|
||||
client := NewTbaClient("my_event_code", "my_secret_id", "my_secret")
|
||||
client.BaseUrl = tbaServer.URL
|
||||
|
||||
assert.Nil(t, PublishTeams())
|
||||
assert.Nil(t, client.PublishTeams(database))
|
||||
}
|
||||
|
||||
func TestPublishMatches(t *testing.T) {
|
||||
setupTest(t)
|
||||
database := setupTestDb(t)
|
||||
|
||||
match1 := model.Match{Type: "qualification", DisplayName: "2", Time: time.Unix(600, 0), Red1: 7, Red2: 8, Red3: 9,
|
||||
Blue1: 10, Blue2: 11, Blue3: 12, Status: "complete"}
|
||||
match2 := model.Match{Type: "elimination", DisplayName: "SF2-2", ElimRound: 2, ElimGroup: 2, ElimInstance: 2}
|
||||
db.CreateMatch(&match1)
|
||||
db.CreateMatch(&match2)
|
||||
database.CreateMatch(&match1)
|
||||
database.CreateMatch(&match2)
|
||||
matchResult1 := model.BuildTestMatchResult(match1.Id, 1)
|
||||
db.CreateMatchResult(matchResult1)
|
||||
database.CreateMatchResult(matchResult1)
|
||||
|
||||
// Mock the TBA server.
|
||||
tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -61,16 +59,17 @@ func TestPublishMatches(t *testing.T) {
|
||||
assert.Equal(t, "sf", matches[1].CompLevel)
|
||||
}))
|
||||
defer tbaServer.Close()
|
||||
tbaBaseUrl = tbaServer.URL
|
||||
client := NewTbaClient("my_event_code", "my_secret_id", "my_secret")
|
||||
client.BaseUrl = tbaServer.URL
|
||||
|
||||
assert.Nil(t, PublishMatches())
|
||||
assert.Nil(t, client.PublishMatches(database))
|
||||
}
|
||||
|
||||
func TestPublishRankings(t *testing.T) {
|
||||
setupTest(t)
|
||||
database := setupTestDb(t)
|
||||
|
||||
db.CreateRanking(game.TestRanking2())
|
||||
db.CreateRanking(game.TestRanking1())
|
||||
database.CreateRanking(game.TestRanking2())
|
||||
database.CreateRanking(game.TestRanking1())
|
||||
|
||||
// Mock the TBA server.
|
||||
tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -82,15 +81,16 @@ func TestPublishRankings(t *testing.T) {
|
||||
assert.Equal(t, "frc1114", response.Rankings[1].TeamKey)
|
||||
}))
|
||||
defer tbaServer.Close()
|
||||
tbaBaseUrl = tbaServer.URL
|
||||
client := NewTbaClient("my_event_code", "my_secret_id", "my_secret")
|
||||
client.BaseUrl = tbaServer.URL
|
||||
|
||||
assert.Nil(t, PublishRankings())
|
||||
assert.Nil(t, client.PublishRankings(database))
|
||||
}
|
||||
|
||||
func TestPublishAlliances(t *testing.T) {
|
||||
setupTest(t)
|
||||
database := setupTestDb(t)
|
||||
|
||||
model.BuildTestAlliances(db)
|
||||
model.BuildTestAlliances(database)
|
||||
|
||||
// Mock the TBA server.
|
||||
tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -100,25 +100,31 @@ func TestPublishAlliances(t *testing.T) {
|
||||
reader.String())
|
||||
}))
|
||||
defer tbaServer.Close()
|
||||
tbaBaseUrl = tbaServer.URL
|
||||
client := NewTbaClient("my_event_code", "my_secret_id", "my_secret")
|
||||
client.BaseUrl = tbaServer.URL
|
||||
|
||||
assert.Nil(t, PublishAlliances())
|
||||
assert.Nil(t, client.PublishAlliances(database))
|
||||
}
|
||||
|
||||
func TestPublishingErrors(t *testing.T) {
|
||||
setupTest(t)
|
||||
database := setupTestDb(t)
|
||||
|
||||
model.BuildTestAlliances(db)
|
||||
model.BuildTestAlliances(database)
|
||||
|
||||
// Mock the TBA server.
|
||||
tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "oh noes", 500)
|
||||
}))
|
||||
defer tbaServer.Close()
|
||||
tbaBaseUrl = tbaServer.URL
|
||||
client := NewTbaClient("my_event_code", "my_secret_id", "my_secret")
|
||||
client.BaseUrl = tbaServer.URL
|
||||
|
||||
assert.NotNil(t, PublishTeams())
|
||||
assert.NotNil(t, PublishMatches())
|
||||
assert.NotNil(t, PublishRankings())
|
||||
assert.NotNil(t, PublishAlliances())
|
||||
assert.NotNil(t, client.PublishTeams(database))
|
||||
assert.NotNil(t, client.PublishMatches(database))
|
||||
assert.NotNil(t, client.PublishRankings(database))
|
||||
assert.NotNil(t, client.PublishAlliances(database))
|
||||
}
|
||||
|
||||
func setupTestDb(t *testing.T) *model.Database {
|
||||
return model.SetupTestDb(t, "partner", "..")
|
||||
}
|
||||
@@ -210,12 +210,12 @@ func AllianceSelectionFinalizeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if eventSettings.TbaPublishingEnabled {
|
||||
// Publish alliances and schedule to The Blue Alliance.
|
||||
err = PublishAlliances()
|
||||
err = tbaClient.PublishAlliances(db)
|
||||
if err != nil {
|
||||
renderAllianceSelection(w, r, fmt.Sprintf("Failed to publish alliances: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
err = PublishMatches()
|
||||
err = tbaClient.PublishMatches(db)
|
||||
if err != nil {
|
||||
renderAllianceSelection(w, r, fmt.Sprintf("Failed to publish matches: %s", err.Error()))
|
||||
return
|
||||
|
||||
@@ -136,7 +136,7 @@ func TestSetupAllianceSelectionErrors(t *testing.T) {
|
||||
assert.Contains(t, recorder.Body.String(), "valid start time")
|
||||
|
||||
// Finalize for real and check that TBA publishing is triggered.
|
||||
tbaBaseUrl = "fakeurl"
|
||||
tbaClient.BaseUrl = "fakeurl"
|
||||
eventSettings.TbaPublishingEnabled = true
|
||||
recorder = postHttpResponse("/setup/alliance_selection/finalize", "startTime=2014-01-01 01:00:00 PM")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
|
||||
@@ -99,12 +99,12 @@ func ScheduleGeneratePostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func ScheduleRepublishPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if eventSettings.TbaPublishingEnabled {
|
||||
// Publish schedule to The Blue Alliance.
|
||||
err := DeletePublishedMatches()
|
||||
err := tbaClient.DeletePublishedMatches()
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to delete published matches: "+err.Error(), 500)
|
||||
return
|
||||
}
|
||||
err = PublishMatches()
|
||||
err = tbaClient.PublishMatches(db)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to publish matches: "+err.Error(), 500)
|
||||
return
|
||||
@@ -151,12 +151,12 @@ func ScheduleSavePostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if eventSettings.TbaPublishingEnabled && cachedMatchType != "practice" {
|
||||
// Publish schedule to The Blue Alliance.
|
||||
err = DeletePublishedMatches()
|
||||
err = tbaClient.DeletePublishedMatches()
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to delete published matches: "+err.Error(), 500)
|
||||
return
|
||||
}
|
||||
err = PublishMatches()
|
||||
err = tbaClient.PublishMatches(db)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to publish matches: "+err.Error(), 500)
|
||||
return
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestSetupSchedule(t *testing.T) {
|
||||
assert.Contains(t, recorder.Body.String(), "2014-01-03 16:54:00") // Last match of third block.
|
||||
|
||||
// Save schedule and check that it is published to TBA.
|
||||
tbaBaseUrl = "fakeurl"
|
||||
tbaClient.BaseUrl = "fakeUrl"
|
||||
eventSettings.TbaPublishingEnabled = true
|
||||
recorder = postHttpResponse("/setup/schedule/save", "")
|
||||
matches, err := db.GetMatchesByType("qualification")
|
||||
|
||||
@@ -8,6 +8,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/Team254/cheesy-arena/partner"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -81,6 +82,10 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Refresh the partner clients in case they changed.
|
||||
tbaClient = partner.NewTbaClient(eventSettings.TbaEventCode, eventSettings.TbaSecretId, eventSettings.TbaSecret)
|
||||
stemTvClient = partner.NewStemTvClient(eventSettings.StemTvEventCode)
|
||||
|
||||
http.Redirect(w, r, "/setup/settings", 302)
|
||||
}
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ func TeamsPublishHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err := PublishTeams()
|
||||
err := tbaClient.PublishTeams(db)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to publish teams: "+err.Error(), 500)
|
||||
return
|
||||
@@ -267,7 +267,7 @@ func getOfficialTeamInfo(teamId int) (*model.Team, error) {
|
||||
|
||||
// If team info download is enabled, download the current teams data (caching isn't easy with the new paging system in the api)
|
||||
if eventSettings.TBADownloadEnabled {
|
||||
tbaTeam, err := getTeamFromTba(teamId)
|
||||
tbaTeam, err := tbaClient.GetTeam(teamId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -276,12 +276,12 @@ func getOfficialTeamInfo(teamId int) (*model.Team, error) {
|
||||
if tbaTeam.TeamNumber == 0 {
|
||||
team = model.Team{Id: teamId}
|
||||
} else {
|
||||
robotName, err := getRobotNameFromTba(teamId, time.Now().Year())
|
||||
robotName, err := tbaClient.GetRobotName(teamId, time.Now().Year())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
recentAwards, err := getTeamAwardsFromTba(teamId)
|
||||
recentAwards, err := tbaClient.GetTeamAwards(teamId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -33,12 +34,14 @@ func TestSetupTeams(t *testing.T) {
|
||||
"country_name": "USA",
|
||||
"nickname": "The Cheesy Poofs"
|
||||
}`
|
||||
teamInfoServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, teamInfoBody)
|
||||
}))
|
||||
defer teamInfoServer.Close()
|
||||
tbaTeamBaseUrl = teamInfoServer.URL
|
||||
|
||||
teamRobotsBody := `{
|
||||
"2017": {
|
||||
"team_key": "frc33",
|
||||
"name": "Buzz 22",
|
||||
"key": "frc33_2017",
|
||||
"year": 2017
|
||||
}
|
||||
}`
|
||||
teamAwardsBody := `[{
|
||||
"event_key": "2014cmp",
|
||||
"award_type": 1,
|
||||
@@ -63,18 +66,22 @@ func TestSetupTeams(t *testing.T) {
|
||||
],
|
||||
"year": 2014
|
||||
}]`
|
||||
teamAwardsServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, teamAwardsBody)
|
||||
}))
|
||||
defer teamAwardsServer.Close()
|
||||
tbaTeamAwardsBaseUrl = teamAwardsServer.URL
|
||||
|
||||
eventBody := `{ "name": "Championship" }`
|
||||
eventServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, eventBody)
|
||||
tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.Contains(r.RequestURI, "history/robots") {
|
||||
fmt.Fprintln(w, teamRobotsBody)
|
||||
} else if strings.Contains(r.RequestURI, "history/awards") {
|
||||
fmt.Fprintln(w, teamAwardsBody)
|
||||
} else if strings.Contains(r.RequestURI, "team") {
|
||||
fmt.Fprintln(w, teamInfoBody)
|
||||
} else if strings.Contains(r.RequestURI, "event") {
|
||||
fmt.Fprintln(w, eventBody)
|
||||
} else {
|
||||
http.Error(w, "Unexpected request during test", 500)
|
||||
}
|
||||
}))
|
||||
defer eventServer.Close()
|
||||
tbaEventBaseUrl = eventServer.URL
|
||||
defer tbaServer.Close()
|
||||
tbaClient.BaseUrl = tbaServer.URL
|
||||
|
||||
// Add some teams.
|
||||
recorder = postHttpResponse("/setup/teams", "teamNumbers=254\r\nnotateam\r\n1114\r\n")
|
||||
@@ -195,7 +202,7 @@ func TestSetupTeamsWpaKeys(t *testing.T) {
|
||||
func TestSetupTeamsPublish(t *testing.T) {
|
||||
setupTest(t)
|
||||
|
||||
tbaBaseUrl = "fakeurl"
|
||||
tbaClient.BaseUrl = "fakeurl"
|
||||
eventSettings.TbaPublishingEnabled = true
|
||||
|
||||
recorder := postHttpResponse("/setup/teams/publish", "")
|
||||
|
||||
28
stemtv.go
28
stemtv.go
@@ -1,28 +0,0 @@
|
||||
// Copyright 2016 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Methods for publishing match video split information to STEMtv.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
preMatchPaddingSec = 5
|
||||
postScoreDisplayPaddingSec = 10
|
||||
)
|
||||
|
||||
var stemTvBaseUrl = "http://stemtv.io"
|
||||
|
||||
func PublishMatchVideoSplit(match *model.Match, scoreDisplayTime time.Time) error {
|
||||
url := fmt.Sprintf("%s/event/api/v1.0/%s/%s/split/%d,%d", stemTvBaseUrl, eventSettings.StemTvEventCode,
|
||||
match.TbaCode(), match.StartedAt.Unix()-preMatchPaddingSec,
|
||||
scoreDisplayTime.Unix()+postScoreDisplayPaddingSec)
|
||||
_, err := http.Get(url)
|
||||
return err
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
// Copyright 2017 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Helper methods for use in tests in this package and others.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/Team254/cheesy-arena/partner"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const testDbPath = "test.db"
|
||||
|
||||
func setupTest(t *testing.T) {
|
||||
os.Remove(testDbPath)
|
||||
db = model.SetupTestDb(t, "main", ".")
|
||||
var err error
|
||||
db, err = model.OpenDatabase(".", testDbPath)
|
||||
assert.Nil(t, err)
|
||||
eventSettings, err = db.GetEventSettings()
|
||||
assert.Nil(t, err)
|
||||
tbaClient = partner.NewTbaClient(eventSettings.TbaEventCode, eventSettings.TbaSecretId, eventSettings.TbaSecret)
|
||||
stemTvClient = partner.NewStemTvClient(eventSettings.StemTvEventCode)
|
||||
mainArena.Setup()
|
||||
}
|
||||
Reference in New Issue
Block a user