From 80c94833998889a483ebc5386beb36fd39a9bc73 Mon Sep 17 00:00:00 2001 From: Your Face Date: Thu, 1 May 2014 17:38:23 -0400 Subject: [PATCH] added basic support for serving snippets of code up to the UI --- macro/annotation.html | 4 + makefile | 1 + st/macros/macros.go | 20 +++++ st/main.go | 7 +- st/server/api.go | 149 ++++++++++++++++++++++++++++++++++++++ st/server/blockmanager.go | 95 +++++++++++++++++++++++- 6 files changed, 271 insertions(+), 5 deletions(-) create mode 100644 macro/annotation.html create mode 100644 st/macros/macros.go diff --git a/macro/annotation.html b/macro/annotation.html new file mode 100644 index 00000000..3e258f2e --- /dev/null +++ b/macro/annotation.html @@ -0,0 +1,4 @@ +

What Ho, Streamtools!

+ diff --git a/makefile b/makefile index c4544aa6..01dd6d5b 100644 --- a/makefile +++ b/makefile @@ -7,6 +7,7 @@ all: $(BINARIES) $(BLDDIR)/%: go get github.com/jteeuwen/go-bindata/... + go-bindata -pkg=macros -o st/macros/static_bindata.go macro/... go-bindata -pkg=server -o st/server/static_bindata.go gui/... examples/... cd st/library && go get . cd st/server && go get . diff --git a/st/macros/macros.go b/st/macros/macros.go new file mode 100644 index 00000000..c6fdd129 --- /dev/null +++ b/st/macros/macros.go @@ -0,0 +1,20 @@ +package macros + +import "log" + +var Macros = map[string]string{ + "annotation": "macro/annotation.html", +} + +var MacroDefs = map[string][]byte{} + +func Start() { + for k, macro := range Macros { + macroAsset, err := Asset(macro) + if err != nil { + log.Println("cannot find macro asset", macro) + continue + } + MacroDefs[k] = macroAsset + } +} diff --git a/st/main.go b/st/main.go index 4d368772..99704ca0 100644 --- a/st/main.go +++ b/st/main.go @@ -2,12 +2,14 @@ package main import ( "flag" + "log" + "os" + "github.com/nytlabs/streamtools/st/library" "github.com/nytlabs/streamtools/st/loghub" + "github.com/nytlabs/streamtools/st/macros" "github.com/nytlabs/streamtools/st/server" "github.com/nytlabs/streamtools/st/util" - "log" - "os" ) var ( @@ -28,6 +30,7 @@ func main() { log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) library.Start() + macros.Start() loghub.Start() s := server.NewServer() diff --git a/st/server/api.go b/st/server/api.go index e5fdfae9..036c11f2 100644 --- a/st/server/api.go +++ b/st/server/api.go @@ -533,6 +533,66 @@ func (s *Server) listBlockHandler(w http.ResponseWriter, r *http.Request) { s.apiWrap(w, r, 200, blocks) } +// listMacroHandler retuns a slice of the current macros operating in the sytem. +func (s *Server) listMacroHandler(w http.ResponseWriter, r *http.Request) { + s.manager.Mu.Lock() + defer s.manager.Mu.Unlock() + + macros, err := json.Marshal(s.manager.ListMacros()) + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + s.apiWrap(w, r, 200, macros) +} + +// createMacrosHandler asks the manager to create a macro and then return that +// macro if the macro has been creates. +func (s *Server) createMacroHandler(w http.ResponseWriter, r *http.Request) { + s.manager.Mu.Lock() + defer s.manager.Mu.Unlock() + + var macro *MacroInfo + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + + err = json.Unmarshal(body, ¯o) + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + + mmacro, err := s.manager.CreateMacro(macro) + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + + loghub.UI <- &loghub.LogMsg{ + Type: loghub.CREATE, + Data: mmacro, + Id: s.Id, + } + + loghub.Log <- &loghub.LogMsg{ + Type: loghub.CREATE, + Data: fmt.Sprintf("Macro %s", mmacro.Id), + Id: s.Id, + } + + jblock, err := json.Marshal(mmacro) + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + + s.apiWrap(w, r, 200, jblock) +} + // createBlockHandler asks the manager to create a block and then return that block // if the block has been creates. func (s *Server) createBlockHandler(w http.ResponseWriter, r *http.Request) { @@ -587,6 +647,53 @@ func (s *Server) createBlockHandler(w http.ResponseWriter, r *http.Request) { s.apiWrap(w, r, 200, jblock) } +// updateMacroHandler updates the coordinates of a macro. +func (s *Server) updateMacroHandler(w http.ResponseWriter, r *http.Request) { + s.manager.Mu.Lock() + defer s.manager.Mu.Unlock() + + var macro *MacroInfo + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + + err = json.Unmarshal(body, ¯o) + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + + mmacro, err := s.manager.UpdateMacro(macro) + + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + + jblock, err := json.Marshal(mmacro) + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + + loghub.Log <- &loghub.LogMsg{ + Type: loghub.UPDATE, + Data: fmt.Sprintf("Block %s", mmacro.Id), + Id: s.Id, + } + + loghub.UI <- &loghub.LogMsg{ + Type: loghub.UPDATE_POSITION, + Data: mmacro, + Id: s.Id, + } + + s.apiWrap(w, r, 200, jblock) +} + // updateBlockHandler updates the coordinates of a block. // block.id and block.type can't be changes. block.rule is set through sendRoute func (s *Server) updateBlockHandler(w http.ResponseWriter, r *http.Request) { @@ -657,6 +764,41 @@ func (s *Server) blockInfoHandler(w http.ResponseWriter, r *http.Request) { s.apiWrap(w, r, 200, jconn) } +// MacroInfoHandler returns a macro given an id +func (s *Server) MacroInfoHandler(w http.ResponseWriter, r *http.Request) { + s.manager.Mu.Lock() + defer s.manager.Mu.Unlock() + + vars := mux.Vars(r) + + conn, err := s.manager.GetMacro(vars["id"]) + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + + jconn, err := json.Marshal(conn) + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + s.apiWrap(w, r, 200, jconn) +} + +// deleteMacroHandler asks the block manager to delete a macro. +func (s *Server) deleteMacroHandler(w http.ResponseWriter, r *http.Request) { + s.manager.Mu.Lock() + defer s.manager.Mu.Unlock() + + vars := mux.Vars(r) + _, err := s.manager.DeleteMacro(vars["id"]) + if err != nil { + s.apiWrap(w, r, 500, s.response(err.Error())) + return + } + s.apiWrap(w, r, 200, s.response("OK")) +} + // deleteBlockHandler asks the block manager to delete a block. func (s *Server) deleteBlockHandler(w http.ResponseWriter, r *http.Request) { s.manager.Mu.Lock() @@ -1063,6 +1205,13 @@ func (s *Server) Run() { r.HandleFunc("/import", s.importHandler).Methods("POST") r.HandleFunc("/import", s.optionsHandler).Methods("OPTIONS") r.HandleFunc("/export", s.exportHandler).Methods("GET") + + r.HandleFunc("/macros", s.listMacroHandler).Methods("GET") // list all blocks + r.HandleFunc("/macros", s.createMacroHandler).Methods("POST") // create block w/o id + r.HandleFunc("/macros/{id}", s.MacroInfoHandler).Methods("GET") // get block info + r.HandleFunc("/macros/{id}", s.updateMacroHandler).Methods("PUT") // update block + r.HandleFunc("/macros/{id}", s.deleteMacroHandler).Methods("DELETE") // delete block + r.HandleFunc("/blocks", s.listBlockHandler).Methods("GET") // list all blocks r.HandleFunc("/blocks", s.createBlockHandler).Methods("POST") // create block w/o id r.HandleFunc("/blocks", s.optionsHandler).Methods("OPTIONS") // allow cross-domain diff --git a/st/server/blockmanager.go b/st/server/blockmanager.go index 500cca49..ae60a922 100644 --- a/st/server/blockmanager.go +++ b/st/server/blockmanager.go @@ -3,12 +3,14 @@ package server import ( "errors" "fmt" - "github.com/nytlabs/streamtools/st/blocks" - "github.com/nytlabs/streamtools/st/library" "net/url" "strconv" "sync" "time" + + "github.com/nytlabs/streamtools/st/blocks" + "github.com/nytlabs/streamtools/st/library" + "github.com/nytlabs/streamtools/st/macros" ) type BlockInfo struct { @@ -27,14 +29,29 @@ type ConnectionInfo struct { chans blocks.BlockChans } +type MacroInfo struct { + Id string + Type string + Dimensions *Dims + Content string +} + type Coords struct { X float64 Y float64 } +type Dims struct { + X float64 + Y float64 + W float64 + H float64 +} + type BlockManager struct { blockMap map[string]*BlockInfo connMap map[string]*ConnectionInfo + macroMap map[string]*MacroInfo genId chan string Mu *sync.Mutex } @@ -54,6 +71,7 @@ func NewBlockManager() *BlockManager { return &BlockManager{ blockMap: make(map[string]*BlockInfo), connMap: make(map[string]*ConnectionInfo), + macroMap: make(map[string]*MacroInfo), genId: idChan, Mu: &sync.Mutex{}, } @@ -72,7 +90,8 @@ func (b *BlockManager) GetId() string { func (b *BlockManager) IdExists(id string) bool { _, okB := b.blockMap[id] _, okC := b.connMap[id] - return okB || okC + _, okD := b.macroMap[id] + return okB || okC || okD } func (b *BlockManager) IdSafe(id string) bool { @@ -156,6 +175,15 @@ func (b *BlockManager) UpdateBlock(id string, coord *Coords) (*BlockInfo, error) return block, nil } +func (b *BlockManager) UpdateMacro(macroInfo *MacroInfo) (*MacroInfo, error) { + _, ok := b.macroMap[macroInfo.Id] + if !ok { + return nil, errors.New(fmt.Sprintf("Cannot update macro %s: does not exist", macroInfo.Id)) + } + b.macroMap[macroInfo.Id] = macroInfo + return macroInfo, nil +} + func (b *BlockManager) Send(id string, route string, msg interface{}) error { _, ok := b.blockMap[id] if !ok { @@ -297,6 +325,37 @@ func (b *BlockManager) Connect(connInfo *ConnectionInfo) (*ConnectionInfo, error return connInfo, nil } +func (b *BlockManager) CreateMacro(macroInfo *MacroInfo) (*MacroInfo, error) { + if macroInfo == nil { + return nil, errors.New("Cannot create: no macro data.") + } + + // check to see if the ID is OK + if !b.IdSafe(macroInfo.Id) { + return nil, errors.New(fmt.Sprintf("Cannot create macro %s: invalid id", macroInfo.Id)) + } + + // create ID if there is none + if macroInfo.Id == "" { + macroInfo.Id = b.GetId() + } + + // make sure ID doesn't already exist + if b.IdExists(macroInfo.Id) { + return nil, errors.New(fmt.Sprintf("Cannot create macro %s: id already exists", macroInfo.Id)) + } + + content, ok := macros.MacroDefs[macroInfo.Type] + if !ok { + return nil, errors.New("requested macro does not exist in macros library") + } + macroInfo.Content = string(content) + + b.macroMap[macroInfo.Id] = macroInfo + + return macroInfo, nil +} + func (b *BlockManager) GetSocket(fromId string) (chan *blocks.Msg, string, error) { _, ok := b.blockMap[fromId] if !ok { @@ -352,6 +411,15 @@ func (b *BlockManager) GetBlock(id string) (*BlockInfo, error) { return block, nil } +func (b *BlockManager) GetMacro(id string) (*MacroInfo, error) { + macro, ok := b.macroMap[id] + if !ok { + return nil, errors.New(fmt.Sprintf("Cannot get macro %s: does not exist", id)) + } + + return macro, nil +} + func (b *BlockManager) GetConnection(id string) (*ConnectionInfo, error) { _, ok := b.connMap[id] if !ok { @@ -416,6 +484,17 @@ func (b *BlockManager) DeleteConnection(id string) (string, error) { return id, nil } +func (b *BlockManager) DeleteMacro(id string) (string, error) { + _, ok := b.macroMap[id] + if !ok { + return "", errors.New(fmt.Sprintf("Cannot delete macro %s: does not exist", id)) + } + + delete(b.macroMap, id) + + return id, nil +} + func (b *BlockManager) StatusBlocks() []string { var wg sync.WaitGroup MsgChan := make(chan string, len(b.blockMap)) @@ -470,3 +549,13 @@ func (b *BlockManager) ListConnections() []*ConnectionInfo { } return conns } + +func (b *BlockManager) ListMacros() []*MacroInfo { + i := 0 + macros := make([]*MacroInfo, len(b.macroMap), len(b.macroMap)) + for _, v := range b.macroMap { + macros[i] = v + i++ + } + return macros +}