Last active
February 27, 2018 04:13
-
-
Save mrcrilly/f453e1f274bada2476357410b1a7fa8a to your computer and use it in GitHub Desktop.
This is why I think an FSM is a good idea on APIs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"net/http" | |
"github.com/julienschmidt/httprouter" | |
"github.com/looplab/fsm" | |
) | |
func newHouse(name, state string) *house { | |
h := &house{ | |
name: name, | |
} | |
h.forSale = (state == "forsale") | |
h.fsm = fsm.NewFSM( | |
state, | |
fsm.Events{ | |
{ | |
Name: "buy", | |
Src: []string{"forsale"}, | |
Dst: "owned", | |
}, | |
{ | |
Name: "sell", | |
Src: []string{"owned"}, | |
Dst: "forsale", | |
}, | |
}, | |
fsm.Callbacks{ | |
"sell": sell, | |
"buy": buy, | |
}, | |
) | |
return h | |
} | |
type database struct { | |
houses map[string]*house | |
} | |
func (d *database) get(name string) *house { return d.houses[name] } | |
func (d *database) upsert(name string, h *house) { d.houses[name] = h } | |
var db = &database{houses: map[string]*house{}} | |
type house struct { | |
name string // poor man's address | |
price float64 | |
forSale bool | |
fsm *fsm.FSM // keep track of our state | |
} | |
func sell(e *fsm.Event) { | |
name := e.Args[0].(string) | |
h := db.get(name) | |
h.forSale = true | |
db.upsert(name, h) | |
} | |
func buy(e *fsm.Event) { | |
name := e.Args[0].(string) | |
h := db.get(name) | |
h.forSale = false | |
db.upsert(name, h) | |
} | |
func getHouseByName(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { | |
h := db.get(ps.ByName("name")) | |
fmt.Fprintf(w, "house for sale: %v\n", h.forSale) | |
} | |
func sellHouseByName(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { | |
h := db.get(ps.ByName("name")) | |
err := h.fsm.Event("sell", ps.ByName("name")) | |
if err != nil { | |
fmt.Fprintf(w, "unable to sell house: %s\n", err.Error()) | |
return | |
} | |
fmt.Fprintf(w, "house marked for sale\n") | |
} | |
func buyHouseByName(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { | |
h := db.get(ps.ByName("name")) | |
err := h.fsm.Event("buy", ps.ByName("name")) | |
if err != nil { | |
fmt.Fprintf(w, "unable to buy house: %s\n", err.Error()) | |
return | |
} | |
fmt.Fprintf(w, "congrats on the house\n") | |
} | |
func main() { | |
db.upsert("mike", newHouse("mike", "forsale")) | |
db.upsert("taylor", newHouse("taylor", "owned")) | |
router := httprouter.New() | |
router.GET("/house/:name", getHouseByName) | |
router.GET("/house/:name/sell", sellHouseByName) | |
router.GET("/house/:name/buy", buyHouseByName) | |
log.Fatal(http.ListenAndServe(":8080", router)) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ curl -sXGET http://localhost:8080/house/taylor | |
house for sale: false | |
$ curl -sXGET http://localhost:8080/house/taylor/sell | |
house marked for sale | |
$ curl -sXGET http://localhost:8080/house/taylor | |
house for sale: true | |
$ curl -sXGET http://localhost:8080/house/taylor/buy | |
congrats on the house | |
$ curl -sXGET http://localhost:8080/house/taylor | |
house for sale: false | |
$ curl -sXGET http://localhost:8080/house/mike/sell | |
unable to sell house: event sell inappropriate in current state forsale | |
$ curl -sXGET http://localhost:8080/house/mike/sell | |
unable to sell house: event sell inappropriate in current state forsale | |
$ curl -sXGET http://localhost:8080/house/mike/buy | |
congrats on the house | |
$ curl -sXGET http://localhost:8080/house/mike/sell | |
house marked for sale | |
$ curl -sXGET http://localhost:8080/house/mike/buy | |
congrats on the house | |
$ curl -sXGET http://localhost:8080/house/mike/buy | |
unable to buy house: event buy inappropriate in current state owned |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment