Add delete connection function to DELETE /who

master
breel 2020-07-25 21:51:55 -06:00
parent 93dff873e2
commit c0fc3530fd
3 changed files with 197 additions and 1 deletions

View File

@ -78,10 +78,11 @@ paths:
delete:
tags:
- who
summary: "Delete the specified entity, which is assumed to be a leaf"
summary: "Delete the specified entity, which is assumed to be a leaf, or only its connection if specified"
parameters:
- $ref: "#/components/parameters/id"
- $ref: "#/components/parameters/namespace"
- $ref: "#/components/parameters/connection"
responses:
200:
$ref: "#/components/schemas/ok"
@ -98,6 +99,13 @@ components:
namespace:
$ref: "./swagger.yaml#/components/parameters/namespace"
connection:
name: connection
in: query
description: "An entity's connection's name"
schema:
type: string
light:
name: light
in: query

View File

@ -152,6 +152,11 @@ func whoDelete(namespace string, g storage.Graph, w http.ResponseWriter, r *http
return json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
}
_, ok := r.URL.Query()["connection"]
if ok {
return whoDeleteConnections(namespace, g, w, r)
}
if err := g.Delete(r.Context(), namespace, operator.CaseInsensitive{Key: entity.Name, Value: id}); err != nil {
return err
}
@ -159,6 +164,31 @@ func whoDelete(namespace string, g storage.Graph, w http.ResponseWriter, r *http
return json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}
func whoDeleteConnections(namespace string, g storage.Graph, w http.ResponseWriter, r *http.Request) error {
id, err := getID(r)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
}
connections, ok := r.URL.Query()["connection"]
if !ok {
w.WriteHeader(http.StatusBadRequest)
return json.NewEncoder(w).Encode(map[string]string{"error": "must provide connections to delete"})
}
one := entity.One{Name: id}
for _, connection := range connections {
path := fmt.Sprintf("%s.%s", entity.Connections, connection)
if err := g.Update(r.Context(), namespace, one.Query(), operator.Unset(path)); err != nil {
return err
}
}
return whoGet(namespace, g, w, r)
}
func whoPatch(namespace string, g storage.Graph, w http.ResponseWriter, r *http.Request) error {
id, err := getID(r)
if err != nil {

View File

@ -38,11 +38,20 @@ func TestWho(t *testing.T) {
ones := fillDB(t, g)
want := ones[len(ones)-1]
reset := func() {
if err := g.Delete(context.TODO(), "col", map[string]string{}); err != nil {
t.Fatal(err)
}
ones = fillDB(t, g)
want = ones[len(ones)-1]
}
handler := jsonHandler(g)
t.Log(handler, want)
t.Run("get no namespace is 404", func(t *testing.T) {
reset()
iwant := want
r := httptest.NewRequest(http.MethodGet, "/who?id="+iwant.Name, nil)
w := httptest.NewRecorder()
@ -53,6 +62,7 @@ func TestWho(t *testing.T) {
})
t.Run("get fake", func(t *testing.T) {
reset()
iwant := want
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col&id=FAKER"+iwant.Name, nil)
w := httptest.NewRecorder()
@ -63,6 +73,7 @@ func TestWho(t *testing.T) {
})
t.Run("get real", func(t *testing.T) {
reset()
iwant := want
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col&id="+iwant.Name, nil)
w := httptest.NewRecorder()
@ -91,6 +102,7 @@ func TestWho(t *testing.T) {
})
t.Run("get real light", func(t *testing.T) {
reset()
iwant := want
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col&light&id="+iwant.Name, nil)
w := httptest.NewRecorder()
@ -124,6 +136,7 @@ func TestWho(t *testing.T) {
})
t.Run("get real but case is wrong", func(t *testing.T) {
reset()
iwant := want
iwantName := strings.ToUpper(iwant.Name)
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col&id="+iwantName, nil)
@ -153,6 +166,7 @@ func TestWho(t *testing.T) {
})
t.Run("put fake", func(t *testing.T) {
reset()
iwant := want
r := httptest.NewRequest(http.MethodPut, "/who?namespace=col&id=FAKER"+iwant.Name, strings.NewReader(`{"title":"this should fail to find someone"}`))
w := httptest.NewRecorder()
@ -163,6 +177,7 @@ func TestWho(t *testing.T) {
})
t.Run("put real", func(t *testing.T) {
reset()
iwant := want
r := httptest.NewRequest(http.MethodPut, "/who?namespace=col&id="+iwant.Name, strings.NewReader(`{"title":"this should work"}`))
w := httptest.NewRecorder()
@ -185,6 +200,7 @@ func TestWho(t *testing.T) {
})
t.Run("post exists", func(t *testing.T) {
reset()
iwant := want
iwant.Name = ""
r := httptest.NewRequest(http.MethodPost, "/who?namespace=col&id="+want.Name, strings.NewReader(`{"title":"this should fail to insert"}`))
@ -196,6 +212,7 @@ func TestWho(t *testing.T) {
})
t.Run("post real", func(t *testing.T) {
reset()
iwant := want
iwant.Name = ""
b, err := json.Marshal(iwant)
@ -223,6 +240,7 @@ func TestWho(t *testing.T) {
})
t.Run("delete real", func(t *testing.T) {
reset()
r := httptest.NewRequest(http.MethodDelete, "/who?namespace=col&id=NEWBIE"+want.Name, nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
@ -238,6 +256,7 @@ func TestWho(t *testing.T) {
})
t.Run("delete fake", func(t *testing.T) {
reset()
r := httptest.NewRequest(http.MethodDelete, "/who?namespace=col&id=FAKER"+want.Name, nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
@ -247,6 +266,7 @@ func TestWho(t *testing.T) {
})
t.Run("delete regexp should be sanitized", func(t *testing.T) {
reset()
r := httptest.NewRequest(http.MethodDelete, "/who?namespace=col&id=.*", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
@ -271,6 +291,7 @@ func TestWho(t *testing.T) {
})
t.Run("patch fake", func(t *testing.T) {
reset()
r := httptest.NewRequest(http.MethodPatch, "/who?namespace=col&id=FAKER"+want.Name, nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
@ -281,6 +302,7 @@ func TestWho(t *testing.T) {
want = ones[4]
t.Run("patch real against existing", func(t *testing.T) {
reset()
from := ones[4]
push := ones[10].Peer()
push.Relationship = "spawn"
@ -327,6 +349,7 @@ func TestWho(t *testing.T) {
want = ones[2]
t.Run("patch real", func(t *testing.T) {
reset()
iwant := want
iwant.Relationship = "spawn"
iwant.Name = "child of " + want.Name
@ -361,6 +384,7 @@ func TestWho(t *testing.T) {
})
t.Run("trace fake", func(t *testing.T) {
reset()
r := httptest.NewRequest(http.MethodTrace, "/who?namespace=notcol", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
@ -370,6 +394,7 @@ func TestWho(t *testing.T) {
})
t.Run("trace real", func(t *testing.T) {
reset()
r := httptest.NewRequest(http.MethodTrace, "/who?namespace=col", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
@ -387,6 +412,7 @@ func TestWho(t *testing.T) {
})
t.Run("get without id == trace real", func(t *testing.T) {
reset()
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
@ -404,6 +430,7 @@ func TestWho(t *testing.T) {
})
t.Run("trace real sorted asc/desc name", func(t *testing.T) {
reset()
for _, order := range []string{"1", "-1"} {
r := httptest.NewRequest(http.MethodTrace, "/who?namespace=col&sort=name&order="+order, nil)
w := httptest.NewRecorder()
@ -428,6 +455,137 @@ func TestWho(t *testing.T) {
}
}
})
t.Run("delete connection 1 of 0 noop but ok", func(t *testing.T) {
reset()
want := ones[0]
if len(want.Connections) > 0 {
t.Fatal(want)
}
r := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/who?namespace=col&id=%s&connection=%s", want.Name, "fake"), nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
}
r = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/who?namespace=col&id=%s", want.Name), nil)
w = httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
}
o := entity.One{}
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
t.Fatal(err)
}
if len(o.Connections) > 0 {
t.Fatal(o.Connections)
}
})
t.Run("delete connection 1 of 1 ok", func(t *testing.T) {
reset()
r := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/who?namespace=col&id=%s&connection=%s", want.Name, want.Peers()[0]), nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
}
r = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/who?namespace=col&id=%s", want.Name), nil)
w = httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
}
o := entity.One{}
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
t.Fatal(err)
}
if _, ok := o.Connections[want.Peers()[0]]; ok {
t.Fatal(want.Peers()[0], o.Connections)
}
})
t.Run("delete connection 1 of 4 ok", func(t *testing.T) {
reset()
want := ones[0]
put := entity.One{
Name: want.Name,
Connections: map[string]entity.One{
ones[1].Name: ones[1].Peer(),
ones[2].Name: ones[2].Peer(),
ones[3].Name: ones[3].Peer(),
ones[4].Name: ones[4].Peer(),
},
}
b, err := json.Marshal(put)
if err != nil {
t.Fatal(err)
}
r := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/who?namespace=col&id=%s", want.Name), bytes.NewReader(b))
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
}
want.Connections = put.Connections
r = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/who?namespace=col&id=%s&light", want.Name), nil)
w = httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
}
o := entity.One{}
if err := json.NewDecoder(w.Body).Decode(&o); err != nil {
t.Fatal(err)
}
o.Modified = 0
want.Modified = 0
if len(o.Connections) != len(put.Connections) {
t.Fatal(o.Connections)
}
for k := range o.Connections {
a := want.Connections[k]
a.Modified = 0
want.Connections[k] = a
b := o.Connections[k]
b.Modified = 0
o.Connections[k] = b
}
if fmt.Sprint(o) != fmt.Sprint(want) {
t.Fatalf("GET put != expected: want:\n%+v, got \n%+v", want, o)
}
r = httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/who?namespace=col&id=%s&connection=%s", want.Name, want.Peers()[0]), nil)
w = httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
}
r = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/who?namespace=col&id=%s", want.Name), nil)
w = httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
}
o = entity.One{}
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
t.Fatal(err)
}
if _, ok := o.Connections[want.Peers()[0]]; ok {
t.Fatal(want.Peers()[0], o.Connections)
}
if len(o.Connections) != len(put.Connections)-1 {
t.Fatal(o.Connections)
}
})
}
func fillDB(t *testing.T, g storage.Graph) []entity.One {