diff --git a/pkg/index/indextest/tests.go b/pkg/index/indextest/tests.go index 9345838ee..929e1be5e 100644 --- a/pkg/index/indextest/tests.go +++ b/pkg/index/indextest/tests.go @@ -137,6 +137,12 @@ func (id *IndexDeps) AddAttribute(permaNode *blobref.BlobRef, attr, value string return id.uploadAndSignMap(m) } +func (id *IndexDeps) DelAttribute(permaNode *blobref.BlobRef, attr string) *blobref.BlobRef { + m := schema.NewDelAttributeClaim(permaNode, attr) + m["claimDate"] = id.advanceTime() + return id.uploadAndSignMap(m) +} + func (id *IndexDeps) UploadFile(fileName string, contents string) (fileRef, wholeRef *blobref.BlobRef) { cb := &test.Blob{Contents: contents} id.BlobSource.AddBlob(cb) diff --git a/pkg/search/handler.go b/pkg/search/handler.go index 92d7260f2..2eadc45a1 100644 --- a/pkg/search/handler.go +++ b/pkg/search/handler.go @@ -123,6 +123,9 @@ func (sh *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { case "camli/search/signerpaths": sh.serveSignerPaths(rw, req) return + case "camli/search/edgesto": + sh.serveEdgesTo(rw, req) + return } } @@ -775,6 +778,81 @@ func (sh *Handler) serveSignerAttrValue(rw http.ResponseWriter, req *http.Reques } } +// Unlike the index interface's EdgesTo method, the "edgesto" Handler +// here additionally filters out since-deleted permanode edges. +func (sh *Handler) serveEdgesTo(rw http.ResponseWriter, req *http.Request) { + ret := jsonMap() + defer httputil.ReturnJSON(rw, ret) + defer setPanicError(ret) + + toRef := blobref.MustParse(mustGet(req, "blobref")) + toRefStr := toRef.String() + blobInfo := jsonMap() + ret[toRefStr] = blobInfo + + jsonEdges := jsonMapList() + + edges, err := sh.index.EdgesTo(toRef, nil) + if err != nil { + panic(err) + } + + type mapOrError struct { + m map[string]interface{} // nil if no map + err error + } + resc := make(chan mapOrError) + verify := func(edge *Edge) { + fromStr := edge.From.String() + db, err := sh.NewDescribeRequest().DescribeSync(edge.From) + if err != nil { + resc <- mapOrError{err: err} + return + } + found := false + if db.Permanode != nil { + for attr, vv := range db.Permanode.Attr { + if IsBlobReferenceAttribute(attr) { + for _, v := range vv { + if v == toRefStr { + found = true + } + } + } + } + } + var em map[string]interface{} + if found { + em = jsonMap() + em["from"] = fromStr + em["fromType"] = "permanode" + } + resc <- mapOrError{m: em} + } + verifying := 0 + for _, edge := range edges { + if edge.FromType == "permanode" { + verifying++ + go verify(edge) + continue + } + em := jsonMap() + em["from"] = edge.From.String() + em["fromType"] = edge.FromType + jsonEdges = append(jsonEdges, em) + } + for i := 0; i < verifying; i++ { + res := <-resc + if res.err != nil { + panic(res.err) // caught and put in JSON response + } + if res.m != nil { + jsonEdges = append(jsonEdges, res.m) + } + } + blobInfo["edgesTo"] = jsonEdges +} + func (sh *Handler) serveSignerPaths(rw http.ResponseWriter, req *http.Request) { ret := jsonMap() defer httputil.ReturnJSON(rw, ret) diff --git a/pkg/search/handler_test.go b/pkg/search/handler_test.go index cb1cc354d..407e7a0b7 100644 --- a/pkg/search/handler_test.go +++ b/pkg/search/handler_test.go @@ -165,6 +165,33 @@ var handlerTests = []handlerTest{ } }`), }, + + // edgeto handler: put a permanode (member) in two parent + // permanodes, then delete the second and verify that edges + // back from member only reveal the first parent. + { + setup: func(*test.FakeIndex) Index { + // Ignore the fakeindex and use the real (but in-memory) implementation, + // using IndexDeps to populate it. + idx := index.NewMemoryIndex() + id := indextest.NewIndexDeps(idx) + + parent1 := id.NewPlannedPermanode("pn1") // sha1-7ca7743e38854598680d94ef85348f2c48a44513 + parent2 := id.NewPlannedPermanode("pn2") + member := id.NewPlannedPermanode("member") // always sha1-9ca84f904a9bc59e6599a53f0a3927636a6dbcae + id.AddAttribute(parent1, "camliMember", member.String()) + id.AddAttribute(parent2, "camliMember", member.String()) + id.DelAttribute(parent2, "camliMember") + return indexAndOwner{idx, id.SignerBlobRef} + }, + query: "edgesto?blobref=sha1-9ca84f904a9bc59e6599a53f0a3927636a6dbcae", + want: parseJSON(`{"sha1-9ca84f904a9bc59e6599a53f0a3927636a6dbcae": { + "edgesTo": [ + {"from": "sha1-7ca7743e38854598680d94ef85348f2c48a44513", + "fromType": "permanode"} + ] + }}`), + }, } func TestHandler(t *testing.T) {