major refactoring of the api
This commit is contained in:
1 parent
d4f7dc57fc
commit
f0628e4682
38 files changed
+808
-354
No files matched your search
@@ -1,144 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/didiercrunch/doormanserver/doormen"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"gopkg.in/mgo.v2/bson"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getDoormenIdsAsJson(id *doormen.DoormanId) string {
|
|
||||||
return fmt.Sprintf(`{"name": "%v", "id": "%v", "url": "/api/doormen/%v"}`, id.Name, id.Id.Hex(), id.Id.Hex())
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateDoorman(w http.ResponseWriter, request *http.Request) {
|
|
||||||
wdef := new(doormen.DoormanDefinition)
|
|
||||||
defer request.Body.Close()
|
|
||||||
decoder := json.NewDecoder(request.Body)
|
|
||||||
if err := decoder.Decode(wdef); err != nil {
|
|
||||||
w.WriteHeader(400)
|
|
||||||
fmt.Fprintln(w, "cannot create doorman definition \n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
wdef.Id = bson.NewObjectId()
|
|
||||||
if err := wdef.Validate(); err != nil {
|
|
||||||
w.WriteHeader(400)
|
|
||||||
fmt.Fprintln(w, "New doorman is invalid \n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := CreateDoormanInDatabase(wdef); err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
fmt.Fprintln(w, "Server error \n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go publisher.Emit(wdef.Id, wdef)
|
|
||||||
w.Header().Set("location", "/api/doormen/"+wdef.Id.Hex())
|
|
||||||
w.WriteHeader(201)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAllDoormen(w http.ResponseWriter, request *http.Request) {
|
|
||||||
fmt.Fprint(w, `{"doormen" :[`)
|
|
||||||
doormenIds := conn.GetAllDoormen()
|
|
||||||
buff := <-doormenIds
|
|
||||||
if buff == nil {
|
|
||||||
fmt.Fprint(w, "]}")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for doormanId := range doormenIds {
|
|
||||||
fmt.Fprint(w, getDoormenIdsAsJson(buff)+`,`)
|
|
||||||
buff = doormanId
|
|
||||||
}
|
|
||||||
fmt.Fprint(w, getDoormenIdsAsJson(buff), "]}")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDoormanIdFromRequest(w http.ResponseWriter, request *http.Request) bson.ObjectId {
|
|
||||||
id := mux.Vars(request)["id"]
|
|
||||||
if !bson.IsObjectIdHex(id) {
|
|
||||||
return ""
|
|
||||||
} else {
|
|
||||||
return bson.ObjectIdHex(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDoorman(w http.ResponseWriter, r *http.Request) {
|
|
||||||
id := getDoormanIdFromRequest(w, r)
|
|
||||||
if id == "" {
|
|
||||||
Write404Error(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
doorman, err := conn.GetDoorman(id)
|
|
||||||
if err != nil {
|
|
||||||
Write500Error(w, err)
|
|
||||||
} else if doorman == nil {
|
|
||||||
Write404Error(w)
|
|
||||||
} else {
|
|
||||||
encoder := json.NewEncoder(w)
|
|
||||||
encoder.Encode(doorman)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateDoorman(w http.ResponseWriter, request *http.Request) {
|
|
||||||
id := getDoormanIdFromRequest(w, request)
|
|
||||||
if id == "" || !conn.ExistsId(id) {
|
|
||||||
Write404Error(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
wdef := new(doormen.DoormanDefinition)
|
|
||||||
defer request.Body.Close()
|
|
||||||
decoder := json.NewDecoder(request.Body)
|
|
||||||
if err := decoder.Decode(wdef); err != nil {
|
|
||||||
Write400Error(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
wdef.Id = id
|
|
||||||
|
|
||||||
if err := wdef.Validate(); err != nil {
|
|
||||||
Write400Error(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
originalDoorman, err := conn.GetDoorman(id)
|
|
||||||
if err != nil {
|
|
||||||
Write500Error(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := originalDoorman.CanBeUpdatedBy(wdef); err != nil {
|
|
||||||
Write400Error(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := UpdateDoormanInDatabase(wdef); err != nil {
|
|
||||||
Write500Error(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go publisher.Emit(wdef.Id, wdef)
|
|
||||||
w.WriteHeader(200)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
type Api struct {
|
|
||||||
GetAllDoormen http.HandlerFunc
|
|
||||||
CreateDoorman http.HandlerFunc
|
|
||||||
GetDoorman http.HandlerFunc
|
|
||||||
UpdateDoorman http.HandlerFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Api) ServeApi(router *mux.Router) {
|
|
||||||
router.Path("/doormen").Methods("GET").HandlerFunc(a.GetAllDoormen)
|
|
||||||
router.Path("/doormen").Methods("POST").HandlerFunc(a.CreateDoorman)
|
|
||||||
router.Path("/doormen/{id}").Methods("GET").HandlerFunc(a.GetDoorman)
|
|
||||||
router.Path("/doormen/{id}").Methods("PUT").HandlerFunc(a.UpdateDoorman)
|
|
||||||
}
|
|
||||||
|
|
||||||
func serveApi(router *mux.Router) {
|
|
||||||
api := new(Api)
|
|
||||||
api.CreateDoorman = CreateDoorman
|
|
||||||
api.GetAllDoormen = GetAllDoormen
|
|
||||||
api.GetDoorman = GetDoorman
|
|
||||||
api.UpdateDoorman = UpdateDoorman
|
|
||||||
api.ServeApi(router)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetAllDoormen(w http.ResponseWriter, request *http.Request) {
|
||||||
|
fmt.Fprint(w, `{"doormen" :[`)
|
||||||
|
doormenIds := conn.GetAllDoormen()
|
||||||
|
buff := <-doormenIds
|
||||||
|
if buff == nil {
|
||||||
|
fmt.Fprint(w, "]}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for doormanId := range doormenIds {
|
||||||
|
fmt.Fprint(w, getDoormenIdsAsJson(buff)+`,`)
|
||||||
|
buff = doormanId
|
||||||
|
}
|
||||||
|
fmt.Fprint(w, getDoormenIdsAsJson(buff), "]}")
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/didiercrunch/doormanserver/doormen"
|
||||||
|
"github.com/didiercrunch/doormanserver/inmemoryconnector"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
conn = inmemoryconnector.NewInMemoryDBConnector()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAllDoormen(t *testing.T) {
|
||||||
|
conn = inmemoryconnector.NewInMemoryDBConnector()
|
||||||
|
conn.Save(doormen.QuickNewDoormanDefinition("foo", 0.25, 0.75))
|
||||||
|
conn.Save(doormen.QuickNewDoormanDefinition("bar", 0.5, 0.5))
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "http://bigtits.com/api/doormen", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
GetAllDoormen(w, req)
|
||||||
|
|
||||||
|
d := json.NewDecoder(w.Body)
|
||||||
|
m := make(map[string][]*doormen.DoormanDefinition)
|
||||||
|
|
||||||
|
if err := d.Decode(&m); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(m["doormen"]) != 2 {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAllDoormenEmpty(t *testing.T) {
|
||||||
|
conn = inmemoryconnector.NewInMemoryDBConnector()
|
||||||
|
req, err := http.NewRequest("GET", "http://bigtits.com/api/doormen", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
GetAllDoormen(w, req)
|
||||||
|
|
||||||
|
if s := w.Body.String(); s != `{"doormen" :[]}` {
|
||||||
|
t.Error(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/didiercrunch/doormanserver/simpleapi"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Create() http.Handler {
|
||||||
|
serverSpecification := &simpleapi.Endpoint{
|
||||||
|
"/server",
|
||||||
|
"Get the server specification.",
|
||||||
|
"GET",
|
||||||
|
GetServerSpecification,
|
||||||
|
}
|
||||||
|
|
||||||
|
createDoorman := &simpleapi.Endpoint{
|
||||||
|
"/doormen",
|
||||||
|
"Create a new doorman.",
|
||||||
|
"POST",
|
||||||
|
CreateDoorman,
|
||||||
|
}
|
||||||
|
|
||||||
|
allDoorman := &simpleapi.Endpoint{
|
||||||
|
"/doormen",
|
||||||
|
"Get the list of all the public doormen.",
|
||||||
|
"GET",
|
||||||
|
GetAllDoormen,
|
||||||
|
}
|
||||||
|
|
||||||
|
doorman := &simpleapi.Endpoint{
|
||||||
|
"/doormen/{id}",
|
||||||
|
"Get the doorman.",
|
||||||
|
"GET",
|
||||||
|
GetDoorman,
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDoorman := &simpleapi.Endpoint{
|
||||||
|
"/doormen/{id}",
|
||||||
|
"Update the doorman with new probabilities.",
|
||||||
|
"PUT",
|
||||||
|
UpdateDoorman,
|
||||||
|
}
|
||||||
|
|
||||||
|
api := simpleapi.New(
|
||||||
|
"/api",
|
||||||
|
serverSpecification,
|
||||||
|
createDoorman,
|
||||||
|
allDoorman,
|
||||||
|
doorman,
|
||||||
|
updateDoorman,
|
||||||
|
)
|
||||||
|
|
||||||
|
return api
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/didiercrunch/doormanserver/doormen"
|
||||||
|
"gopkg.in/mgo.v2/bson"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateDoorman(w http.ResponseWriter, request *http.Request) {
|
||||||
|
wdef := new(doormen.DoormanDefinition)
|
||||||
|
defer request.Body.Close()
|
||||||
|
decoder := json.NewDecoder(request.Body)
|
||||||
|
if err := decoder.Decode(wdef); err != nil {
|
||||||
|
w.WriteHeader(400)
|
||||||
|
fmt.Fprintln(w, "cannot create doorman definition \n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wdef.Id = bson.NewObjectId()
|
||||||
|
if err := wdef.Validate(); err != nil {
|
||||||
|
w.WriteHeader(400)
|
||||||
|
fmt.Fprintln(w, "New doorman is invalid \n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := CreateDoormanInDatabase(wdef); err != nil {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
fmt.Fprintln(w, "Server error \n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go publisher.Emit(wdef.Id, wdef)
|
||||||
|
w.Header().Set("location", "/api/doormen/"+wdef.Id.Hex())
|
||||||
|
w.WriteHeader(201)
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/didiercrunch/doormanserver/inmemoryconnector"
|
||||||
|
"gopkg.in/mgo.v2/bson"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetIdFromLocation(p string) bson.ObjectId {
|
||||||
|
return bson.ObjectIdHex(path.Base(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateDoorman(t *testing.T) {
|
||||||
|
conn = inmemoryconnector.NewInMemoryDBConnector()
|
||||||
|
payload := `{"name":"name","values":[{"name":"red","probability":0.2},{"name":"blue","probability":0.8}]}`
|
||||||
|
req, err := http.NewRequest(
|
||||||
|
"POST",
|
||||||
|
"http://bigtits.com/api/doormen",
|
||||||
|
bytes.NewBufferString(payload),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
CreateDoorman(w, req)
|
||||||
|
if w.Code != 201 {
|
||||||
|
t.Error("bad status code", w.Code)
|
||||||
|
} else if location := w.Header().Get("location"); location == "" {
|
||||||
|
t.Error("missing location header", location)
|
||||||
|
} else if dor, _ := conn.GetDoorman(GetIdFromLocation(location)); dor == nil {
|
||||||
|
t.Error("dorman not created")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateDoormanWithBadInput(t *testing.T) {
|
||||||
|
conn = inmemoryconnector.NewInMemoryDBConnector()
|
||||||
|
payload := `{"name":"name","values":[{"name":"red","probability":0.2},{"name":"blue","probability":0.2}]}`
|
||||||
|
req, err := http.NewRequest(
|
||||||
|
"POST",
|
||||||
|
"http://bigtits.com/api/doormen",
|
||||||
|
bytes.NewBufferString(payload),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
CreateDoorman(w, req)
|
||||||
|
if w.Code != 400 {
|
||||||
|
t.Error("bad status code", w.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
package main
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/didiercrunch/doormanserver/doormen"
|
||||||
"github.com/didiercrunch/doormanserver/inmemoryconnector"
|
"github.com/didiercrunch/doormanserver/inmemoryconnector"
|
||||||
"github.com/didiercrunch/doormanserver/mongoconnector"
|
"github.com/didiercrunch/doormanserver/mongoconnector"
|
||||||
"github.com/didiercrunch/doormanserver/shared"
|
"github.com/didiercrunch/doormanserver/shared"
|
||||||
"github.com/didiercrunch/doormanserver/doormen"
|
|
||||||
"gopkg.in/mgo.v2/bson"
|
"gopkg.in/mgo.v2/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
package main
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/didiercrunch/doormanserver/inmemoryconnector"
|
|
||||||
"github.com/didiercrunch/doormanserver/doormen"
|
"github.com/didiercrunch/doormanserver/doormen"
|
||||||
|
"github.com/didiercrunch/doormanserver/inmemoryconnector"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateDoorman(t *testing.T) {
|
func TestCreateDoormanInDatabase(t *testing.T) {
|
||||||
wab := doormen.NewDoormanDefinition("some_name")
|
wab := doormen.NewDoormanDefinition("some_name")
|
||||||
conn = inmemoryconnector.NewInMemoryDBConnector()
|
conn = inmemoryconnector.NewInMemoryDBConnector()
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetDoorman(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := getDoormanIdFromRequest(w, r)
|
||||||
|
if id == "" {
|
||||||
|
Write404Error(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
doorman, err := conn.GetDoorman(id)
|
||||||
|
if err != nil {
|
||||||
|
Write500Error(w, err)
|
||||||
|
} else if doorman == nil {
|
||||||
|
Write404Error(w)
|
||||||
|
} else {
|
||||||
|
encoder := json.NewEncoder(w)
|
||||||
|
encoder.Encode(doorman)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetDormanState(w http.ResponseWriter, request *http.Request) {
|
||||||
|
id := getDoormanIdFromRequest(w, request)
|
||||||
|
if id == "" {
|
||||||
|
Write404Error(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
doorman, err := conn.GetDoorman(id)
|
||||||
|
if err != nil {
|
||||||
|
Write500Error(w, err)
|
||||||
|
return
|
||||||
|
} else if doorman == nil {
|
||||||
|
Write404Error(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload, err := doorman.AsDoormanUpdatePayload(); err != nil {
|
||||||
|
Write500Error(w, err)
|
||||||
|
} else {
|
||||||
|
w.Header().Add("Content-Type", mime.TypeByExtension(".json"))
|
||||||
|
w.Write(payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
@@ -37,7 +37,6 @@ func initPublisher(params *shared.Params) {
|
|||||||
if params.UseNSQ() {
|
if params.UseNSQ() {
|
||||||
initNSQPublisher()
|
initNSQPublisher()
|
||||||
log.Println("using nsq as message queue")
|
log.Println("using nsq as message queue")
|
||||||
|
|
||||||
} else if params.UseNanomsg() {
|
} else if params.UseNanomsg() {
|
||||||
initNanoMsgPublisher()
|
initNanoMsgPublisher()
|
||||||
log.Println("using nanomsg as message queue")
|
log.Println("using nanomsg as message queue")
|
||||||
@@ -45,8 +44,8 @@ func initPublisher(params *shared.Params) {
|
|||||||
publisher = &logpublisher.LogPublisher{log.Println}
|
publisher = &logpublisher.LogPublisher{log.Println}
|
||||||
log.Println("using stdout as message queue")
|
log.Println("using stdout as message queue")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
initPublisher(shared.GetParams())
|
initPublisher(shared.GetParams())
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package api
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/didiercrunch/doormanserver/serverstat"
|
||||||
|
"github.com/didiercrunch/doormanserver/shared"
|
||||||
|
"log"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetServerSpecification(w http.ResponseWriter, request *http.Request) {
|
||||||
|
s := serverstat.Get(shared.GetParams())
|
||||||
|
w.Header().Add("Content-Type", mime.TypeByExtension(".json"))
|
||||||
|
encoder := json.NewEncoder(w)
|
||||||
|
if err := encoder.Encode(s); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/didiercrunch/doormanserver/doormen"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UpdateDoorman(w http.ResponseWriter, request *http.Request) {
|
||||||
|
id := getDoormanIdFromRequest(w, request)
|
||||||
|
if id == "" || !conn.ExistsId(id) {
|
||||||
|
Write404Error(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wdef := new(doormen.DoormanDefinition)
|
||||||
|
defer request.Body.Close()
|
||||||
|
decoder := json.NewDecoder(request.Body)
|
||||||
|
if err := decoder.Decode(wdef); err != nil {
|
||||||
|
Write400Error(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wdef.Id = id
|
||||||
|
|
||||||
|
if err := wdef.Validate(); err != nil {
|
||||||
|
Write400Error(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
originalDoorman, err := conn.GetDoorman(id)
|
||||||
|
if err != nil {
|
||||||
|
Write500Error(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := originalDoorman.CanBeUpdatedBy(wdef); err != nil {
|
||||||
|
Write400Error(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := UpdateDoormanInDatabase(wdef); err != nil {
|
||||||
|
Write500Error(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go publisher.Emit(wdef.Id, wdef)
|
||||||
|
w.WriteHeader(200)
|
||||||
|
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/didiercrunch/doormanserver/doormen"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"gopkg.in/mgo.v2/bson"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getDoormenIdsAsJson(id *doormen.DoormanId) string {
|
||||||
|
return fmt.Sprintf(`{"name": "%v", "id": "%v", "url": "/api/doormen/%v"}`, id.Name, id.Id.Hex(), id.Id.Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDoormanIdFromRequest(w http.ResponseWriter, request *http.Request) bson.ObjectId {
|
||||||
|
id := mux.Vars(request)["id"]
|
||||||
|
if !bson.IsObjectIdHex(id) {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return bson.ObjectIdHex(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Write500Error(w http.ResponseWriter, err error) {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
fmt.Fprint(w, "server error")
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Write404Error(w http.ResponseWriter) {
|
||||||
|
w.WriteHeader(404)
|
||||||
|
fmt.Fprint(w, "url not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Write400Error(w http.ResponseWriter, err error) {
|
||||||
|
w.WriteHeader(400)
|
||||||
|
fmt.Fprint(w, err.Error())
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
-115
@@ -1,115 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/didiercrunch/doormanserver/inmemoryconnector"
|
|
||||||
"github.com/didiercrunch/doormanserver/doormen"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
conn = inmemoryconnector.NewInMemoryDBConnector()
|
|
||||||
}
|
|
||||||
|
|
||||||
func OkHandler(w http.ResponseWriter, request *http.Request) {
|
|
||||||
fmt.Fprintf(w, "ok")
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertBodyIs(t *testing.T, res *http.Response, expt string) {
|
|
||||||
if body, err := ioutil.ReadAll(res.Body); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
} else if s := string(body); s != expt {
|
|
||||||
t.Error("expected ", expt, " but received ", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPOSTToDoormen(t *testing.T) {
|
|
||||||
api := &Api{CreateDoorman: OkHandler}
|
|
||||||
r := mux.NewRouter()
|
|
||||||
api.ServeApi(r)
|
|
||||||
|
|
||||||
ts := httptest.NewServer(r)
|
|
||||||
defer ts.Close()
|
|
||||||
res, err := http.Post(ts.URL+"/doormen", "", bytes.NewBufferString("boby"))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
assertBodyIs(t, res, "ok")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGETToDoormen(t *testing.T) {
|
|
||||||
api := &Api{GetAllDoormen: OkHandler}
|
|
||||||
|
|
||||||
r := mux.NewRouter()
|
|
||||||
api.ServeApi(r)
|
|
||||||
|
|
||||||
ts := httptest.NewServer(r)
|
|
||||||
defer ts.Close()
|
|
||||||
res, err := http.Get(ts.URL + "/doormen")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
assertBodyIs(t, res, "ok")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetAllDoormenEmpty(t *testing.T) {
|
|
||||||
conn = inmemoryconnector.NewInMemoryDBConnector()
|
|
||||||
api := &Api{GetAllDoormen: GetAllDoormen}
|
|
||||||
|
|
||||||
r := mux.NewRouter()
|
|
||||||
api.ServeApi(r)
|
|
||||||
|
|
||||||
ts := httptest.NewServer(r)
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
res, err := http.Get(ts.URL + "/doormen")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if body, err := ioutil.ReadAll(res.Body); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
} else if s := string(body); s != `{"doormen" :[]}` {
|
|
||||||
t.Error(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetAllDoormen(t *testing.T) {
|
|
||||||
conn = inmemoryconnector.NewInMemoryDBConnector()
|
|
||||||
conn.Save(doormen.QuickNewDoormanDefinition("foo", 0.25, 0.75))
|
|
||||||
conn.Save(doormen.QuickNewDoormanDefinition("bar", 0.5, 0.5))
|
|
||||||
|
|
||||||
api := &Api{GetAllDoormen: GetAllDoormen}
|
|
||||||
r := mux.NewRouter()
|
|
||||||
api.ServeApi(r)
|
|
||||||
|
|
||||||
ts := httptest.NewServer(r)
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
res, err := http.Get(ts.URL + "/doormen")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d := json.NewDecoder(res.Body)
|
|
||||||
m := make(map[string][]*doormen.DoormanDefinition)
|
|
||||||
|
|
||||||
if err := d.Decode(&m); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(m["doormen"]) != 2 {
|
|
||||||
t.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Write500Error(w http.ResponseWriter, err error) {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
fmt.Fprint(w, "server error")
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Write404Error(w http.ResponseWriter) {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
fmt.Fprint(w, "url not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Write400Error(w http.ResponseWriter, err error) {
|
|
||||||
w.WriteHeader(400)
|
|
||||||
fmt.Fprint(w, err.Error())
|
|
||||||
}
|
|
||||||
+2
-1
@@ -71,13 +71,14 @@ func (dmd *DoormanDefinition) ValidateDoormanValueNames() error {
|
|||||||
func (dmd *DoormanDefinition) ValidateDoormanValueProbabilities() error {
|
func (dmd *DoormanDefinition) ValidateDoormanValueProbabilities() error {
|
||||||
var sum float64 = 0
|
var sum float64 = 0
|
||||||
for _, w := range dmd.Values {
|
for _, w := range dmd.Values {
|
||||||
|
|
||||||
if p := w.Probability; p < 0-epsilon || p > 1+epsilon {
|
if p := w.Probability; p < 0-epsilon || p > 1+epsilon {
|
||||||
return errors.New(fmt.Sprintf("doorman value %v is out of range", p))
|
return errors.New(fmt.Sprintf("doorman value %v is out of range", p))
|
||||||
} else {
|
} else {
|
||||||
sum += p
|
sum += p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sum < 0-epsilon || sum > 1+epsilon {
|
if sum < 1-epsilon || sum > 1+epsilon {
|
||||||
return errors.New("the sum of the probability must be 1.0")
|
return errors.New("the sum of the probability must be 1.0")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ func TestValidate(t *testing.T) {
|
|||||||
if err := dmd.Validate(); err.Error() != "the sum of the probability must be 1.0" {
|
if err := dmd.Validate(); err.Error() != "the sum of the probability must be 1.0" {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0.2}, &DoormanValue{"bar", 0.2}}}
|
||||||
|
if err := dmd.Validate(); err.Error() != "the sum of the probability must be 1.0" {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAsJson(t *testing.T) {
|
func TestAsJson(t *testing.T) {
|
||||||
|
|||||||
@@ -2,32 +2,36 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/didiercrunch/doormanserver/api"
|
||||||
|
"github.com/didiercrunch/doormanserver/shared"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/didiercrunch/doormanserver/shared"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func createMuxRouter() http.Handler {
|
const STATIC_DIR_PATH = "static/"
|
||||||
r := mux.NewRouter()
|
|
||||||
serveApi(r.PathPrefix("/api").Subrouter())
|
|
||||||
serveStatic(r.PathPrefix("/").Subrouter())
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAddressToServe() string {
|
func GetAddressToServe() string {
|
||||||
params := shared.GetParams()
|
params := shared.GetParams()
|
||||||
return params.Host + ":" + strconv.Itoa(params.Port)
|
return params.Host + ":" + strconv.Itoa(params.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func serveStatic2(w http.ResponseWriter, request *http.Request) {
|
||||||
|
filepath := request.URL.Path
|
||||||
|
w.Header().Set("Cache-Control", "public, max-age=43200")
|
||||||
|
fmt.Println(STATIC_DIR_PATH, filepath, path.Join(STATIC_DIR_PATH, filepath))
|
||||||
|
http.ServeFile(w, request, path.Join(STATIC_DIR_PATH, filepath))
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
r := createMuxRouter()
|
mux := http.NewServeMux()
|
||||||
http.Handle("/", r)
|
mux.Handle("/api/", api.Create())
|
||||||
|
mux.HandleFunc("/", serveStatic2)
|
||||||
|
|
||||||
address := GetAddressToServe()
|
address := GetAddressToServe()
|
||||||
fmt.Println("server running at", address)
|
fmt.Println("server running at", address)
|
||||||
if err := http.ListenAndServe(address, createMuxRouter()); err != nil {
|
if err := http.ListenAndServe(address, mux); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package serverstat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/didiercrunch/doormanserver/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
type m map[string]interface{}
|
||||||
|
|
||||||
|
var GO_VERSION = runtime.Version()
|
||||||
|
|
||||||
|
type ServerStat struct {
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
Go string `json:"go_version"`
|
||||||
|
|
||||||
|
MessageQueue string `json:"message_queue"`
|
||||||
|
NSQ m `json:"nsq,omitempty"`
|
||||||
|
Nanomsg m `json:"nano_msg,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Get(p *shared.Params) *ServerStat {
|
||||||
|
r := new(ServerStat)
|
||||||
|
|
||||||
|
r.Hostname = p.Host
|
||||||
|
r.Port = p.Port
|
||||||
|
r.Go = GO_VERSION
|
||||||
|
r.MessageQueue = strings.ToLower(p.MessageQueue)
|
||||||
|
if p.UseNSQ() {
|
||||||
|
r.NSQ = m{"ns_lookup_url": p.NSQ.NSQLookupdUrl}
|
||||||
|
|
||||||
|
}
|
||||||
|
if p.UseNanomsg() {
|
||||||
|
r.Nanomsg = m{"url": p.NanoMsg.NanoMsgUrl}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package serverstat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
import "github.com/didiercrunch/doormanserver/shared"
|
||||||
|
|
||||||
|
func deserializeServerStats(data []byte) *ServerStat {
|
||||||
|
ret := new(ServerStat)
|
||||||
|
if err := json.Unmarshal(data, ret); err != nil {
|
||||||
|
panic("cannot deserialize data")
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServerStatsEqual(a, b *ServerStat) bool {
|
||||||
|
return reflect.DeepEqual(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetTrivial(t *testing.T) {
|
||||||
|
p := new(shared.Params)
|
||||||
|
expected := &ServerStat{Go: GO_VERSION}
|
||||||
|
if s := Get(p); !ServerStatsEqual(s, expected) {
|
||||||
|
t.Error(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package simpleapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
func JSONHendler(handler http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, request *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", mime.TypeByExtension(".json"))
|
||||||
|
handler(w, request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Endpoint struct {
|
||||||
|
Pattern string
|
||||||
|
Description string
|
||||||
|
Method string
|
||||||
|
HandlerFunc http.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimpleApi struct {
|
||||||
|
apiEndpoints []*Endpoint
|
||||||
|
router *mux.Router
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(prefix string, apiEndpoints ...*Endpoint) *SimpleApi {
|
||||||
|
api := &SimpleApi{apiEndpoints, nil, prefix}
|
||||||
|
api.initRouter()
|
||||||
|
return api
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *SimpleApi) initRouter() {
|
||||||
|
api.router = mux.NewRouter()
|
||||||
|
for _, apiEndpoint := range api.apiEndpoints {
|
||||||
|
api.router.Path(path.Join(api.prefix, apiEndpoint.Pattern)).
|
||||||
|
Methods(apiEndpoint.Method).
|
||||||
|
HandlerFunc(JSONHendler(apiEndpoint.HandlerFunc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *SimpleApi) Set404Handler(handler http.HandlerFunc) {
|
||||||
|
api.router.NotFoundHandler = http.HandlerFunc(handler)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *SimpleApi) ServeHTTP(w http.ResponseWriter, request *http.Request) {
|
||||||
|
api.router.ServeHTTP(w, request)
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package simpleapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type requestData struct {
|
||||||
|
Path string
|
||||||
|
Method string
|
||||||
|
Response *http.Response
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRequestData(r *http.Request) *requestData {
|
||||||
|
return &requestData{r.URL.Path, r.Method, nil}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHandlerFunc() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
encoder := json.NewEncoder(w)
|
||||||
|
encoder.Encode(createRequestData(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMockApiEndpoint(pattern, method string) *Endpoint {
|
||||||
|
return &Endpoint{Pattern: pattern, Method: method, HandlerFunc: GetHandlerFunc()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GET(ts *httptest.Server, path string) (*requestData, error) {
|
||||||
|
res, err := http.Get(ts.URL + path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res.Body.Close()
|
||||||
|
ret := new(requestData)
|
||||||
|
if err := json.Unmarshal(body, ret); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret.Response = res
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func POST(ts *httptest.Server, path, data string) (*requestData, error) {
|
||||||
|
res, err := http.Post(ts.URL+path, "", bytes.NewBufferString(data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res.Body.Close()
|
||||||
|
ret := new(requestData)
|
||||||
|
return ret, json.Unmarshal(body, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServeGet(t *testing.T) {
|
||||||
|
api := New("/", GetMockApiEndpoint("/", "GET"))
|
||||||
|
ts := httptest.NewServer(api)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
if rd, err := GET(ts, ""); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else if rd.Path != "/" {
|
||||||
|
t.Error(rd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServeGetSpecificPath(t *testing.T) {
|
||||||
|
api := New("/", GetMockApiEndpoint("/some/path", "GET"))
|
||||||
|
ts := httptest.NewServer(api)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
if rd, err := GET(ts, "/some/path"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else if rd.Path != "/some/path" {
|
||||||
|
t.Error(rd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServePOSTSpecificPath(t *testing.T) {
|
||||||
|
api := New("/", GetMockApiEndpoint("/some/path", "POST"))
|
||||||
|
ts := httptest.NewServer(api)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
if rd, err := POST(ts, "/some/path", ""); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else if rd.Path != "/some/path" || rd.Method != "POST" {
|
||||||
|
t.Error(rd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test404(t *testing.T) {
|
||||||
|
api := New("/")
|
||||||
|
api.Set404Handler(GetHandlerFunc())
|
||||||
|
ts := httptest.NewServer(api)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
if rd, err := GET(ts, "/some/path"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else if rd.Path != "/some/path" || rd.Method != "GET" {
|
||||||
|
t.Error(rd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHeaders(t *testing.T) {
|
||||||
|
api := New("/", GetMockApiEndpoint("/", "GET"))
|
||||||
|
ts := httptest.NewServer(api)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
if rd, err := GET(ts, ""); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else if rd.Response.Header.Get("Content-Type") != "application/json" {
|
||||||
|
t.Error(rd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServePrefix(t *testing.T) {
|
||||||
|
api := New("/api", GetMockApiEndpoint("/foo/bar", "GET"))
|
||||||
|
ts := httptest.NewServer(api)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
if rd, err := GET(ts, "/api/foo/bar"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else if rd.Path != "/api/foo/bar" {
|
||||||
|
t.Error(rd)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,6 @@
|
|||||||
"angular-mocks": "~1.2.1",
|
"angular-mocks": "~1.2.1",
|
||||||
"requirejs": "~2.1.9",
|
"requirejs": "~2.1.9",
|
||||||
"requirejs-text": "~2.0.10",
|
"requirejs-text": "~2.0.10",
|
||||||
"foundation": "zurb/bower-foundation",
|
|
||||||
"html5-boilerplate": "~4.3.0",
|
"html5-boilerplate": "~4.3.0",
|
||||||
"angularAMD": "~0.2.1",
|
"angularAMD": "~0.2.1",
|
||||||
"angular-foundation": "~0.5.1"
|
"angular-foundation": "~0.5.1"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
define [
|
define [
|
||||||
"angularAMD"
|
"angularAMD"
|
||||||
"angular-route"
|
"angular-route"
|
||||||
@@ -9,6 +10,10 @@ define [
|
|||||||
templateUrl: "partials/home.html"
|
templateUrl: "partials/home.html"
|
||||||
controller: "HomeCtrl"
|
controller: "HomeCtrl"
|
||||||
controllerUrl: "controllers/homeCtrl"
|
controllerUrl: "controllers/homeCtrl"
|
||||||
|
)).when("/serverspec", angularAMD.route(
|
||||||
|
templateUrl: "partials/serverspec.html"
|
||||||
|
controller: "ServerspecCtrl"
|
||||||
|
controllerUrl: "controllers/serverspecCtrl"
|
||||||
)).when("/apispec", angularAMD.route(
|
)).when("/apispec", angularAMD.route(
|
||||||
templateUrl: "partials/apispec.html"
|
templateUrl: "partials/apispec.html"
|
||||||
controller: "ApispecCtrl"
|
controller: "ApispecCtrl"
|
||||||
@@ -32,4 +37,6 @@ define [
|
|||||||
)).otherwise redirectTo: "/"
|
)).otherwise redirectTo: "/"
|
||||||
return
|
return
|
||||||
|
|
||||||
|
serverspec
|
||||||
|
|
||||||
angularAMD.bootstrap(app)
|
angularAMD.bootstrap(app)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
define ["app"], (app) ->
|
||||||
|
app.controller "ServerspecCtrl",["$scope", "$http", ($scope, $http) ->
|
||||||
|
$scope.server = {}
|
||||||
|
$http.get("/api/server").then (res) -> $scope.server = res.data
|
||||||
|
return
|
||||||
|
]
|
||||||
|
return
|
||||||
+2
-1
@@ -5,6 +5,7 @@ var help = require('gulp-task-listing');
|
|||||||
var clean = require('gulp-clean');
|
var clean = require('gulp-clean');
|
||||||
var sass = require('gulp-sass');
|
var sass = require('gulp-sass');
|
||||||
var rjs = require("gulp-rjs");
|
var rjs = require("gulp-rjs");
|
||||||
|
var gutil = require('gulp-util');
|
||||||
|
|
||||||
|
|
||||||
gulp.task('help', help);
|
gulp.task('help', help);
|
||||||
@@ -22,7 +23,7 @@ gulp.task('sass', ['bower'], function () {
|
|||||||
|
|
||||||
gulp.task('coffee', ['bower'], function() {
|
gulp.task('coffee', ['bower'], function() {
|
||||||
return gulp.src('coffee/**/*.coffee')
|
return gulp.src('coffee/**/*.coffee')
|
||||||
.pipe(coffee({bare: true}))
|
.pipe(coffee({bare: true}).on('error', gutil.log))
|
||||||
.pipe(gulp.dest('js/'))
|
.pipe(gulp.dest('js/'))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+18
-20
@@ -8,37 +8,35 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="off-canvas-wrap">
|
|
||||||
<div class="inner-wrap">
|
|
||||||
|
|
||||||
<nav class="tab-bar">
|
|
||||||
<section class="middle tab-bar-section">
|
|
||||||
<h1 class="title">Doorman</h1>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="right-small">
|
<top-bar>
|
||||||
<a class="right-off-canvas-toggle menu-icon" ><span></span></a>
|
<ul class="title-area">
|
||||||
</section>
|
<li class="name">
|
||||||
</nav>
|
<h1><a href="#">Doorman</a></h1>
|
||||||
|
</li>
|
||||||
|
<li toggle-top-bar class="menu-icon"><a href="#">Menu</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<aside class="right-off-canvas-menu">
|
<top-bar-section>
|
||||||
<ul class="off-canvas-list">
|
<!-- Right Nav Section -->
|
||||||
<li><a></a></li>
|
<ul class="right">
|
||||||
|
<li has-dropdown>
|
||||||
|
<a href="#">Dropdown</a>
|
||||||
|
<ul top-bar-dropdown>
|
||||||
<li><a href="#/doormen">doormen</a></li>
|
<li><a href="#/doormen">doormen</a></li>
|
||||||
<li><a href="#/newdoorman">new doorman</a></li>
|
<li><a href="#/newdoorman">new doorman</a></li>
|
||||||
<li><a href="#/apispec">api specification</a></li>
|
<li><a href="#/apispec">api specification</a></li>
|
||||||
<li><a href="#/serverspec">server specification</a></li>
|
<li><a href="#/serverspec">server specification</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</aside>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<section class="main-section">
|
</top-bar-section>
|
||||||
<div class="large-12 small-12 columns" ng-view>
|
</top-bar>
|
||||||
</section>
|
|
||||||
|
|
||||||
|
<div class="large-12 small-12 columns" ng-view></div>
|
||||||
|
|
||||||
<a class="exit-off-canvas"></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="row wab-apispect">
|
<div class="row">
|
||||||
<div class="columns large-12">
|
<div class="columns large-12">
|
||||||
<h2>Api specification</h2>
|
<h1>Api specification</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -15,6 +15,11 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><wab-api endpoint="/server"></wab-api></td>
|
||||||
|
<td><wab-method method="get"></wab-method></td>
|
||||||
|
<td>Get the server specification</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><wab-api endpoint="/doormen"></wab-api></td>
|
<td><wab-api endpoint="/doormen"></wab-api></td>
|
||||||
<td><wab-method method="get"></wab-method></td>
|
<td><wab-method method="get"></wab-method></td>
|
||||||
@@ -40,6 +45,16 @@
|
|||||||
<td><wab-method method="delete"></wab-method></td>
|
<td><wab-method method="delete"></wab-method></td>
|
||||||
<td>Delete the doorman</td>
|
<td>Delete the doorman</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><wab-api endpoint="/doormen/:id/status"></wab-api></td>
|
||||||
|
<td><wab-method method="get"></wab-method></td>
|
||||||
|
<td>Return the state of the doormen as expected by the client</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><wab-api endpoint="/doormen/:id/result?data=<base64>"></wab-api></td>
|
||||||
|
<td><wab-method method="get"></wab-method></td>
|
||||||
|
<td>Return the state of the doormen as expected by the client</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="columns large-12">
|
<div class="columns large-12">
|
||||||
<h2>{{ doorman.name }} </h2>
|
<h1>{{ doorman.name }} </h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -19,6 +20,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="row">
|
||||||
|
<div class="columns large-12 small-12">
|
||||||
|
<h3>try it!</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<textarea class="large-offset-1 small-offset-1 large-10 small-10 columns">
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="large-offset-1 small-offset-1 large-10 small-10 columns">
|
||||||
|
<a href="#" class="button tiny">try</a>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="large-12 small-12 columns">
|
<div class="large-12 small-12 columns">
|
||||||
<ul class="button-group">
|
<ul class="button-group">
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="columns large-12">
|
||||||
|
<h1>error page</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="columns large-12">
|
<div class="columns large-12">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="columns large-12">
|
<div class="columns large-12">
|
||||||
<h2>doormen</h2>
|
<h1>doormen</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
<h2>Welcome to the world best a/b testing library</h2>
|
<div class="row">
|
||||||
|
<div class="columns large-12">
|
||||||
|
<h1>Welcome to the world best a/b testing library</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="large-12 columns">
|
<div class="large-12 columns">
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
<div class="row">
|
||||||
<h2>New Doorman</h2>
|
<div class="columns large-12">
|
||||||
|
<h1>new doorman</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form>
|
<form>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="columns large-12">
|
||||||
|
<h1>server specification</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" ng-show="server.message_queue == 'nanomsg'">
|
||||||
|
<div class="columns large-12">
|
||||||
|
<h3>general</h3>
|
||||||
|
<table style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th >value</th>
|
||||||
|
<th>key</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>hostname</td>
|
||||||
|
<td><code>{{server.hostname}}</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>port</td>
|
||||||
|
<td><code>{{server.port}}</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>go version</td>
|
||||||
|
<td><code>{{server.go_version}}</code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row" ng-show="server.message_queue == 'nanomsg'">
|
||||||
|
<div class="columns large-12">
|
||||||
|
<h3>nano msg</h3>
|
||||||
|
<table style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th >value</th>
|
||||||
|
<th>key</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>url</td>
|
||||||
|
<td><code>{{server.nano_msg.url}}</code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
|
||||||
|
|
||||||
const STATIC_DIR_PATH = "static/"
|
|
||||||
|
|
||||||
func serveStatic(router *mux.Router) {
|
|
||||||
handler := func(w http.ResponseWriter, request *http.Request) {
|
|
||||||
vars := mux.Vars(request)
|
|
||||||
filepath := "/" + vars["path"]
|
|
||||||
w.Header().Set("Cache-Control", "public, max-age=43200")
|
|
||||||
http.ServeFile(w, request, path.Join(STATIC_DIR_PATH, filepath))
|
|
||||||
}
|
|
||||||
router.HandleFunc("/{path:.*}", handler)
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user