diff --git a/storage/entity/one.go b/storage/entity/one.go index a6ed574..2756595 100644 --- a/storage/entity/one.go +++ b/storage/entity/one.go @@ -35,6 +35,16 @@ func (o One) Query() One { return One{Name: o.Name} } +func (o One) Peer() One { + return One{ + Name: o.Name, + Type: o.Type, + Title: o.Title, + Relationship: o.Relationship, + Modified: o.Modified, + } +} + func (o One) Peers() []string { names := make([]string, len(o.Connections)) i := 0 diff --git a/view/who.go b/view/who.go index 73bbd4e..44d068e 100644 --- a/view/who.go +++ b/view/who.go @@ -32,6 +32,10 @@ func who(g storage.Graph, w http.ResponseWriter, r *http.Request) error { return whoPost(namespace, g, w, r) case http.MethodDelete: return whoDelete(namespace, g, w, r) + case http.MethodPatch: + return whoPatch(namespace, g, w, r) + case http.MethodHead: + return whoHead(namespace, g, w, r) default: http.NotFound(w, r) return nil @@ -150,3 +154,45 @@ func whoDelete(namespace string, g storage.Graph, w http.ResponseWriter, r *http return json.NewEncoder(w).Encode(`{"status":"ok"}`) } + +func whoPatch(namespace string, g storage.Graph, w http.ResponseWriter, r *http.Request) error { + id := r.URL.Query().Get("id") + if id == "" { + http.Error(w, `{"error":"no ?id provided"}`, http.StatusBadRequest) + return nil + } + + one := entity.One{} + if err := json.NewDecoder(r.Body).Decode(&one); err != nil { + return err + } + if one.Name == "" { + http.Error(w, `{"error":"no name provided"}`, http.StatusBadRequest) + return nil + } + relationship := one.Relationship + one.Relationship = "" + if err := g.Insert(r.Context(), namespace, one); err != nil { + return err + } + one.Relationship = relationship + if err := g.Update(r.Context(), namespace, entity.One{Name: id}, operator.Set{Key: "connections." + one.Name, Value: one.Peer()}); err != nil { + return err + } + + return whoGet(namespace, g, w, r) +} + +func whoHead(namespace string, g storage.Graph, w http.ResponseWriter, r *http.Request) error { + ones, err := g.List(r.Context(), namespace) + if err != nil { + return err + } + names := make([]string, len(ones)) + for i := range ones { + names[i] = ones[i].Name + } + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + return enc.Encode(names) +} diff --git a/view/who_test.go b/view/who_test.go index 6b7b6bf..00d456d 100644 --- a/view/who_test.go +++ b/view/who_test.go @@ -21,13 +21,15 @@ import ( func TestWho(t *testing.T) { os.Args = os.Args[:1] - f, err := ioutil.TempFile(os.TempDir(), "pattern*") - if err != nil { - t.Fatal(err) + if _, ok := os.LookupEnv("DBURI"); !ok { + f, err := ioutil.TempFile(os.TempDir(), "pattern*") + if err != nil { + t.Fatal(err) + } + f.Close() + defer os.Remove(f.Name()) + os.Setenv("DBURI", f.Name()) } - f.Close() - defer os.Remove(f.Name()) - os.Setenv("DBURI", f.Name()) t.Logf("config: %+v", config.New()) @@ -180,6 +182,76 @@ func TestWho(t *testing.T) { t.Fatalf("%d: %s", w.Code, w.Body.Bytes()) } }) + + t.Run("patch fake", func(t *testing.T) { + r := httptest.NewRequest(http.MethodPatch, "/who/col?id=FAKER"+want.Name, nil) + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + if w.Code == http.StatusOK { + t.Fatalf("%d: %s", w.Code, w.Body.Bytes()) + } + }) + + want = ones[2] + t.Run("patch real", func(t *testing.T) { + iwant := want + iwant.Relationship = "spawn" + iwant.Name = "child of " + want.Name + b, err := json.Marshal(iwant) + if err != nil { + t.Fatal(err) + } + r := httptest.NewRequest(http.MethodPatch, "/who/col?id="+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()) + } + got := entity.One{} + if err := json.NewDecoder(w.Body).Decode(&got); err != nil { + t.Fatal(err) + } + if fmt.Sprint(got) == fmt.Sprint(entity.One{}) { + t.Fatal(got) + } + iwant = want + if len(got.Connections) != len(want.Connections)+1 { + t.Fatal(len(got.Connections), len(want.Connections)+1) + } + got.Connections = want.Connections + got.Modified = 0 + want.Modified = 0 + if fmt.Sprint(got) != fmt.Sprint(want) { + t.Fatalf("withotu connections and modified, got != want: want \n %+v, got \n %+v", want, got) + } + t.Logf("%s", w.Body.Bytes()) + }) + + t.Run("head fake", func(t *testing.T) { + r := httptest.NewRequest(http.MethodHead, "/who/notcol", nil) + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + if w.Code != http.StatusOK { + t.Fatalf("%d: %s", w.Code, w.Body.Bytes()) + } + }) + + t.Run("head real", func(t *testing.T) { + r := httptest.NewRequest(http.MethodHead, "/who/col", nil) + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + if w.Code != http.StatusOK { + t.Fatalf("%d: %s", w.Code, w.Body.Bytes()) + } + var v []string + if err := json.Unmarshal(w.Body.Bytes(), &v); err != nil { + t.Fatalf("%v: %s", err, w.Body.Bytes()) + } + if len(v) < 5 { + t.Fatal(len(v)) + } + t.Logf("%+v", v) + }) } func fillDB(t *testing.T, g storage.Graph) []entity.One {