Added getRoute to control api

This commit is contained in:
Héctor Hurtado
2019-10-24 17:51:55 +02:00
parent 2fc18b76e3
commit a54c6f24a1
5 changed files with 145 additions and 8 deletions
+19 -3
View File
@@ -36,11 +36,13 @@ func Run(bindAddr string) {
func configRouter() *mux.Router {
r := mux.NewRouter()
r.HandleFunc("/routes/{id}", removeRoute).
Methods("DELETE")
Methods(http.MethodDelete)
r.HandleFunc("/routes/{id}", getRoute).
Methods(http.MethodGet)
r.HandleFunc("/routes", listRoutes).
Methods("GET")
Methods(http.MethodGet)
r.HandleFunc("/routes", addRoute).
Methods("POST")
Methods(http.MethodPost)
return r
}
@@ -84,6 +86,7 @@ func addRoute(res http.ResponseWriter, req *http.Request) {
return
}
if route.Method == "" {
spec / test / features / control / get / success.feature
res.WriteHeader(http.StatusUnprocessableEntity)
return
}
@@ -112,3 +115,16 @@ func addRoute(res http.ResponseWriter, req *http.Request) {
res.Header().Set("Content-Type", "application/json")
_, _ = res.Write(createdBytes)
}
var funcGet func(string) (model.Route, error) = user.Routes.Get
func getRoute(res http.ResponseWriter, req *http.Request) {
id := mux.Vars(req)["id"]
if r, err := funcGet(id); err != nil {
res.WriteHeader(http.StatusNotFound)
} else {
res.Header().Set("Content-Type", "application/json")
rBytes, _ := json.Marshal(r)
_, _ = res.Write(rBytes)
}
}
+46 -4
View File
@@ -18,6 +18,7 @@ package control
import (
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
@@ -28,6 +29,7 @@ import (
"github.com/gorilla/mux"
"github.com/BBVA/kapow/internal/server/model"
"github.com/BBVA/kapow/internal/server/user"
)
func TestConfigRouterHasRoutesWellConfigured(t *testing.T) {
@@ -37,10 +39,10 @@ func TestConfigRouterHasRoutesWellConfigured(t *testing.T) {
mustMatch bool
vars []string
}{
{"/routes/ROUTE_YYYYYYYYYYYYYYY", http.MethodGet, 0, false, []string{}},
{"/routes/ROUTE_YYYYYYYYYYYYYYY", http.MethodPut, 0, false, []string{}},
{"/routes/ROUTE_YYYYYYYYYYYYYYY", http.MethodPost, 0, false, []string{}},
{"/routes/ROUTE_YYYYYYYYYYYYYYY", http.MethodDelete, reflect.ValueOf(removeRoute).Pointer(), true, []string{"id"}},
{"/routes/FOO", http.MethodGet, reflect.ValueOf(getRoute).Pointer(), true, []string{"id"}},
{"/routes/FOO", http.MethodPut, 0, false, []string{}},
{"/routes/FOO", http.MethodPost, 0, false, []string{}},
{"/routes/FOO", http.MethodDelete, reflect.ValueOf(removeRoute).Pointer(), true, []string{"id"}},
{"/routes", http.MethodGet, reflect.ValueOf(listRoutes).Pointer(), true, []string{}},
{"/routes", http.MethodPut, 0, false, []string{}},
{"/routes", http.MethodPost, reflect.ValueOf(addRoute).Pointer(), true, []string{}},
@@ -402,3 +404,43 @@ func TestListRoutesReturnsTwoElementsList(t *testing.T) {
t.Errorf("Response mismatch. Expected %#v, got: %#v", expectedRouteList, respJson)
}
}
func TestGetRouteReturns404sWhenRouteDoesntExist(t *testing.T) {
handler := mux.NewRouter()
handler.HandleFunc("/routes/{id}", getRoute).
Methods("GET")
r := httptest.NewRequest(http.MethodGet, "/routes/FOO", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
resp := w.Result()
if resp.StatusCode != http.StatusNotFound {
t.Errorf("HTTP status mismatch. Expected: %d, got: %d", http.StatusNotFound, resp.StatusCode)
}
}
func TestGetRouteReturnsTheRequestedRoute(t *testing.T) {
handler := mux.NewRouter()
handler.HandleFunc("/routes/{id}", getRoute).
Methods("GET")
r := httptest.NewRequest(http.MethodGet, "/routes/FOO", nil)
w := httptest.NewRecorder()
user.Routes.Append(model.Route{ID: "FOO"})
handler.ServeHTTP(w, r)
resp := w.Result()
respJson := model.Route{}
if resp.StatusCode != http.StatusOK {
t.Errorf("HTTP status mismatch. Expected: %d, got: %d", http.StatusOK, resp.StatusCode)
}
bBytes, _ := ioutil.ReadAll(resp.Body)
if err := json.Unmarshal(bBytes, &respJson); err != nil {
t.Errorf("Invalid JSON response. %s", string(bBytes))
}
if respJson.ID != "FOO" {
t.Errorf(`Route mismatch. Expected: "FOO". Got: %s`, respJson.ID)
}
}
+13
View File
@@ -80,3 +80,16 @@ func (srl *safeRouteList) Delete(ID string) error {
srl.m.Unlock()
return errors.New("Route not found")
}
func (srl *safeRouteList) Get(ID string) (r model.Route, err error) {
srl.m.RLock()
defer srl.m.RUnlock()
for _, r = range srl.rs {
if r.ID == ID {
return
}
}
err = errors.New("Route not found")
return
}
+66
View File
@@ -364,3 +364,69 @@ func TestDeleteUpdatesMuxWithRemainingRoutes(t *testing.T) {
t.Fatalf("Unexpected error: %v", err)
}
}
func TestGetReturnsAnErrorWhenEmptyList(t *testing.T) {
srl := New()
if _, err := srl.Get("FOO"); err == nil {
t.Error("Expected error not returned")
}
}
func TestGetReturnsAnErrorWhenRouteNotExists(t *testing.T) {
srl := New()
srl.Append(model.Route{ID: "FOO"})
if _, err := srl.Get("BAR"); err == nil {
t.Error("Expected error not returned")
}
}
func TestGetReturnsTheRequestedRoute(t *testing.T) {
srl := New()
srl.Append(model.Route{ID: "FOO"})
if r, err := srl.Get("FOO"); err != nil {
t.Errorf("Unexpected error %+v", err)
} else if r.ID != "FOO" {
t.Errorf(`Route mismatch. Expected: "FOO". Got %q`, r.ID)
}
}
func TestGetWaitsForTheWriterToFinish(t *testing.T) {
srl := New()
srl.Append(model.Route{ID: "FOO"})
srl.m.Lock()
defer srl.m.Unlock()
c := make(chan model.Route)
go func() { r, _ := srl.Get("FOO"); c <- r }()
time.Sleep(10 * time.Millisecond)
select {
case <-c:
t.Error("Route list readed while mutex was acquired")
default: // This default prevents the select from being blocking
}
}
func TestGetNonBlockingReadWithOtherReaders(t *testing.T) {
srl := New()
srl.Append(model.Route{ID: "FOO"})
srl.m.RLock()
defer srl.m.RUnlock()
c := make(chan model.Route)
go func() { r, _ := srl.Get("FOO"); c <- r }()
time.Sleep(10 * time.Millisecond)
select {
case <-c:
default: // This default prevents the select from being blocking
t.Error("Route list couldn't be readed while mutex was acquired for read")
}
}
@@ -21,7 +21,7 @@ Feature: Retrieve route details in Kapow! server.
Get route details by id.
Given I have a Kapow! server with the following routes:
| method | url_pattern | entrypoint | command |
| method | url_pattern | entrypoint | command |
| GET | /foo | /bin/sh -c | ls -la / \| kapow set /response/body |
| GET | /qux/{dirname} | /bin/sh -c | ls -la /request/params/dirname \| kapow set /response/body |
When I get the first route