added doormen ownership
This commit is contained in:
1 parent
f0628e4682
commit
f7a8d416c1
27 files changed
+475
-112
No files matched your search
+10
-4
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Create() http.Handler {
|
||||
func Create(root string) http.Handler {
|
||||
serverSpecification := &simpleapi.Endpoint{
|
||||
"/server",
|
||||
"Get the server specification.",
|
||||
@@ -29,26 +29,32 @@ func Create() http.Handler {
|
||||
|
||||
doorman := &simpleapi.Endpoint{
|
||||
"/doormen/{id}",
|
||||
"Get the doorman.",
|
||||
"Get a specific doorman by id.",
|
||||
"GET",
|
||||
GetDoorman,
|
||||
}
|
||||
|
||||
updateDoorman := &simpleapi.Endpoint{
|
||||
"/doormen/{id}",
|
||||
"Update the doorman with new probabilities.",
|
||||
`Update the doorman with new probabilities or owner emails. The payload
|
||||
must be a valid doorman with the same id than the targeted doorman.
|
||||
The values of the PUT doorman will replace the values of the old
|
||||
ones.`,
|
||||
"PUT",
|
||||
UpdateDoorman,
|
||||
}
|
||||
|
||||
api := simpleapi.New(
|
||||
"/api",
|
||||
root,
|
||||
serverSpecification,
|
||||
createDoorman,
|
||||
allDoorman,
|
||||
doorman,
|
||||
updateDoorman,
|
||||
)
|
||||
api.EnableDocumentation("documentation")
|
||||
api.AddMiddlewares(AuthenticationMiddleware)
|
||||
api.InitRouter()
|
||||
|
||||
return api
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gorilla/context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const userKey = "user"
|
||||
|
||||
func GetUser(req *http.Request) string {
|
||||
if rv := context.Get(req, userKey); rv != nil {
|
||||
return rv.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func SetUser(req *http.Request, user string) {
|
||||
context.Set(req, userKey, user)
|
||||
}
|
||||
|
||||
func AuthenticationMiddleware(h http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
query := req.URL.Query()
|
||||
user := query.Get("user")
|
||||
SetUser(req, user)
|
||||
h(w, req)
|
||||
context.Clear(req)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAuthentication(t *testing.T) {
|
||||
handler := func(w http.ResponseWriter, req *http.Request) {
|
||||
if user := GetUser(req); user != "natasha" {
|
||||
t.Error(user)
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "http://example.com/foo?user=natasha", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
AuthenticationMiddleware(handler)(w, req)
|
||||
if user := GetUser(req); user != "" {
|
||||
t.Error(user)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,8 @@ func CreateDoorman(w http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
wdef.Id = bson.NewObjectId()
|
||||
if err := wdef.Validate(); err != nil {
|
||||
|
||||
if err := wdef.Validate(GetUser(request)); err != nil {
|
||||
w.WriteHeader(400)
|
||||
fmt.Fprintln(w, "New doorman is invalid \n", err)
|
||||
return
|
||||
|
||||
@@ -16,7 +16,11 @@ func GetIdFromLocation(p string) bson.ObjectId {
|
||||
|
||||
func TestCreateDoorman(t *testing.T) {
|
||||
conn = inmemoryconnector.NewInMemoryDBConnector()
|
||||
payload := `{"name":"name","values":[{"name":"red","probability":0.2},{"name":"blue","probability":0.8}]}`
|
||||
payload := `
|
||||
{"name":"name",
|
||||
"values":[{"name":"red","probability":0.2},{"name":"blue","probability":0.8}],
|
||||
"emails": ["alice"]
|
||||
}`
|
||||
req, err := http.NewRequest(
|
||||
"POST",
|
||||
"http://bigtits.com/api/doormen",
|
||||
@@ -26,6 +30,7 @@ func TestCreateDoorman(t *testing.T) {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
SetUser(req, "alice")
|
||||
w := httptest.NewRecorder()
|
||||
CreateDoorman(w, req)
|
||||
if w.Code != 201 {
|
||||
|
||||
@@ -22,7 +22,7 @@ func UpdateDoorman(w http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
wdef.Id = id
|
||||
|
||||
if err := wdef.Validate(); err != nil {
|
||||
if err := wdef.Validate(GetUser(request)); err != nil {
|
||||
Write400Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
+19
-5
@@ -34,20 +34,21 @@ type DoormanDefinition struct {
|
||||
Id bson.ObjectId `json:"id,omitempty" bson:"_id"`
|
||||
Name string `json:"name" bson:"name"`
|
||||
Values []*DoormanValue `json:"values" bson:"values"`
|
||||
OwnerEmails []string `json:"emails" bson:"emails"`
|
||||
}
|
||||
|
||||
func NewDoormanDefinition(name string) *DoormanDefinition {
|
||||
return &DoormanDefinition{Id: bson.NewObjectId(), Name: name}
|
||||
func NewDoormanDefinition(name string, ownerEmails ...string) *DoormanDefinition {
|
||||
return &DoormanDefinition{Id: bson.NewObjectId(), Name: name, OwnerEmails: ownerEmails}
|
||||
}
|
||||
|
||||
// a way to quickly create doormen in tests. do not use in real code. will
|
||||
// panic if a doorman is wrongly specify
|
||||
func QuickNewDoormanDefinition(name string, probs ...float64) *DoormanDefinition {
|
||||
ret := NewDoormanDefinition(name)
|
||||
ret := NewDoormanDefinition(name, "natasha@bigtits.com")
|
||||
for i, prob := range probs {
|
||||
ret.Values = append(ret.Values, &DoormanValue{"T" + strconv.Itoa(i), prob})
|
||||
}
|
||||
if err := ret.Validate(); err != nil {
|
||||
if err := ret.Validate("natasha@bigtits.com"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ret
|
||||
@@ -68,6 +69,15 @@ func (dmd *DoormanDefinition) ValidateDoormanValueNames() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dmd *DoormanDefinition) AsWriteAccess(email string) bool {
|
||||
for _, email_ := range dmd.OwnerEmails {
|
||||
if email_ == email {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (dmd *DoormanDefinition) ValidateDoormanValueProbabilities() error {
|
||||
var sum float64 = 0
|
||||
for _, w := range dmd.Values {
|
||||
@@ -84,7 +94,7 @@ func (dmd *DoormanDefinition) ValidateDoormanValueProbabilities() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dmd *DoormanDefinition) Validate() (err error) {
|
||||
func (dmd *DoormanDefinition) Validate(author string) (err error) {
|
||||
switch {
|
||||
case dmd.Name == "":
|
||||
err = errors.New("name cannot be empty")
|
||||
@@ -94,6 +104,10 @@ func (dmd *DoormanDefinition) Validate() (err error) {
|
||||
err = dmd.ValidateDoormanValueNames()
|
||||
case dmd.ValidateDoormanValueProbabilities() != nil:
|
||||
err = dmd.ValidateDoormanValueProbabilities()
|
||||
case len(dmd.OwnerEmails) < 1:
|
||||
err = errors.New("A doorman needs at least one owner specify as 'email'.")
|
||||
case !dmd.AsWriteAccess(author):
|
||||
err = errors.New(fmt.Sprintf("'%s' is not allow to edit this doorman.", author))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+34
-9
@@ -21,45 +21,53 @@ func TestNewDoormanDefinition(t *testing.T) {
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
dmd := &DoormanDefinition{Name: ""}
|
||||
if err := dmd.Validate(); err.Error() != "name cannot be empty" {
|
||||
if err := dmd.Validate("bob"); err.Error() != "name cannot be empty" {
|
||||
t.Error(err)
|
||||
}
|
||||
dmd = &DoormanDefinition{Name: "foo"}
|
||||
if err := dmd.Validate(); err.Error() != "need at least 2 values" {
|
||||
if err := dmd.Validate("bob"); err.Error() != "need at least 2 values" {
|
||||
t.Error(err)
|
||||
}
|
||||
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{nil}}
|
||||
if err := dmd.Validate(); err.Error() != "need at least 2 values" {
|
||||
if err := dmd.Validate("bob"); err.Error() != "need at least 2 values" {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0}, &DoormanValue{"foo", 0}}}
|
||||
if err := dmd.Validate(); err.Error() != "doorman value names must be unique within a doorman" {
|
||||
if err := dmd.Validate("bob"); err.Error() != "doorman value names must be unique within a doorman" {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0}, &DoormanValue{"", 0}}}
|
||||
if err := dmd.Validate(); err.Error() != "doorman value cannot be empty" {
|
||||
if err := dmd.Validate("bob"); err.Error() != "doorman value cannot be empty" {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", -0.25}, &DoormanValue{"bar", 0.5}}}
|
||||
if err := dmd.Validate(); err.Error() != "doorman value -0.25 is out of range" {
|
||||
if err := dmd.Validate("bob"); err.Error() != "doorman value -0.25 is out of range" {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 2.5}, &DoormanValue{"bar", 0.5}}}
|
||||
if err := dmd.Validate(); err.Error() != "doorman value 2.5 is out of range" {
|
||||
if err := dmd.Validate("bob"); err.Error() != "doorman value 2.5 is out of range" {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0.75}, &DoormanValue{"bar", 0.5}}}
|
||||
if err := dmd.Validate(); err.Error() != "the sum of the probability must be 1.0" {
|
||||
if err := dmd.Validate("bob"); 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" {
|
||||
if err := dmd.Validate("bob"); err.Error() != "the sum of the probability must be 1.0" {
|
||||
t.Error(err)
|
||||
}
|
||||
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0.8}, &DoormanValue{"bar", 0.2}}}
|
||||
if err := dmd.Validate("bob"); err.Error() != "A doorman needs at least one owner specify as 'email'." {
|
||||
t.Error(err)
|
||||
}
|
||||
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0.8}, &DoormanValue{"bar", 0.2}}, OwnerEmails: []string{"alice"}}
|
||||
if err := dmd.Validate("bob"); err.Error() != "'bob' is not allow to edit this doorman." {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
@@ -126,4 +134,21 @@ func TestQuickNewDoormanDefinition(t *testing.T) {
|
||||
if dmd.Values[1].Name != "T1" || dmd.Values[1].Probability != 0.25 {
|
||||
t.Error()
|
||||
}
|
||||
if len(dmd.OwnerEmails) != 1 && dmd.OwnerEmails[0] != "natasha@bigtits.com" {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsWriteAccess(t *testing.T) {
|
||||
dmd := &DoormanDefinition{OwnerEmails: []string{"natasha@bigtits.com"}}
|
||||
testCases := map[string]bool{
|
||||
"natasha@bigtits.com": true,
|
||||
"natasha@bigtis.net": false,
|
||||
}
|
||||
for email, hasAccess := range testCases {
|
||||
if hasAccess != dmd.AsWriteAccess(email) {
|
||||
t.Error(email)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -17,7 +17,7 @@ host: 0.0.0.0 #localhost
|
||||
message_queue: nanomsg # nanomsg # nsq
|
||||
|
||||
# determine which database to use
|
||||
database: in_memory # mongo
|
||||
database: mongo # mongo
|
||||
|
||||
################################################################################
|
||||
|
||||
|
||||
@@ -19,14 +19,13 @@ func GetAddressToServe() string {
|
||||
|
||||
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))
|
||||
w.Header().Set("Cache-Control", "private, max-age=0, no-cache")
|
||||
http.ServeFile(w, request, path.Join(STATIC_DIR_PATH, filepath))
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/api/", api.Create())
|
||||
mux.Handle("/api/", api.Create("/api"))
|
||||
mux.HandleFunc("/", serveStatic2)
|
||||
|
||||
address := GetAddressToServe()
|
||||
|
||||
+51
-5
@@ -1,13 +1,17 @@
|
||||
package simpleapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"mime"
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
func JSONHendler(handler http.HandlerFunc) http.HandlerFunc {
|
||||
type MiddleWare func(handler http.HandlerFunc) http.HandlerFunc
|
||||
|
||||
func JSONHandler(handler http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, request *http.Request) {
|
||||
w.Header().Add("Content-Type", mime.TypeByExtension(".json"))
|
||||
handler(w, request)
|
||||
@@ -21,25 +25,67 @@ type Endpoint struct {
|
||||
HandlerFunc http.HandlerFunc
|
||||
}
|
||||
|
||||
func (ep *Endpoint) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]string{
|
||||
"pattern": path.Join(ep.Pattern),
|
||||
"description": ep.Description,
|
||||
"method": ep.Method,
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
type SimpleApi struct {
|
||||
apiEndpoints []*Endpoint
|
||||
router *mux.Router
|
||||
prefix string
|
||||
middlewares []MiddleWare
|
||||
documentationRoute string
|
||||
}
|
||||
|
||||
func New(prefix string, apiEndpoints ...*Endpoint) *SimpleApi {
|
||||
api := &SimpleApi{apiEndpoints, nil, prefix}
|
||||
api.initRouter()
|
||||
api := &SimpleApi{apiEndpoints, nil, prefix, make([]MiddleWare, 0), ""}
|
||||
api.InitRouter()
|
||||
return api
|
||||
}
|
||||
|
||||
func (api *SimpleApi) initRouter() {
|
||||
func (api *SimpleApi) DocumentationHandler(w http.ResponseWriter, req *http.Request) {
|
||||
enc := json.NewEncoder(w)
|
||||
payload := map[string]interface{}{"endpoints": api.apiEndpoints}
|
||||
if e := enc.Encode(payload); e != nil {
|
||||
fmt.Fprintln(w, e)
|
||||
}
|
||||
}
|
||||
|
||||
func (api *SimpleApi) EnableDocumentation(route string) {
|
||||
if route == "" {
|
||||
return
|
||||
}
|
||||
api.documentationRoute = route
|
||||
api.router.Path(path.Join(api.prefix, route)).
|
||||
HandlerFunc(JSONHandler(api.DocumentationHandler))
|
||||
}
|
||||
|
||||
func (api *SimpleApi) AddMiddlewares(middlewares ...MiddleWare) {
|
||||
api.middlewares = append(api.middlewares, middlewares...)
|
||||
}
|
||||
|
||||
func (api *SimpleApi) applyMiddlewares(handler http.HandlerFunc) http.HandlerFunc {
|
||||
ret := handler
|
||||
for _, middleware := range api.middlewares {
|
||||
ret = middleware(ret)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (api *SimpleApi) InitRouter() {
|
||||
api.router = mux.NewRouter()
|
||||
for _, apiEndpoint := range api.apiEndpoints {
|
||||
handler := JSONHandler(api.applyMiddlewares(apiEndpoint.HandlerFunc))
|
||||
api.router.Path(path.Join(api.prefix, apiEndpoint.Pattern)).
|
||||
Methods(apiEndpoint.Method).
|
||||
HandlerFunc(JSONHendler(apiEndpoint.HandlerFunc))
|
||||
HandlerFunc(handler)
|
||||
}
|
||||
api.EnableDocumentation(api.documentationRoute)
|
||||
}
|
||||
|
||||
func (api *SimpleApi) Set404Handler(handler http.HandlerFunc) {
|
||||
|
||||
@@ -135,3 +135,77 @@ func TestServePrefix(t *testing.T) {
|
||||
t.Error(rd)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocumentationHandler(t *testing.T) {
|
||||
api := New("/api", GetMockApiEndpoint("/foo/bar", "GET"))
|
||||
req, err := http.NewRequest("GET", "http://example.com/foo", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
api.DocumentationHandler(w, req)
|
||||
if code := w.Code; code != 200 {
|
||||
t.Error("bad status", code)
|
||||
}
|
||||
body := `{"endpoints":[{"description":"","method":"GET","pattern":"/foo/bar"}]}` + "\n"
|
||||
if body != w.Body.String() {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableDocumentation(t *testing.T) {
|
||||
api := New("/api", GetMockApiEndpoint("/foo/bar", "GET"))
|
||||
api.EnableDocumentation("/doc")
|
||||
ts := httptest.NewServer(api)
|
||||
defer ts.Close()
|
||||
res, err := http.Get(ts.URL + "/api/doc")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if res.StatusCode != 200 {
|
||||
t.Error("should have status 200 but got", res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableDocumentationAndIniting(t *testing.T) {
|
||||
api := New("/api", GetMockApiEndpoint("/foo/bar", "GET"))
|
||||
api.EnableDocumentation("/doc")
|
||||
api.InitRouter()
|
||||
ts := httptest.NewServer(api)
|
||||
defer ts.Close()
|
||||
res, err := http.Get(ts.URL + "/api/doc")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if res.StatusCode != 200 {
|
||||
t.Error("should have status 200 but got", res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMiddleWares(t *testing.T) {
|
||||
api := New("/api", GetMockApiEndpoint("/foo", "GET"))
|
||||
middleWare1 := func(f http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
r.URL.Path += "/1"
|
||||
f(w, r)
|
||||
}
|
||||
}
|
||||
middleWare2 := func(f http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
r.URL.Path += "/2"
|
||||
f(w, r)
|
||||
}
|
||||
}
|
||||
api.AddMiddlewares(middleWare1, middleWare2)
|
||||
api.InitRouter()
|
||||
ts := httptest.NewServer(api)
|
||||
defer ts.Close()
|
||||
if rd, err := GET(ts, "/api/foo"); err != nil {
|
||||
t.Error(err)
|
||||
} else if rd.Path != "/api/foo/2/1" {
|
||||
t.Error(rd.Path)
|
||||
}
|
||||
|
||||
}
|
||||
+3
-1
@@ -20,6 +20,8 @@
|
||||
"requirejs-text": "~2.0.10",
|
||||
"html5-boilerplate": "~4.3.0",
|
||||
"angularAMD": "~0.2.1",
|
||||
"angular-foundation": "~0.5.1"
|
||||
"angular-foundation": "~0.5.1",
|
||||
"lodash": "~3.9.3",
|
||||
"font-awesome": "~4.3.0"
|
||||
}
|
||||
}
|
||||
@@ -34,9 +34,11 @@ define [
|
||||
templateUrl: "partials/doorman.html"
|
||||
controller: "DoormanCtrl"
|
||||
controllerUrl: "controllers/doormanCtrl"
|
||||
)).otherwise redirectTo: "/"
|
||||
)).otherwise({redirectTo: "/"})
|
||||
|
||||
document.getElementsByTagName("body")[0].style.visibility = ""
|
||||
return
|
||||
|
||||
serverspec
|
||||
|
||||
|
||||
angularAMD.bootstrap(app)
|
||||
@@ -1,8 +1,12 @@
|
||||
define ["app", "angularAMD"], (app, angularAMD) ->
|
||||
app.controller "ApispecCtrl", ($scope) ->
|
||||
app.controller "ApispecCtrl",['$scope', '$http', ($scope, $http) ->
|
||||
$http.get("/api/documentation").then (res)->
|
||||
$scope.endpoints = res.data.endpoints
|
||||
|
||||
$scope.methods =
|
||||
get: "GET"
|
||||
post: "POST"
|
||||
]
|
||||
|
||||
angularAMD.directive "wabMethod", () ->
|
||||
directive =
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
define ["app"], (app) ->
|
||||
define ["app", "lodash"], (app, _) ->
|
||||
app.controller "DoormanCtrl", ["$scope", "$http","$routeParams","$location", ($scope, $http, $routeParams, $location) ->
|
||||
$scope.doorman = {values: []}
|
||||
$scope.apiurl = "/api/doormen/#{$routeParams.id}"
|
||||
$scope.user = localStorage.getItem("email")
|
||||
|
||||
$http.get($scope.apiurl).then((res) ->
|
||||
$scope.doorman = res.data
|
||||
$scope.isAdmin = _.contains($scope.doorman.emails, $scope.user)
|
||||
$scope.initEmails()
|
||||
|
||||
).catch((res) ->
|
||||
if res.status == 404
|
||||
$location.path("/doormen/error/#{res.status}/#{$routeParams.id}")
|
||||
@@ -12,19 +17,44 @@ define ["app"], (app) ->
|
||||
$scope.deleteDoorman = () ->
|
||||
console.log("not yet implemented")
|
||||
|
||||
$scope.initEmails = () ->
|
||||
[$scope.leftEmailColumns, $scope.rightEmailColumns] = _.chunk($scope.doorman.emails, 2)
|
||||
|
||||
$scope.validate = ()->
|
||||
ret = 0
|
||||
for val in $scope.doorman.values
|
||||
ret += val.probability
|
||||
return Math.abs(ret - 1.0) < 0.000001
|
||||
|
||||
$scope.addNewAuthorEmail = () ->
|
||||
if not $scope.canAddNewEmail()
|
||||
return
|
||||
$scope.doorman.emails.push($scope.newEmail)
|
||||
$scope.updateDoorman()
|
||||
$scope.newEmail = ""
|
||||
$scope.initEmails()
|
||||
|
||||
$scope.removeEmail = (email) ->
|
||||
$scope.doorman.emails = _.reject($scope.doorman.emails , (email_) -> email_ == email)
|
||||
$scope.initEmails()
|
||||
$scope.updateDoorman()
|
||||
|
||||
$scope.canAddNewEmail = ()->
|
||||
if !$scope.newEmail or $scope.newEmail.length < 5
|
||||
return false
|
||||
not _.contains($scope.doorman.emails, $scope.newEmail)
|
||||
|
||||
|
||||
$scope.updateDoorman = () ->
|
||||
if !$scope.validate()
|
||||
return
|
||||
$http.put($scope.apiurl, $scope.doorman).then((ret)->
|
||||
params = {params: {user: $scope.user}}
|
||||
$http.put($scope.apiurl, $scope.doorman, params).then((ret)->
|
||||
console.log "update"
|
||||
).catch((res) ->
|
||||
d = 9
|
||||
$scope.error =
|
||||
message: res.data
|
||||
status : res.status
|
||||
)
|
||||
]
|
||||
return
|
||||
@@ -1,6 +1,11 @@
|
||||
define ["app"], (app) ->
|
||||
app.controller "HomeCtrl", ($scope) ->
|
||||
$scope.message = "message from home ctrl"
|
||||
return
|
||||
$scope.loggedEmail = localStorage.getItem("email")
|
||||
$scope.login = () ->
|
||||
if $scope.email
|
||||
localStorage.setItem("email", $scope.email)
|
||||
$scope.loggedEmail = $scope.email
|
||||
$scope.email = ""
|
||||
|
||||
return
|
||||
@@ -1,15 +1,17 @@
|
||||
epsilon = 0.0001
|
||||
|
||||
define ["app"], (app) ->
|
||||
define ["app", "../filters"], (app, filters) ->
|
||||
app.controller "NewDoormanCtrl", ["$scope", "$http", "$location", ($scope, $http, $location) ->
|
||||
$scope.error = ""
|
||||
$scope.value = {}
|
||||
$scope.user = localStorage.getItem("email")
|
||||
$scope.value = {probability: 0, name: ""}
|
||||
$scope.payload =
|
||||
name: ""
|
||||
values: []
|
||||
emails: [$scope.user]
|
||||
|
||||
$scope.isValidValue = () ->
|
||||
return $scope.value.name and $scope.value.probability
|
||||
return $scope.value.name
|
||||
|
||||
$scope.addNewValue = () ->
|
||||
if !$scope.isValidValue()
|
||||
@@ -34,7 +36,8 @@ define ["app"], (app) ->
|
||||
$scope.createDoorman = () ->
|
||||
if not $scope.isDoormanValid()
|
||||
return
|
||||
$http.post("/api/doormen", $scope.payload).
|
||||
params = {params: {user: $scope.user}}
|
||||
$http.post("/api/doormen", $scope.payload, params).
|
||||
then((res, s) ->
|
||||
$scope.redirectToDoorman(res.headers("location"))
|
||||
).catch((res) ->
|
||||
@@ -50,6 +53,18 @@ define ["app"], (app) ->
|
||||
return false
|
||||
return true
|
||||
|
||||
$scope.addEmail = () ->
|
||||
if not $scope.newEmail
|
||||
return
|
||||
$scope.payload.emails.push($scope.newEmail)
|
||||
$scope.newEmail = ""
|
||||
|
||||
$scope.removeEmail = (idx) ->
|
||||
if $scope.payload.emails[idx] == $scope.user
|
||||
return
|
||||
$scope.payload.emails.splice(idx, 1);
|
||||
|
||||
|
||||
|
||||
return
|
||||
]
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
define ['angularAMD', 'app'], (angular, app) ->
|
||||
app.filter 'reverse', ->
|
||||
(items) ->
|
||||
if !Array.isArray(items)
|
||||
return false
|
||||
items.slice().reverse()
|
||||
return
|
||||
@@ -7,6 +7,7 @@ require.config
|
||||
"angular-route": "/bower_components/angular-route/angular-route.min"
|
||||
"angularAMD" : "/bower_components/angularAMD/angularAMD.min"
|
||||
"foundation" : "/bower_components/angular-foundation/mm-foundation-tpls"
|
||||
"lodash" : "/bower_components/lodash/lodash.min"
|
||||
|
||||
|
||||
# Add angular modules that does not support AMD out of the box, put it in a shim
|
||||
|
||||
+2
-1
@@ -4,10 +4,11 @@
|
||||
<head>
|
||||
<title>doorman</title>
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css">
|
||||
<script data-main="/js/main" src="/bower_components/requirejs/require.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body style="visibility:hidden;">
|
||||
|
||||
|
||||
<top-bar>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"gulp-coffee": "~2.2.0",
|
||||
"gulp-sass": "^1.2.4",
|
||||
"gulp-task-listing": "~0.3.0",
|
||||
"gulp-util": "^3.0.3",
|
||||
"node-sass": "~1.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -8,52 +8,19 @@
|
||||
<div class="row wab-apispect">
|
||||
<table class="columns large-12">
|
||||
<thead>
|
||||
<tr>
|
||||
<tr >
|
||||
<th >api endpoint</th>
|
||||
<th>method</th>
|
||||
<th >description</th>
|
||||
</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>
|
||||
<td>Get the list of all the public doormen.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><wab-api endpoint="/doormen"></wab-api></td>
|
||||
<td><wab-method method="post"></wab-method></td>
|
||||
<td>Create a new doorman.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><wab-api endpoint="/doormen/:id"></wab-api></td>
|
||||
<td><wab-method method="get"></wab-method></td>
|
||||
<td>Get the doorman</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><wab-api endpoint="/doormen/:id"></wab-api></td>
|
||||
<td><wab-method method="put"></wab-method></td>
|
||||
<td>Update the doorman with new probabilities</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><wab-api endpoint="/doormen/:id"></wab-api></td>
|
||||
<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 ng-repeat="endpoint in endpoints">
|
||||
<td><wab-api endpoint="{{ endpoint.pattern }}"></wab-api></td>
|
||||
<td>
|
||||
<code style="width:5em; display: block; text-align: center;">{{ endpoint.method }}</code>
|
||||
</td>
|
||||
<td >{{ endpoint.description }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
|
||||
@@ -12,31 +12,73 @@
|
||||
</div>
|
||||
|
||||
<div class="row" ng-repeat="value in doorman.values">
|
||||
<div class="columns large-9 small-10">
|
||||
<div class="columns large-8 small-10">
|
||||
<label >name<input type="text" placeholder="value name" ng-value="value.name" disabled></label>
|
||||
</div>
|
||||
<div class="columns large-3 small-2">
|
||||
<label >probability<input type="number" placeholder="value probability" ng-model="value.probability" min="0" max="1" step="0.01"></label>
|
||||
<div class="columns large-4 small-2">
|
||||
<label >
|
||||
probability
|
||||
<input type="number"
|
||||
placeholder="value probability"
|
||||
ng-model="value.probability"
|
||||
ng-disabled="!isAdmin"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"></label>
|
||||
</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 class="large-4 columns">
|
||||
<ul>
|
||||
<li ng-repeat="email in leftEmailColumns">
|
||||
<span ng-bind="email"></span>
|
||||
<i class="fa fa-times right"
|
||||
ng-click="removeEmail(email)"
|
||||
style="cursor: pointer;"
|
||||
ng-show="doorman.emails.length > 1 && isAdmin && email != user"
|
||||
|
||||
></i>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="large-4 columns">
|
||||
<ul>
|
||||
<li ng-repeat="email in rightEmailColumns">
|
||||
<span ng-bind="email"></span>
|
||||
<i class="fa fa-times right"></i>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="large-4 columns">
|
||||
<div>
|
||||
<label>
|
||||
new admin
|
||||
<div class="row collapse">
|
||||
<div class="columns large-10">
|
||||
|
||||
<input type="text" ng-model="newEmail"/>
|
||||
</div>
|
||||
<div class="columns large-2">
|
||||
|
||||
<a class="button postfix" ng-click="addNewAuthorEmail()" ng-class=" canAddNewEmail() ? '' : 'disabled' " >+</a>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</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 class="row" ng-show="error">
|
||||
<div class="columns large-12">
|
||||
<code style="width:100%; height:100%; display:block; margin-bottom: 1em;">
|
||||
<b>{{ error.status }}</b><br> {{ error.message }}
|
||||
</code>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row" ng-show="isAdmin">
|
||||
<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>
|
||||
|
||||
@@ -4,8 +4,26 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
<fieldset>
|
||||
<legend>Login</legend>
|
||||
<div class="large-12 columns">
|
||||
{{ message }}
|
||||
<div class="row collapse">
|
||||
<div class="small-10 columns">
|
||||
<input type="text" placeholder="email" ng-model="email">
|
||||
</div>
|
||||
<div class="small-2 columns">
|
||||
<a class="button postfix" ng-click="login()">Login!</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="loggedEmail">
|
||||
<div class="large-12 columns">
|
||||
You are currently logged in as <code ng-bind="loggedEmail"></code>
|
||||
</div>
|
||||
</div>
|
||||
@@ -11,18 +11,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-repeat="value in payload.values">
|
||||
<div class="columns large-8">
|
||||
<label >value<input type="text" placeholder="value name" ng-model="value.name"></label>
|
||||
</div>
|
||||
<div class="columns large-3">
|
||||
<label >probability<input type="number" placeholder="value probability" ng-model="value.probability" min="0" max="1" step="0.01"></label>
|
||||
</div>
|
||||
<div class="columns large-1">
|
||||
<br>
|
||||
<a class="button round tiny" ng-click="removeValue( $index )">-</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="columns large-12">
|
||||
<fieldset>
|
||||
<legend>doorman state</legend>
|
||||
|
||||
<div class="row">
|
||||
<div class="columns large-8">
|
||||
@@ -38,6 +30,49 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-repeat="value in payload.values | reverse">
|
||||
<div class="columns large-8">
|
||||
<label >value<input type="text" placeholder="value name" ng-model="value.name"></label>
|
||||
</div>
|
||||
<div class="columns large-3">
|
||||
<label >probability<input type="number" placeholder="value probability" ng-model="value.probability" min="0" max="1" step="0.01"></label>
|
||||
</div>
|
||||
<div class="columns large-1">
|
||||
<br>
|
||||
<a class="button round tiny" ng-click="removeValue( payload.values.length - $index - 1 )">-</a>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="columns large-12">
|
||||
<fieldset>
|
||||
<legend>administrator</legend>
|
||||
|
||||
<div class="row">
|
||||
<div class="columns large-11">
|
||||
<label>admin email <input type="text" placeholder="wild cards are allow here" ng-model="newEmail"></label>
|
||||
</div>
|
||||
<div class="columns large-1">
|
||||
<br><a class="button round tiny" ng-click="addEmail()" ng-class="{disabled: !newEmail}">+</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-repeat="email in payload.emails | reverse">
|
||||
<div class="columns large-11">
|
||||
<label>admin email <input type="text" ng-disabled="email==user" placeholder="wild cards are allow here" ng-model="email"></label>
|
||||
</div>
|
||||
<div class="columns large-1">
|
||||
<br><a class="button round tiny" ng-click="removeEmail(payload.emails.length - $index -1)" ng-disabled="email==user? 'disabled' : ''">-</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="error">
|
||||
<div class="columns large-12">
|
||||
<code style="width:100%; height:100%; display:block; margin-bottom: 1em;">{{ error }}</code>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
@import "settings";
|
||||
@import url(http://fonts.googleapis.com/css?family=Orbitron:400,900,500,700);
|
||||
// @import url(http://fonts.googleapis.com/css?family=Orbitron:400,900,500,700);
|
||||
@font-face {
|
||||
font-family: 'Orbitron';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Orbitron-Light'), local('Orbitron-Regular'), url(/scss/font/orbitron/HmnHiRzvcnQr8CjBje6GQvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'), url(/scss/font/orbitron/94ug0rEgQO_WuI_xKJMFc_esZW2xOQ-xsNqO47m55DA.woff) format('woff');
|
||||
}
|
||||
|
||||
|
||||
h1, h2{
|
||||
font-family: 'Orbitron', sans-serif;
|
||||
|
||||
Reference in new issue
Block a user