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
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
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"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
)
|
||||
|
||||
type DBConnector interface {
|
||||
Save(wab *doormen.DoormanDefinition) error
|
||||
Update(wab *doormen.DoormanDefinition) error
|
||||
Exists(wab *doormen.DoormanDefinition) bool
|
||||
ExistsId(id bson.ObjectId) bool
|
||||
GetAllDoormen() <-chan *doormen.DoormanId
|
||||
GetDoorman(id bson.ObjectId) (*doormen.DoormanDefinition, error)
|
||||
DeleteDoorman(id bson.ObjectId) error
|
||||
}
|
||||
|
||||
var conn DBConnector
|
||||
|
||||
func initMongoDbConnector() {
|
||||
var err error
|
||||
if conn, err = mongoconnector.NewMongoDBConnector(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
if params := shared.GetParams(); params.UseMongoDb() {
|
||||
initMongoDbConnector()
|
||||
log.Println("using mongodb as database")
|
||||
} else {
|
||||
conn = inmemoryconnector.NewInMemoryDBConnector()
|
||||
log.Println("using inmemory as database")
|
||||
}
|
||||
}
|
||||
|
||||
func CreateDoormanInDatabase(wab *doormen.DoormanDefinition) error {
|
||||
if conn.Exists(wab) {
|
||||
return errors.New("doormen already exists")
|
||||
}
|
||||
return conn.Save(wab)
|
||||
}
|
||||
|
||||
func UpdateDoormanInDatabase(wab *doormen.DoormanDefinition) error {
|
||||
return conn.Update(wab)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/didiercrunch/doormanserver/doormen"
|
||||
"github.com/didiercrunch/doormanserver/inmemoryconnector"
|
||||
)
|
||||
|
||||
func TestCreateDoormanInDatabase(t *testing.T) {
|
||||
wab := doormen.NewDoormanDefinition("some_name")
|
||||
conn = inmemoryconnector.NewInMemoryDBConnector()
|
||||
|
||||
if err := CreateDoormanInDatabase(wab); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := CreateDoormanInDatabase(wab); err == nil {
|
||||
t.Error("should have an error here")
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/didiercrunch/doormanserver/logpublisher"
|
||||
"github.com/didiercrunch/doormanserver/nanomsgpublisher"
|
||||
"github.com/didiercrunch/doormanserver/nsqpublisher"
|
||||
"github.com/didiercrunch/doormanserver/shared"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
)
|
||||
|
||||
type Publisher interface {
|
||||
Emit(doormanId bson.ObjectId, doorman shared.AsDoormanUpdatePayloader) error
|
||||
Init() error
|
||||
}
|
||||
|
||||
var publisher Publisher
|
||||
|
||||
func initNSQPublisher() {
|
||||
publisher = new(nsqpublisher.NsqPublisher)
|
||||
if err := publisher.Init(); err != nil {
|
||||
log.Panic(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func initNanoMsgPublisher() {
|
||||
publisher = new(nanomsgpublisher.NanoMsgPublisher)
|
||||
if err := publisher.Init(); err != nil {
|
||||
log.Panic(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
} else {
|
||||
publisher = &logpublisher.LogPublisher{log.Println}
|
||||
log.Println("using stdout as message queue")
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
initPublisher(shared.GetParams())
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package api
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestInitPublisher(t *testing.T) {
|
||||
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWrite404Error(t *testing.T) {
|
||||
rw := httptest.NewRecorder()
|
||||
Write404Error(rw)
|
||||
if rw.Code != 404 {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrite500Error(t *testing.T) {
|
||||
rw := httptest.NewRecorder()
|
||||
Write500Error(rw, errors.New("blah"))
|
||||
if rw.Code != 500 {
|
||||
t.Error()
|
||||
}
|
||||
if string(rw.Body.Bytes()) != "server error" {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrite400Error(t *testing.T) {
|
||||
rw := httptest.NewRecorder()
|
||||
Write400Error(rw, errors.New("blah"))
|
||||
if rw.Code != 400 {
|
||||
t.Error()
|
||||
}
|
||||
if string(rw.Body.Bytes()) != "blah" {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user