diff --git a/main_test.go b/main_test.go index a52b07c..198b2bd 100644 --- a/main_test.go +++ b/main_test.go @@ -96,12 +96,12 @@ func muckedToken(t *testing.T, uri, obf string) { } func createUpdate(t *testing.T, uri, token string) { - id := callCreate(t, uri, token) + id := callCreate(t, uri, token, "") callUpdate(t, uri, token, id) } func createDelete(t *testing.T, uri, token string) { - id := callCreate(t, uri, token) + id := callCreate(t, uri, token, "") callDelete(t, uri, token, id) one := callGet(t, uri, token, id, http.StatusNotFound) if one.ID == id { @@ -159,7 +159,7 @@ func callUpdate(t *testing.T, uri, token, id string) { "title": "titl-" + uuid.New().String()[:5], "text": "text-" + uuid.New().String()[:5], "connections": map[string]map[string]string{ - "???": map[string]string{"relationship": ":?"}, + callCreate(t, uri, token, ""): map[string]string{"relationship": "callUpdate peer"}, }, "attachments": map[string]map[string]string{ "myfile": map[string]string{"location": "/files/my_file_location.txt"}, @@ -180,8 +180,34 @@ func callUpdate(t *testing.T, uri, token, id string) { found := false for k := range response { for k2 := range m { - if fmt.Sprint(m[k2]) != fmt.Sprint(response[k][k2]) { - t.Fatal(k2, response[k][k2]) + if k2 == entity.Connections { + if v, ok := response[k][k2]; !ok { + t.Fatal("missing connection", k2) + } else if m2, ok := v.(map[string]interface{}); !ok { + t.Fatalf("wrong connection type %T", v) + } else { + wantConnections := m[k2].(map[string]map[string]string) + for k3 := range wantConnections { + wantRelationship := wantConnections[k3][entity.Relationship] + gotRelationship := m2[k3].(map[string]interface{})[entity.Relationship] + if wantRelationship != gotRelationship { + t.Fatal(gotRelationship, wantRelationship) + } + b, _ := json.Marshal(m2[k3]) + var gotOne entity.One + json.Unmarshal(b, &gotOne) + if k3 != gotOne.ID { + t.Fatal(gotOne.ID) + } + if "" == gotOne.Text { + t.Fatal(gotOne) + } + } + } + } else { + if fmt.Sprint(m[k2]) != fmt.Sprint(response[k][k2]) { + t.Fatalf("@%q.%q, want %q, got %q", k, k2, fmt.Sprint(m[k2]), fmt.Sprint(response[k][k2])) + } } } found = true @@ -192,15 +218,17 @@ func callUpdate(t *testing.T, uri, token, id string) { } } -func callCreate(t *testing.T, uri, token string) string { +func callCreate(t *testing.T, uri, token, peerID string) string { + connections := map[string]map[string]string{} + if peerID != "" { + connections[peerID] = map[string]string{"relationship": "call Create peer"} + } m := map[string]interface{}{ - "name": "name-" + uuid.New().String()[:5], - "type": "type-" + uuid.New().String()[:5], - "title": "titl-" + uuid.New().String()[:5], - "text": "text-" + uuid.New().String()[:5], - "connections": map[string]map[string]string{ - "???": map[string]string{"relationship": ":?"}, - }, + "name": "name-" + uuid.New().String()[:5], + "type": "type-" + uuid.New().String()[:5], + "title": "titl-" + uuid.New().String()[:5], + "text": "text-" + uuid.New().String()[:5], + "connections": connections, "attachments": map[string]map[string]string{ "myfile": map[string]string{"location": "/files/my_file_location.txt"}, }, diff --git a/server/entities.go b/server/entities.go index 1a88689..0403279 100644 --- a/server/entities.go +++ b/server/entities.go @@ -6,7 +6,6 @@ import ( "io" "local/dndex/storage/entity" "local/dndex/storage/operator" - "log" "net/http" "path" "strings" @@ -107,12 +106,20 @@ func (rest *REST) entitiesGetOne(w http.ResponseWriter, r *http.Request) { rest.respNotFound(w) return } + resp := one.Generic() if _, ok := r.URL.Query()["light"]; !ok { - log.Println("TODO need to get all connections") - //http.Error(w, "not impl", http.StatusNotImplemented) - //return + m := bson.M{} + for id := range one.Connections { + one2, err := rest.g.Get(r.Context(), scope.Namespace, id) + if err == nil { + m2 := one2.Generic() + m2[entity.Relationship] = one.Connections[id].Relationship + m[id] = m2 + } + } + resp[entity.Connections] = m } - rest.respMap(w, entityScope[0], one) + rest.respMap(w, entityScope[0], resp) } func (rest *REST) entitiesGetOneSub(w http.ResponseWriter, r *http.Request) { diff --git a/server/entities_test.go b/server/entities_test.go index 78a815e..d6613fc 100644 --- a/server/entities_test.go +++ b/server/entities_test.go @@ -56,12 +56,20 @@ func TestEntities(t *testing.T) { }) t.Run("create+get0", func(t *testing.T) { - w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname", "attachments": {"abc": {}}, "connections": {"def": {"relationship": "ghi"}}}`) + w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"my friend"}`) + if w.Code != http.StatusOK { + t.Fatal(w.Code) + } + friend := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { + return one.Name == "my friend" + }) + + w = testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname", "attachments": {"abc": {}}, "connections": {"`+friend+`": {"relationship": "ghi"}}}`) if w.Code != http.StatusOK { t.Fatal(w.Code) } id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { - return one.Name == "myname" && len(one.Attachments) == 1 && len(one.Connections) == 1 && one.Connections["def"].Relationship == "ghi" + return one.Name == "myname" && len(one.Attachments) == 1 && len(one.Connections) == 1 && one.Connections[friend].Relationship == "ghi" }) w = testEntitiesMethod(t, authit, rest, http.MethodGet, "/", ``) @@ -168,21 +176,29 @@ func TestEntities(t *testing.T) { }) t.Run("create+update/replace.connection.abc.relationship", func(t *testing.T) { + w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name": "my friend"}`) + if w.Code != http.StatusOK { + t.Fatal(w.Code) + } + friend := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { + return one.Name == "my friend" + }) + for _, method := range []string{http.MethodPut, http.MethodPatch} { - w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"connections": {"abc": {"relationship": "def"}}}`) + w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"connections": {"`+friend+`": {"relationship": "def"}}}`) if w.Code != http.StatusOK { t.Fatal(w.Code) } id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { - return one.Connections["abc"].Relationship == "def" + return one.Connections[friend].Relationship == "def" }) - w = testEntitiesMethod(t, authit, rest, method, "/"+id+"/connections/abc/relationship", `"new"`) + w = testEntitiesMethod(t, authit, rest, method, "/"+id+"/connections/"+friend+"/relationship", `"new"`) if w.Code != http.StatusOK { t.Fatal(w.Code) } id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { - return one.Connections["abc"].Relationship == "new" + return one.Connections[friend].Relationship == "new" }) if id != id2 { @@ -192,21 +208,29 @@ func TestEntities(t *testing.T) { }) t.Run("create+update/replace.connection.abc", func(t *testing.T) { + w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name": "my friend"}`) + if w.Code != http.StatusOK { + t.Fatal(w.Code) + } + friend := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { + return one.Name == "my friend" + }) + for _, method := range []string{http.MethodPut, http.MethodPatch} { - w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"connections": {"abc": {"relationship": "def"}}}`) + w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"connections": {"`+friend+`": {"relationship": "def"}}}`) if w.Code != http.StatusOK { t.Fatal(w.Code) } id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { - return one.Connections["abc"].Relationship == "def" + return one.Connections[friend].Relationship == "def" }) - w = testEntitiesMethod(t, authit, rest, method, "/"+id+"/connections/abc", `{"relationship": "new"}`) + w = testEntitiesMethod(t, authit, rest, method, "/"+id+"/connections/"+friend, `{"relationship": "new"}`) if w.Code != http.StatusOK { t.Fatal(w.Code) } id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { - return one.Connections["abc"].Relationship == "new" + return one.Connections[friend].Relationship == "new" }) if id != id2 { @@ -238,16 +262,24 @@ func TestEntities(t *testing.T) { }) t.Run("create w connection.abc+delete.connections.abc", func(t *testing.T) { - w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname", "title": "mytitle", "connections": {"abc": {"relationship": "good"}}}`) + w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name": "my friend"}`) + if w.Code != http.StatusOK { + t.Fatal(w.Code) + } + friend := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { + return one.Name == "my friend" + }) + + w = testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname", "title": "mytitle", "connections": {"`+friend+`": {"relationship": "good"}}}`) if w.Code != http.StatusOK { t.Fatalf("%v: %s", w.Code, w.Body.Bytes()) } id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { - abc, ok := one.Connections["abc"] - return one.Name == "myname" && one.Title == "mytitle" && ok && abc.Relationship == "good" + friended, ok := one.Connections[friend] + return one.Name == "myname" && one.Title == "mytitle" && ok && friended.Relationship == "good" }) - w = testEntitiesMethod(t, authit, rest, http.MethodDelete, "/"+id+"/connections/abc", ``) + w = testEntitiesMethod(t, authit, rest, http.MethodDelete, "/"+id+"/connections/"+friend, ``) if w.Code != http.StatusOK { t.Fatalf("%v: %s", w.Code, w.Body.Bytes()) } @@ -261,21 +293,29 @@ func TestEntities(t *testing.T) { }) t.Run("create w connection.abc.relationship+delete.connections.abc.relationship", func(t *testing.T) { - w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"connections": {"abc": {"relationship": "good"}}}`) + w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name": "my friend"}`) + if w.Code != http.StatusOK { + t.Fatal(w.Code) + } + friend := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { + return one.Name == "my friend" + }) + + w = testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"connections": {"`+friend+`": {"relationship": "good"}}}`) if w.Code != http.StatusOK { t.Fatalf("%v: %s", w.Code, w.Body.Bytes()) } id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { - abc, ok := one.Connections["abc"] - return ok && abc.Relationship == "good" + friended, ok := one.Connections[friend] + return ok && friended.Relationship == "good" }) - w = testEntitiesMethod(t, authit, rest, http.MethodDelete, "/"+id+"/connections/abc/relationship", ``) + w = testEntitiesMethod(t, authit, rest, http.MethodDelete, "/"+id+"/connections/"+friend+"/relationship", ``) if w.Code != http.StatusOK { t.Fatalf("%v: %s", w.Code, w.Body.Bytes()) } id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool { - return len(one.Connections) == 1 && one.Connections["abc"].Relationship == "" + return len(one.Connections) == 1 && one.Connections[friend].Relationship == "" }) if id != id2 { diff --git a/storage/entity/one.go b/storage/entity/one.go index 09ce9a9..5b34230 100644 --- a/storage/entity/one.go +++ b/storage/entity/one.go @@ -55,6 +55,13 @@ func (o One) Peers() []string { return ids } +func (o One) Generic() bson.M { + b, _ := bson.Marshal(o) + var m bson.M + bson.Unmarshal(b, &m) + return m +} + func (o One) MarshalBSON() ([]byte, error) { isMin := fmt.Sprint(o) == fmt.Sprint(o.Query()) if !isMin {