index: PathsOfSignerTarget

Change-Id: Ie82117ed5c0c5479e456c7a0d8ee554cdc783cc5
This commit is contained in:
Brad Fitzpatrick 2011-12-01 10:43:57 -08:00
parent fcba3443d0
commit ffb43a01a8
5 changed files with 164 additions and 16 deletions

View File

@ -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) {

View File

@ -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)

View File

@ -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},
},
}
)

View File

@ -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),

View File

@ -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)