major refactoring of the api
This commit is contained in:
1 parent
d4f7dc57fc
commit
f0628e4682
38 files changed
+821
-367
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 (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/didiercrunch/doormanserver/doormen"
|
||||
"github.com/didiercrunch/doormanserver/inmemoryconnector"
|
||||
"github.com/didiercrunch/doormanserver/mongoconnector"
|
||||
"github.com/didiercrunch/doormanserver/shared"
|
||||
"github.com/didiercrunch/doormanserver/doormen"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package main
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/didiercrunch/doormanserver/inmemoryconnector"
|
||||
"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")
|
||||
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 (
|
||||
"log"
|
||||
@@ -37,7 +37,6 @@ func initPublisher(params *shared.Params) {
|
||||
if params.UseNSQ() {
|
||||
initNSQPublisher()
|
||||
log.Println("using nsq as message queue")
|
||||
|
||||
} else if params.UseNanomsg() {
|
||||
initNanoMsgPublisher()
|
||||
log.Println("using nanomsg as message queue")
|
||||
@@ -45,8 +44,8 @@ func initPublisher(params *shared.Params) {
|
||||
publisher = &logpublisher.LogPublisher{log.Println}
|
||||
log.Println("using stdout as message queue")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
initPublisher(shared.GetParams())
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package api
|
||||
|
||||
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 (
|
||||
"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())
|
||||
}
|
||||
+4
-3
@@ -31,8 +31,8 @@ func (wId *DoormanId) AsJson() string {
|
||||
}
|
||||
|
||||
type DoormanDefinition struct {
|
||||
Id bson.ObjectId `json:"id,omitempty" bson:"_id"`
|
||||
Name string `json:"name" bson:"name"`
|
||||
Id bson.ObjectId `json:"id,omitempty" bson:"_id"`
|
||||
Name string `json:"name" bson:"name"`
|
||||
Values []*DoormanValue `json:"values" bson:"values"`
|
||||
}
|
||||
|
||||
@@ -71,13 +71,14 @@ func (dmd *DoormanDefinition) ValidateDoormanValueNames() error {
|
||||
func (dmd *DoormanDefinition) ValidateDoormanValueProbabilities() error {
|
||||
var sum float64 = 0
|
||||
for _, w := range dmd.Values {
|
||||
|
||||
if p := w.Probability; p < 0-epsilon || p > 1+epsilon {
|
||||
return errors.New(fmt.Sprintf("doorman value %v is out of range", p))
|
||||
} else {
|
||||
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 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" {
|
||||
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) {
|
||||
|
||||
@@ -2,32 +2,36 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/didiercrunch/doormanserver/api"
|
||||
"github.com/didiercrunch/doormanserver/shared"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/didiercrunch/doormanserver/shared"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func createMuxRouter() http.Handler {
|
||||
r := mux.NewRouter()
|
||||
serveApi(r.PathPrefix("/api").Subrouter())
|
||||
serveStatic(r.PathPrefix("/").Subrouter())
|
||||
return r
|
||||
}
|
||||
const STATIC_DIR_PATH = "static/"
|
||||
|
||||
func GetAddressToServe() string {
|
||||
params := shared.GetParams()
|
||||
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() {
|
||||
r := createMuxRouter()
|
||||
http.Handle("/", r)
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/api/", api.Create())
|
||||
mux.HandleFunc("/", serveStatic2)
|
||||
|
||||
address := GetAddressToServe()
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
"requirejs": "~2.1.9",
|
||||
"requirejs-text": "~2.0.10",
|
||||
"foundation": "zurb/bower-foundation",
|
||||
"html5-boilerplate": "~4.3.0",
|
||||
"angularAMD": "~0.2.1",
|
||||
"angular-foundation": "~0.5.1"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
define [
|
||||
"angularAMD"
|
||||
"angular-route"
|
||||
@@ -9,6 +10,10 @@ define [
|
||||
templateUrl: "partials/home.html"
|
||||
controller: "HomeCtrl"
|
||||
controllerUrl: "controllers/homeCtrl"
|
||||
)).when("/serverspec", angularAMD.route(
|
||||
templateUrl: "partials/serverspec.html"
|
||||
controller: "ServerspecCtrl"
|
||||
controllerUrl: "controllers/serverspecCtrl"
|
||||
)).when("/apispec", angularAMD.route(
|
||||
templateUrl: "partials/apispec.html"
|
||||
controller: "ApispecCtrl"
|
||||
@@ -32,4 +37,6 @@ define [
|
||||
)).otherwise redirectTo: "/"
|
||||
return
|
||||
|
||||
serverspec
|
||||
|
||||
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 sass = require('gulp-sass');
|
||||
var rjs = require("gulp-rjs");
|
||||
var gutil = require('gulp-util');
|
||||
|
||||
|
||||
gulp.task('help', help);
|
||||
@@ -22,7 +23,7 @@ gulp.task('sass', ['bower'], function () {
|
||||
|
||||
gulp.task('coffee', ['bower'], function() {
|
||||
return gulp.src('coffee/**/*.coffee')
|
||||
.pipe(coffee({bare: true}))
|
||||
.pipe(coffee({bare: true}).on('error', gutil.log))
|
||||
.pipe(gulp.dest('js/'))
|
||||
});
|
||||
|
||||
|
||||
+27
-29
@@ -8,37 +8,35 @@
|
||||
</head>
|
||||
|
||||
<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">
|
||||
<a class="right-off-canvas-toggle menu-icon" ><span></span></a>
|
||||
</section>
|
||||
</nav>
|
||||
|
||||
<aside class="right-off-canvas-menu">
|
||||
<ul class="off-canvas-list">
|
||||
<li><a></a></li>
|
||||
<li><a href="#/doormen">doormen</a></li>
|
||||
<li><a href="#/newdoorman">new doorman</a></li>
|
||||
<li><a href="#/apispec">api specification</a></li>
|
||||
<li><a href="#/serverspec">server specification</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<section class="main-section">
|
||||
<div class="large-12 small-12 columns" ng-view>
|
||||
</section>
|
||||
|
||||
|
||||
<a class="exit-off-canvas"></a>
|
||||
</div>
|
||||
</div>
|
||||
<top-bar>
|
||||
<ul class="title-area">
|
||||
<li class="name">
|
||||
<h1><a href="#">Doorman</a></h1>
|
||||
</li>
|
||||
<li toggle-top-bar class="menu-icon"><a href="#">Menu</a></li>
|
||||
</ul>
|
||||
|
||||
<top-bar-section>
|
||||
<!-- Right Nav Section -->
|
||||
<ul class="right">
|
||||
<li has-dropdown>
|
||||
<a href="#">Dropdown</a>
|
||||
<ul top-bar-dropdown>
|
||||
<li><a href="#/doormen">doormen</a></li>
|
||||
<li><a href="#/newdoorman">new doorman</a></li>
|
||||
<li><a href="#/apispec">api specification</a></li>
|
||||
<li><a href="#/serverspec">server specification</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</top-bar-section>
|
||||
</top-bar>
|
||||
|
||||
<div class="large-12 small-12 columns" ng-view></div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="row wab-apispect">
|
||||
<div class="row">
|
||||
<div class="columns large-12">
|
||||
<h2>Api specification</h2>
|
||||
<h1>Api specification</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<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>
|
||||
<td><wab-api endpoint="/doormen"></wab-api></td>
|
||||
<td><wab-method method="get"></wab-method></td>
|
||||
@@ -40,6 +45,16 @@
|
||||
<td><wab-method method="delete"></wab-method></td>
|
||||
<td>Delete the doorman</td>
|
||||
</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>
|
||||
|
||||
</table>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="columns large-12">
|
||||
<h2>{{ doorman.name }} </h2>
|
||||
<h1>{{ doorman.name }} </h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,11 +20,27 @@
|
||||
</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="large-12 small-12 columns">
|
||||
<ul class="button-group">
|
||||
<li><a ng-click="updateDoorman()" class="small button" ng-class="{disabled: !validate()}">Update</a></li>
|
||||
<li><a ng-click="deleteDoorman()" class="small button">Delete</a></li>
|
||||
<li><a ng-click="updateDoorman()" class="small button" ng-class="{disabled: !validate()}">Update</a></li>
|
||||
<li><a ng-click="deleteDoorman()" class="small button">Delete</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,3 +1,8 @@
|
||||
<div class="row">
|
||||
<div class="columns large-12">
|
||||
<h1>error page</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="columns large-12">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="row">
|
||||
<div class="columns large-12">
|
||||
<h2>doormen</h2>
|
||||
<h1>doormen</h1>
|
||||
</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="large-12 columns">
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
|
||||
<h2>New Doorman</h2>
|
||||
<div class="row">
|
||||
<div class="columns large-12">
|
||||
<h1>new doorman</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form>
|
||||
<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