Adds godoc to api package

This commit is contained in:
Javier Cervantes 2014-10-15 12:57:07 -05:00
parent 6d2e25b0d8
commit ad96293edb
10 changed files with 141 additions and 16 deletions

View File

@ -9,6 +9,7 @@ type Adaptor struct {
adaptorType string
}
// AdaptorInterface defines behaviour expected for a Gobot Adaptor
type AdaptorInterface interface {
Finalize() bool
Connect() bool
@ -22,6 +23,7 @@ type AdaptorInterface interface {
ToJSON() *JSONConnection
}
// NewAdaptor returns a new Gobot Adaptor
func NewAdaptor(name string, adaptorType string, v ...interface{}) *Adaptor {
if name == "" {
name = fmt.Sprintf("%X", Rand(int(^uint(0)>>1)))
@ -43,34 +45,42 @@ func NewAdaptor(name string, adaptorType string, v ...interface{}) *Adaptor {
return a
}
// Port returns adaptor port
func (a *Adaptor) Port() string {
return a.port
}
// SetPort sets adaptor port
func (a *Adaptor) SetPort(s string) {
a.port = s
}
// Name returns adaptor name
func (a *Adaptor) Name() string {
return a.name
}
// SetName sets adaptor name
func (a *Adaptor) SetName(s string) {
a.name = s
}
// Type returns adaptor type
func (a *Adaptor) Type() string {
return a.adaptorType
}
// Connected returns true if adaptor is connected
func (a *Adaptor) Connected() bool {
return a.connected
}
// SetConnected sets adaptor as connected/disconnected
func (a *Adaptor) SetConnected(b bool) {
a.connected = b
}
// ToJSON returns a json representation of adaptor
func (a *Adaptor) ToJSON() *JSONConnection {
return &JSONConnection{
Name: a.Name(),

View File

@ -12,8 +12,6 @@ import (
"github.com/hybridgroup/gobot/api/robeaux"
)
// Optional restful API through Gobot has access
// all the robots.
type api struct {
gobot *gobot.Gobot
router *pat.PatternServeMux
@ -25,6 +23,8 @@ type api struct {
start func(*api)
}
// newAPI returns a gobot api instance
// and starts a http server using configuration options
func NewAPI(g *gobot.Gobot) *api {
return &api{
gobot: g,
@ -47,6 +47,7 @@ func NewAPI(g *gobot.Gobot) *api {
}
}
// ServeHTTP calls api handlers and then serves request using api router
func (a *api) ServeHTTP(res http.ResponseWriter, req *http.Request) {
for _, handler := range a.handlers {
handler(res, req)
@ -54,38 +55,43 @@ func (a *api) ServeHTTP(res http.ResponseWriter, req *http.Request) {
a.router.ServeHTTP(res, req)
}
// Post wraps api router Post call
func (a *api) Post(path string, f func(http.ResponseWriter, *http.Request)) {
a.router.Post(path, http.HandlerFunc(f))
}
// Put wraps api router Put call
func (a *api) Put(path string, f func(http.ResponseWriter, *http.Request)) {
a.router.Put(path, http.HandlerFunc(f))
}
// Delete wraps api router Delete call
func (a *api) Delete(path string, f func(http.ResponseWriter, *http.Request)) {
a.router.Del(path, http.HandlerFunc(f))
}
// Options wraps api router Options call
func (a *api) Options(path string, f func(http.ResponseWriter, *http.Request)) {
a.router.Options(path, http.HandlerFunc(f))
}
// Get wraps api router Get call
func (a *api) Get(path string, f func(http.ResponseWriter, *http.Request)) {
a.router.Get(path, http.HandlerFunc(f))
}
// Head wraps api router Head call
func (a *api) Head(path string, f func(http.ResponseWriter, *http.Request)) {
a.router.Head(path, http.HandlerFunc(f))
}
// AddHandler appends handler to api handlers
func (a *api) AddHandler(f func(http.ResponseWriter, *http.Request)) {
a.handlers = append(a.handlers, f)
}
// start starts the api using the start function
// sets on the API on initialization.
// Start initializes the api by setting up c3pio routes and robeaux
func (a *api) Start() {
// api
mcpCommandRoute := "/api/commands/:command"
deviceCommandRoute := "/api/robots/:robot/devices/:device/commands/:command"
robotCommandRoute := "/api/robots/:robot/commands/:command"
@ -108,7 +114,6 @@ func (a *api) Start() {
a.Get("/api/robots/:robot/connections/:connection", a.robotConnection)
a.Get("/api/", a.mcp)
// robeaux
a.Get("/", func(res http.ResponseWriter, req *http.Request) {
http.Redirect(res, req, "/index.html", http.StatusMovedPermanently)
})
@ -125,6 +130,8 @@ func (a *api) Start() {
a.start(a)
}
// robeaux returns handler for robeaux routes.
// Writes asset in response and sets correct header
func (a *api) robeaux(res http.ResponseWriter, req *http.Request) {
path := req.URL.Path
buf, err := robeaux.Asset(path[1:])
@ -141,14 +148,20 @@ func (a *api) robeaux(res http.ResponseWriter, req *http.Request) {
res.Write(buf)
}
// mcp returns MCP route handler.
// Writes JSON with gobot representation
func (a *api) mcp(res http.ResponseWriter, req *http.Request) {
a.writeJSON(map[string]interface{}{"MCP": a.gobot.ToJSON()}, res)
}
// mcpCommands returns commands route handler.
// Writes JSON with global commands representation
func (a *api) mcpCommands(res http.ResponseWriter, req *http.Request) {
a.writeJSON(map[string]interface{}{"commands": a.gobot.ToJSON().Commands}, res)
}
// robots returns route handler.
// Writes JSON with robots representation
func (a *api) robots(res http.ResponseWriter, req *http.Request) {
jsonRobots := []*gobot.JSONRobot{}
a.gobot.Robots().Each(func(r *gobot.Robot) {
@ -157,14 +170,20 @@ func (a *api) robots(res http.ResponseWriter, req *http.Request) {
a.writeJSON(map[string]interface{}{"robots": jsonRobots}, res)
}
// robot returns route handler.
// Writes JSON with robot representation
func (a *api) robot(res http.ResponseWriter, req *http.Request) {
a.writeJSON(map[string]interface{}{"robot": a.gobot.Robot(req.URL.Query().Get(":robot")).ToJSON()}, res)
}
// robotCommands returns commands route handler
// Writes JSON with robot commands representation
func (a *api) robotCommands(res http.ResponseWriter, req *http.Request) {
a.writeJSON(map[string]interface{}{"commands": a.gobot.Robot(req.URL.Query().Get(":robot")).ToJSON().Commands}, res)
}
// robotDevices returns devices route handler.
// Writes JSON with robot devices representation
func (a *api) robotDevices(res http.ResponseWriter, req *http.Request) {
jsonDevices := []*gobot.JSONDevice{}
a.gobot.Robot(req.URL.Query().Get(":robot")).Devices().Each(func(d gobot.Device) {
@ -173,6 +192,8 @@ func (a *api) robotDevices(res http.ResponseWriter, req *http.Request) {
a.writeJSON(map[string]interface{}{"devices": jsonDevices}, res)
}
// robotDevice returns device route handler.
// Writes JSON with robot device representation
func (a *api) robotDevice(res http.ResponseWriter, req *http.Request) {
a.writeJSON(
map[string]interface{}{"device": a.gobot.Robot(req.URL.Query().Get(":robot")).
@ -180,6 +201,9 @@ func (a *api) robotDevice(res http.ResponseWriter, req *http.Request) {
)
}
// robotDeviceEvent returns device event route handler.
// Creates an event stream connection
// and queries event data to be written when received
func (a *api) robotDeviceEvent(res http.ResponseWriter, req *http.Request) {
f, _ := res.(http.Flusher)
c, _ := res.(http.CloseNotifier)
@ -210,6 +234,8 @@ func (a *api) robotDeviceEvent(res http.ResponseWriter, req *http.Request) {
}
}
// robotDeviceCommands returns device commands route handler
// writes JSON with robot device commands representation
func (a *api) robotDeviceCommands(res http.ResponseWriter, req *http.Request) {
a.writeJSON(
map[string]interface{}{"commands": a.gobot.Robot(req.URL.Query().Get(":robot")).
@ -217,6 +243,8 @@ func (a *api) robotDeviceCommands(res http.ResponseWriter, req *http.Request) {
)
}
// robotConnections returns connections route handler
// writes JSON with robot connections representation
func (a *api) robotConnections(res http.ResponseWriter, req *http.Request) {
jsonConnections := []*gobot.JSONConnection{}
a.gobot.Robot(req.URL.Query().Get(":robot")).Connections().Each(func(c gobot.Connection) {
@ -225,6 +253,8 @@ func (a *api) robotConnections(res http.ResponseWriter, req *http.Request) {
a.writeJSON(map[string]interface{}{"connections": jsonConnections}, res)
}
// robotConnection returns connection route handler
// writes JSON with robot connection representation
func (a *api) robotConnection(res http.ResponseWriter, req *http.Request) {
a.writeJSON(
map[string]interface{}{"connection": a.gobot.Robot(req.URL.Query().Get(":robot")).
@ -233,6 +263,7 @@ func (a *api) robotConnection(res http.ResponseWriter, req *http.Request) {
)
}
// executeMcpCommand calls a global command asociated to requested route
func (a *api) executeMcpCommand(res http.ResponseWriter, req *http.Request) {
a.executeCommand(a.gobot.Command(req.URL.Query().Get(":command")),
res,
@ -240,6 +271,7 @@ func (a *api) executeMcpCommand(res http.ResponseWriter, req *http.Request) {
)
}
// executeDeviceCommand calls a device command asociated to requested route
func (a *api) executeDeviceCommand(res http.ResponseWriter, req *http.Request) {
a.executeCommand(
a.gobot.Robot(req.URL.Query().Get(":robot")).
@ -250,6 +282,7 @@ func (a *api) executeDeviceCommand(res http.ResponseWriter, req *http.Request) {
)
}
// executeRobotCommand calls a robot command asociated to requested route
func (a *api) executeRobotCommand(res http.ResponseWriter, req *http.Request) {
a.executeCommand(
a.gobot.Robot(req.URL.Query().Get(":robot")).
@ -259,6 +292,7 @@ func (a *api) executeRobotCommand(res http.ResponseWriter, req *http.Request) {
)
}
// executeCommand writes JSON response with `f` returned value.
func (a *api) executeCommand(f func(map[string]interface{}) interface{},
res http.ResponseWriter,
req *http.Request,
@ -274,12 +308,14 @@ func (a *api) executeCommand(f func(map[string]interface{}) interface{},
}
}
// writeJSON writes `j` as JSON in response
func (a *api) writeJSON(j interface{}, res http.ResponseWriter) {
data, _ := json.Marshal(j)
res.Header().Set("Content-Type", "application/json; charset=utf-8")
res.Write(data)
}
// Debug add handler to api that prints each request
func (a *api) Debug() {
a.AddHandler(func(res http.ResponseWriter, req *http.Request) {
log.Println(req)

View File

@ -6,9 +6,9 @@ import (
"net/http"
)
// BasicAuth returns basic auth handler.
// Inspired by https://github.com/codegangsta/martini-contrib/blob/master/auth/
func BasicAuth(username, password string) http.HandlerFunc {
// basic auth inspired by
// https://github.com/codegangsta/martini-contrib/blob/master/auth/
return func(res http.ResponseWriter, req *http.Request) {
if !secureCompare(req.Header.Get("Authorization"),
"Basic "+base64.StdEncoding.EncodeToString([]byte(username+":"+password)),

View File

@ -14,6 +14,7 @@ type CORS struct {
allowOriginPatterns []string
}
// AllowRequestFrom returns handler to verify that requests come from allowedOrigins
func AllowRequestsFrom(allowedOrigins ...string) http.HandlerFunc {
c := &CORS{
AllowOrigins: allowedOrigins,
@ -35,6 +36,7 @@ func AllowRequestsFrom(allowedOrigins ...string) http.HandlerFunc {
}
}
// isOriginAllowed returns true if origin matches an allowed origin pattern.
func (c *CORS) isOriginAllowed(origin string) (allowed bool) {
for _, allowedOriginPattern := range c.allowOriginPatterns {
allowed, _ = regexp.MatchString(allowedOriginPattern, origin)
@ -45,6 +47,7 @@ func (c *CORS) isOriginAllowed(origin string) (allowed bool) {
return
}
// generatePatterns generates regex expresions for AllowOrigins
func (c *CORS) generatePatterns() {
if c.AllowOrigins != nil {
for _, origin := range c.AllowOrigins {
@ -56,10 +59,12 @@ func (c *CORS) generatePatterns() {
}
}
// AllowedHeaders returns allowed headers in a string
func (c *CORS) AllowedHeaders() string {
return strings.Join(c.AllowHeaders, ",")
}
// AllowedMethods returns allowed http methods in a string
func (c *CORS) AllowedMethods() string {
return strings.Join(c.AllowMethods, ",")
}

40
api/doc.go Normal file
View File

@ -0,0 +1,40 @@
/*
Package api provides functionally to expose your gobot programs
to other by using starting a web server and adding commands.
Example:
package main
import (
"fmt"
"github.com/hybridgroup/gobot"
"github.com/hybridgroup/gobot/api"
)
func main() {
gbot := gobot.NewGobot()
// Starts the API server on default port 3000
api.NewAPI(gbot).Start()
// Accessible via http://localhost:3000/api/commands/say_hello
gbot.AddCommand("say_hello", func(params map[string]interface{}) interface{} {
return "Master says hello!"
})
hello := gbot.AddRobot(gobot.NewRobot("Eve"))
// Accessible via http://localhost:3000/robots/Eve/commands/say_hello
hello.AddCommand("say_hello", func(params map[string]interface{}) interface{} {
return fmt.Sprintf("%v says hello!", hello.Name)
})
gbot.Start()
}
It follows Common Protocol for Programming Physical Input and Output (CPPP-IO) spec:
https://github.com/hybridgroup/cppp-io
*/
package api

View File

@ -5,6 +5,7 @@ import (
"log"
)
// JSONConnection holds a JSON representation of a connection.
type JSONConnection struct {
Name string `json:"name"`
Adaptor string `json:"adaptor"`
@ -14,17 +15,19 @@ type Connection AdaptorInterface
type connections []Connection
// Len returns connections length
func (c *connections) Len() int {
return len(*c)
}
// Each calls function for each connection
func (c *connections) Each(f func(Connection)) {
for _, connection := range *c {
f(connection)
}
}
// Start() starts all the connections.
// Start initializes all the connections.
func (c *connections) Start() error {
var err error
log.Println("Starting connections...")
@ -42,7 +45,7 @@ func (c *connections) Start() error {
return err
}
// Finalize() finalizes all the connections.
// Finalize finishes all the connections.
func (c *connections) Finalize() {
for _, connection := range *c {
connection.Finalize()

View File

@ -5,6 +5,7 @@ import (
"log"
)
// JSONDevice is a JSON representation of a Gobot Device.
type JSONDevice struct {
Name string `json:"name"`
Driver string `json:"driver"`
@ -16,17 +17,19 @@ type Device DriverInterface
type devices []Device
// Len returns devices length
func (d *devices) Len() int {
return len(*d)
}
// Each calls `f` function each device
func (d *devices) Each(f func(Device)) {
for _, device := range *d {
f(device)
}
}
// Start() starts all the devices.
// Start starts all the devices.
func (d *devices) Start() error {
var err error
log.Println("Starting devices...")
@ -44,7 +47,7 @@ func (d *devices) Start() error {
return err
}
// Halt() stop all the devices.
// Halt stop all the devices.
func (d *devices) Halt() {
for _, device := range *d {
device.Halt()

View File

@ -5,6 +5,7 @@ import (
"time"
)
// DriverInterface defines Driver expected behaviour
type DriverInterface interface {
Start() bool
Halt() bool
@ -35,6 +36,8 @@ type Driver struct {
driverType string
}
// NewDriver returns a Driver with specified parameters
// and sets driver pin, adaptor and interval
func NewDriver(name string, driverType string, v ...interface{}) *Driver {
if name == "" {
name = fmt.Sprintf("%X", Rand(int(^uint(0)>>1)))
@ -64,42 +67,52 @@ func NewDriver(name string, driverType string, v ...interface{}) *Driver {
return d
}
// Adaptor returns driver adaptor
func (d *Driver) Adaptor() AdaptorInterface {
return d.adaptor
}
// SetInterval defines driver interval duration.
func (d *Driver) SetInterval(t time.Duration) {
d.interval = t
}
// Interval current driver interval duration
func (d *Driver) Interval() time.Duration {
return d.interval
}
// SetName sets driver name.
func (d *Driver) SetName(s string) {
d.name = s
}
// Name returns driver name.
func (d *Driver) Name() string {
return d.name
}
// Pin returns driver pin
func (d *Driver) Pin() string {
return d.pin
}
// SetPin defines driver pin
func (d *Driver) SetPin(pin string) {
d.pin = pin
}
// Type returns driver type
func (d *Driver) Type() string {
return d.driverType
}
// Events returns driver events map
func (d *Driver) Events() map[string]*Event {
return d.events
}
// Event returns an event by name if exists
func (d *Driver) Event(name string) *Event {
e, ok := d.events[name]
if ok {
@ -109,22 +122,27 @@ func (d *Driver) Event(name string) *Event {
}
}
// AddEvents adds a new event by name
func (d *Driver) AddEvent(name string) {
d.events[name] = NewEvent()
}
// Command retrieves a command by name
func (d *Driver) Command(name string) func(map[string]interface{}) interface{} {
return d.commands[name]
}
// Commands returns a map of driver commands
func (d *Driver) Commands() map[string]func(map[string]interface{}) interface{} {
return d.commands
}
// AddCommand links specified command name to `f`
func (d *Driver) AddCommand(name string, f func(map[string]interface{}) interface{}) {
d.commands[name] = f
}
// ToJSON returns JSON Driver represnentation including adaptor and commands
func (d *Driver) ToJSON() *JSONDevice {
jsonDevice := &JSONDevice{
Name: d.Name(),

View File

@ -10,6 +10,8 @@ type Event struct {
Callbacks []callback
}
// NewEvent generates a new event by making a channel
// and start reading from it
func NewEvent() *Event {
e := &Event{
Chan: make(chan interface{}, 1),
@ -23,6 +25,7 @@ func NewEvent() *Event {
return e
}
// Writes sends event data to channel
func (e *Event) Write(data interface{}) {
select {
case e.Chan <- data:
@ -30,6 +33,8 @@ func (e *Event) Write(data interface{}) {
}
}
// Read waits data from channel and execute callbacks
// for each event when received
func (e *Event) Read() {
for s := range e.Chan {
tmp := []callback{}

View File

@ -10,13 +10,10 @@ import (
// Every triggers f every `t` time until the end of days.
func Every(t time.Duration, f func()) {
c := time.Tick(t)
// start a go routine to not bloc the function
go func() {
for {
// wait for the ticker to tell us to run
<-c
// run the passed function in another go routine
// so we don't slow down the loop.
go f()
}
}()
@ -27,27 +24,35 @@ func After(t time.Duration, f func()) {
time.AfterFunc(t, f)
}
// Publish emits an event by writting value
func Publish(e *Event, val interface{}) {
e.Write(val)
}
// On adds `f` to callbacks that are executed on specified event
func On(e *Event, f func(s interface{})) {
e.Callbacks = append(e.Callbacks, callback{f, false})
}
// Once adds `f` to callbacks that are executed on specified event
// and sets flag to be called only once
func Once(e *Event, f func(s interface{})) {
e.Callbacks = append(e.Callbacks, callback{f, true})
}
// Rand generates random int lower than max
func Rand(max int) int {
i, _ := rand.Int(rand.Reader, big.NewInt(int64(max)))
return int(i.Int64())
}
// FromScale creates a scale using min and max values
// to be used in combination with ToScale
func FromScale(input, min, max float64) float64 {
return (input - math.Min(min, max)) / (math.Max(min, max) - math.Min(min, max))
}
// ToScale is used with FromScale to return input converted to new scale
func ToScale(input, min, max float64) float64 {
i := input*(math.Max(min, max)-math.Min(min, max)) + math.Min(min, max)
if i < math.Min(min, max) {