mirror of https://github.com/perkeep/perkeep.git
index: PathsOfSignerTarget
Change-Id: Ie82117ed5c0c5479e456c7a0d8ee554cdc783cc5
This commit is contained in:
parent
fcba3443d0
commit
ffb43a01a8
|
@ -283,9 +283,55 @@ func (x *Index) PermanodeOfSignerAttrValue(signer *blobref.BlobRef, attr, val st
|
|||
return nil, os.ENOENT
|
||||
}
|
||||
|
||||
func (x *Index) PathsOfSignerTarget(signer, target *blobref.BlobRef) ([]*search.Path, os.Error) {
|
||||
log.Printf("index: TODO PathsOfSignerTarget")
|
||||
return nil, os.NewError("TODO: PathsOfSignerTarget")
|
||||
func (x *Index) PathsOfSignerTarget(signer, target *blobref.BlobRef) (paths []*search.Path, err os.Error) {
|
||||
paths = []*search.Path{}
|
||||
keyId, err := x.keyId(signer)
|
||||
if err != nil {
|
||||
if err == ErrNotFound {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
mostRecent := make(map[string]*search.Path)
|
||||
maxClaimDates := make(map[string]string)
|
||||
|
||||
it := x.queryPrefix(keySignerTargetPaths, keyId, target)
|
||||
defer it.Close()
|
||||
for it.Next() {
|
||||
keyPart := strings.Split(it.Key(), "|")
|
||||
valPart := strings.Split(it.Value(), "|")
|
||||
if len(keyPart) < 3 || len(valPart) < 4 {
|
||||
continue
|
||||
}
|
||||
claimRef := blobref.Parse(keyPart[2])
|
||||
baseRef := blobref.Parse(valPart[1])
|
||||
if claimRef == nil || baseRef == nil {
|
||||
continue
|
||||
}
|
||||
claimDate := valPart[0]
|
||||
active := valPart[2]
|
||||
suffix := urld(valPart[3])
|
||||
key := baseRef.String() + "/" + suffix
|
||||
|
||||
if claimDate > maxClaimDates[key] {
|
||||
maxClaimDates[key] = claimDate
|
||||
if active == "Y" {
|
||||
mostRecent[key] = &search.Path{
|
||||
Claim: claimRef,
|
||||
ClaimDate: claimDate,
|
||||
Base: baseRef,
|
||||
Suffix: suffix,
|
||||
}
|
||||
} else {
|
||||
mostRecent[key] = nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, v := range mostRecent {
|
||||
paths = append(paths, v)
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (x *Index) PathsLookup(signer, base *blobref.BlobRef, suffix string) ([]*search.Path, os.Error) {
|
||||
|
|
|
@ -148,6 +148,7 @@ func TestIndex(t *testing.T) {
|
|||
rootClaim := id.SetAttribute(pn, "camliRoot", "rootval")
|
||||
rootClaimTime := id.lastTimeNanos()
|
||||
t.Logf("set attribute %q", rootClaim)
|
||||
|
||||
id.dumpIndex(t)
|
||||
|
||||
key := "signerkeyid:sha1-ad87ca5c78bd0ce1195c46f7c98e6025abbaf007"
|
||||
|
@ -277,6 +278,39 @@ func TestIndex(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPathsOfSignerTarget(t *testing.T) {
|
||||
id := NewIndexDeps()
|
||||
pn := id.NewPermanode()
|
||||
t.Logf("uploaded permanode %q", pn)
|
||||
|
||||
claim1 := id.SetAttribute(pn, "camliPath:somedir", "targ-123")
|
||||
claim2 := id.SetAttribute(pn, "camliPath:with|pipe", "targ-124")
|
||||
t.Logf("made path claims %q and %q", claim1, claim2)
|
||||
|
||||
id.dumpIndex(t)
|
||||
|
||||
type test struct {
|
||||
blobref string
|
||||
want int
|
||||
}
|
||||
tests := []test{
|
||||
{"targ-123", 1},
|
||||
{"targ-124", 1},
|
||||
{"targ-125", 0},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
signer := id.SignerBlobRef
|
||||
paths, err := id.Index.PathsOfSignerTarget(signer, blobref.Parse(tt.blobref))
|
||||
if err != nil {
|
||||
t.Fatalf("PathsOfSignerTarget(%q): %v", tt.blobref, err)
|
||||
}
|
||||
if len(paths) != tt.want {
|
||||
t.Fatalf("PathsOfSignerTarget(%q) got %d results; want %d",
|
||||
tt.blobref, len(paths), tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverseTimeString(t *testing.T) {
|
||||
in := "2011-11-27T01:23:45Z"
|
||||
got := reverseTimeString(in)
|
||||
|
|
|
@ -22,29 +22,50 @@ import (
|
|||
)
|
||||
|
||||
type keyType struct {
|
||||
name string
|
||||
parts []keyPart
|
||||
name string
|
||||
keyParts []part
|
||||
valParts []part
|
||||
}
|
||||
|
||||
func (k *keyType) Prefix(args ...interface{}) string {
|
||||
return k.build(true, args...)
|
||||
return k.build(true, true, k.keyParts, args...)
|
||||
}
|
||||
|
||||
func (k *keyType) Key(args ...interface{}) string {
|
||||
return k.build(false, args...)
|
||||
return k.build(false, true, k.keyParts, args...)
|
||||
}
|
||||
|
||||
func (k *keyType) build(finalPipe bool, args ...interface{}) string {
|
||||
func (k *keyType) Val(args ...interface{}) string {
|
||||
return k.build(false, false, k.valParts, args...)
|
||||
}
|
||||
|
||||
func (k *keyType) build(isPrefix, isKey bool, parts []part, args ...interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(k.name)
|
||||
if isKey {
|
||||
buf.WriteString(k.name)
|
||||
}
|
||||
if !isPrefix && len(args) != len(parts) {
|
||||
panic("wrong number of arguments")
|
||||
}
|
||||
if len(args) > len(parts) {
|
||||
panic("too many arguments")
|
||||
}
|
||||
for i, arg := range args {
|
||||
buf.WriteString("|")
|
||||
switch k.parts[i].typ {
|
||||
case typeReverseTime:
|
||||
if isKey || i > 0 {
|
||||
buf.WriteString("|")
|
||||
}
|
||||
asStr := func() string {
|
||||
s, ok := arg.(string)
|
||||
if !ok {
|
||||
s = arg.(fmt.Stringer).String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
switch parts[i].typ {
|
||||
case typeStr:
|
||||
buf.WriteString(urle(asStr()))
|
||||
case typeReverseTime:
|
||||
s := asStr()
|
||||
const example = "2011-01-23T05:23:12"
|
||||
if len(s) < len(example) || s[4] != '-' && s[10] != 'T' {
|
||||
panic("doesn't look like a time: " + s)
|
||||
|
@ -56,16 +77,16 @@ func (k *keyType) build(finalPipe bool, args ...interface{}) string {
|
|||
buf.WriteString(s)
|
||||
} else {
|
||||
buf.WriteString(arg.(fmt.Stringer).String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if finalPipe {
|
||||
if isPrefix {
|
||||
buf.WriteString("|")
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
type keyPart struct {
|
||||
type part struct {
|
||||
name string
|
||||
typ partType
|
||||
}
|
||||
|
@ -77,15 +98,32 @@ const (
|
|||
typeTime
|
||||
typeReverseTime // time prepended with "rt" + each numeric digit reversed from '9'
|
||||
typeBlobRef
|
||||
typeStr
|
||||
)
|
||||
|
||||
var (
|
||||
keyRecentPermanode = &keyType{
|
||||
"recpn",
|
||||
[]keyPart{
|
||||
[]part{
|
||||
{"owner", typeKeyId},
|
||||
{"modtime", typeReverseTime},
|
||||
{"claim", typeBlobRef},
|
||||
},
|
||||
nil,
|
||||
}
|
||||
|
||||
keySignerTargetPaths = &keyType{
|
||||
"signertargetpath",
|
||||
[]part{
|
||||
{"signer", typeKeyId},
|
||||
{"target", typeBlobRef},
|
||||
{"claim", typeBlobRef}, // for key uniqueness
|
||||
},
|
||||
[]part{
|
||||
{"claimDate", typeTime},
|
||||
{"base", typeBlobRef},
|
||||
{"active", typeStr}, // 'Y', or 'N' for deleted
|
||||
{"suffix", typeStr},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"camli/blobref"
|
||||
"camli/blobserver"
|
||||
|
@ -127,6 +128,25 @@ func (ix *Index) populateClaim(br *blobref.BlobRef, ss *schema.Superset, sniffer
|
|||
claimKey := pipes("claim", pnbr, verifiedKeyId, ss.ClaimDate, br)
|
||||
bm.Set(claimKey, pipes(urle(ss.ClaimType), urle(ss.Attribute), urle(ss.Value)))
|
||||
|
||||
if strings.HasPrefix(ss.Attribute, "camliPath:") {
|
||||
targetRef := blobref.Parse(ss.Value)
|
||||
if targetRef != nil {
|
||||
// TODO: deal with set-attribute vs. del-attribute
|
||||
// properly? I think we get it for free when
|
||||
// del-attribute has no Value, but we need to deal
|
||||
// with the case where they explicitly delete the
|
||||
// current value.
|
||||
suffix := ss.Attribute[len("camliPath:"):]
|
||||
active := "Y"
|
||||
if ss.ClaimType == "del-attribute" {
|
||||
active = "N"
|
||||
}
|
||||
stpKey := keySignerTargetPaths.Key(verifiedKeyId, targetRef, br)
|
||||
stpVal := keySignerTargetPaths.Val(ss.ClaimDate, pnbr, active, suffix)
|
||||
bm.Set(stpKey, stpVal)
|
||||
}
|
||||
}
|
||||
|
||||
if search.IsIndexedAttribute(ss.Attribute) {
|
||||
savKey := pipes("signerattrvalue",
|
||||
verifiedKeyId, urle(ss.Attribute), urle(ss.Value),
|
||||
|
|
|
@ -156,6 +156,16 @@ type Index interface {
|
|||
// Only attributes white-listed by IsIndexedAttribute are valid.
|
||||
PermanodeOfSignerAttrValue(signer *blobref.BlobRef, attr, val string) (*blobref.BlobRef, os.Error)
|
||||
|
||||
// PathsOfSignerTarget queries the index about "camliPath:"
|
||||
// URL-dispatch attributes.
|
||||
//
|
||||
// It returns a list of all the path claims that have been signed
|
||||
// by the provided signer and point at the given target.
|
||||
//
|
||||
// This is used when editing a permanode, to figure work up
|
||||
// the name resolution tree backwards ultimately to a
|
||||
// camliRoot permanode (which should know its base URL), and
|
||||
// then the complete URL(s) of a target can be found.
|
||||
PathsOfSignerTarget(signer, target *blobref.BlobRef) ([]*Path, os.Error)
|
||||
|
||||
// All Path claims for (signer, base, suffix)
|
||||
|
|
Loading…
Reference in New Issue