| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |  | 
| 2 // Use of this source code is governed by a BSD-style license that can be |  | 
| 3 // found in the LICENSE file. |  | 
| 4 |  | 
| 5 // adapted from github.com/golang/appengine/datastore |  | 
| 6 |  | 
| 7 package helper |  | 
| 8 |  | 
| 9 import ( |  | 
| 10         "bytes" |  | 
| 11         "encoding/base64" |  | 
| 12         "errors" |  | 
| 13         "strconv" |  | 
| 14         "strings" |  | 
| 15 |  | 
| 16         "github.com/luci/gae" |  | 
| 17         pb "github.com/luci/gae/helper/internal/protos/datastore" |  | 
| 18 |  | 
| 19         "github.com/golang/protobuf/proto" |  | 
| 20 ) |  | 
| 21 |  | 
| 22 // DSKeyEncode encodes the provided key as a base64-encoded protobuf. |  | 
| 23 // |  | 
| 24 // This encoding is compatible with the SDK-provided encoding and is agnostic |  | 
| 25 // to the underlying implementation of the DSKey. |  | 
| 26 func DSKeyEncode(k gae.DSKey) string { |  | 
| 27         n := 0 |  | 
| 28         for i := k; i != nil; i = i.Parent() { |  | 
| 29                 n++ |  | 
| 30         } |  | 
| 31         e := make([]*pb.Path_Element, n) |  | 
| 32         for i := k; i != nil; i = i.Parent() { |  | 
| 33                 n-- |  | 
| 34                 kind := i.Kind() |  | 
| 35                 e[n] = &pb.Path_Element{ |  | 
| 36                         Type: &kind, |  | 
| 37                 } |  | 
| 38                 // At most one of {Name,Id} should be set. |  | 
| 39                 // Neither will be set for incomplete keys. |  | 
| 40                 if i.StringID() != "" { |  | 
| 41                         sid := i.StringID() |  | 
| 42                         e[n].Name = &sid |  | 
| 43                 } else if i.IntID() != 0 { |  | 
| 44                         iid := i.IntID() |  | 
| 45                         e[n].Id = &iid |  | 
| 46                 } |  | 
| 47         } |  | 
| 48         var namespace *string |  | 
| 49         if k.Namespace() != "" { |  | 
| 50                 namespace = proto.String(k.Namespace()) |  | 
| 51         } |  | 
| 52         r, err := proto.Marshal(&pb.Reference{ |  | 
| 53                 App:       proto.String(k.AppID()), |  | 
| 54                 NameSpace: namespace, |  | 
| 55                 Path: &pb.Path{ |  | 
| 56                         Element: e, |  | 
| 57                 }, |  | 
| 58         }) |  | 
| 59         if err != nil { |  | 
| 60                 panic(err) |  | 
| 61         } |  | 
| 62 |  | 
| 63         // trim padding |  | 
| 64         return strings.TrimRight(base64.URLEncoding.EncodeToString(r), "=") |  | 
| 65 } |  | 
| 66 |  | 
| 67 // DSKeyToksDecode decodes a base64-encoded protobuf representation of a DSKey |  | 
| 68 // into a tokenized form. This is so that implementations of the gae wrapper |  | 
| 69 // can decode to their own implementation of DSKey. |  | 
| 70 // |  | 
| 71 // This encoding is compatible with the SDK-provided encoding and is agnostic |  | 
| 72 // to the underlying implementation of the DSKey. |  | 
| 73 func DSKeyToksDecode(encoded string) (appID, namespace string, toks []gae.DSKeyT
     ok, err error) { |  | 
| 74         // Re-add padding |  | 
| 75         if m := len(encoded) % 4; m != 0 { |  | 
| 76                 encoded += strings.Repeat("=", 4-m) |  | 
| 77         } |  | 
| 78         b, err := base64.URLEncoding.DecodeString(encoded) |  | 
| 79         if err != nil { |  | 
| 80                 return |  | 
| 81         } |  | 
| 82 |  | 
| 83         r := &pb.Reference{} |  | 
| 84         if err = proto.Unmarshal(b, r); err != nil { |  | 
| 85                 return |  | 
| 86         } |  | 
| 87 |  | 
| 88         appID = r.GetApp() |  | 
| 89         namespace = r.GetNameSpace() |  | 
| 90         toks = make([]gae.DSKeyTok, len(r.Path.Element)) |  | 
| 91         for i, e := range r.Path.Element { |  | 
| 92                 toks[i] = gae.DSKeyTok{ |  | 
| 93                         Kind:     e.GetType(), |  | 
| 94                         IntID:    e.GetId(), |  | 
| 95                         StringID: e.GetName(), |  | 
| 96                 } |  | 
| 97         } |  | 
| 98         return |  | 
| 99 } |  | 
| 100 |  | 
| 101 // DSKeyMarshalJSON returns a MarshalJSON-compatible serialization of a DSKey. |  | 
| 102 func DSKeyMarshalJSON(k gae.DSKey) ([]byte, error) { |  | 
| 103         return []byte(`"` + DSKeyEncode(k) + `"`), nil |  | 
| 104 } |  | 
| 105 |  | 
| 106 // DSKeyUnmarshalJSON returns the tokenized version of a DSKey as encoded by |  | 
| 107 // DSKeyMarshalJSON. |  | 
| 108 func DSKeyUnmarshalJSON(buf []byte) (appID, namespace string, toks []gae.DSKeyTo
     k, err error) { |  | 
| 109         if len(buf) < 2 || buf[0] != '"' || buf[len(buf)-1] != '"' { |  | 
| 110                 err = errors.New("datastore: bad JSON key") |  | 
| 111         } else { |  | 
| 112                 appID, namespace, toks, err = DSKeyToksDecode(string(buf[1 : len
     (buf)-1])) |  | 
| 113         } |  | 
| 114         return |  | 
| 115 } |  | 
| 116 |  | 
| 117 // DSKeyIncomplete returns true iff k doesn't have an id yet. |  | 
| 118 func DSKeyIncomplete(k gae.DSKey) bool { |  | 
| 119         return k != nil && k.StringID() == "" && k.IntID() == 0 |  | 
| 120 } |  | 
| 121 |  | 
| 122 // DSKeyValid determines if a key is valid, according to a couple rules: |  | 
| 123 //   - k is not nil |  | 
| 124 //   - k's namespace matches ns |  | 
| 125 //   - every token of k: |  | 
| 126 //     - (if !allowSpecial) token's kind doesn't start with '__' |  | 
| 127 //     - token's kind and appid are non-blank |  | 
| 128 //     - token is not incomplete |  | 
| 129 //   - all tokens have the same namespace and appid |  | 
| 130 func DSKeyValid(k gae.DSKey, ns string, allowSpecial bool) bool { |  | 
| 131         if k == nil { |  | 
| 132                 return false |  | 
| 133         } |  | 
| 134         // since we do "client-side" validation of namespaces in local |  | 
| 135         // implementations, it's convenient to check this here. |  | 
| 136         if k.Namespace() != ns { |  | 
| 137                 return false |  | 
| 138         } |  | 
| 139         for ; k != nil; k = k.Parent() { |  | 
| 140                 if !allowSpecial && len(k.Kind()) >= 2 && k.Kind()[:2] == "__" { |  | 
| 141                         return false |  | 
| 142                 } |  | 
| 143                 if k.Kind() == "" || k.AppID() == "" { |  | 
| 144                         return false |  | 
| 145                 } |  | 
| 146                 if k.StringID() != "" && k.IntID() != 0 { |  | 
| 147                         return false |  | 
| 148                 } |  | 
| 149                 if k.Parent() != nil { |  | 
| 150                         if DSKeyIncomplete(k.Parent()) { |  | 
| 151                                 return false |  | 
| 152                         } |  | 
| 153                         if k.Parent().AppID() != k.AppID() || k.Parent().Namespa
     ce() != k.Namespace() { |  | 
| 154                                 return false |  | 
| 155                         } |  | 
| 156                 } |  | 
| 157         } |  | 
| 158         return true |  | 
| 159 } |  | 
| 160 |  | 
| 161 // DSKeyRoot returns the entity root for the given key. |  | 
| 162 func DSKeyRoot(k gae.DSKey) gae.DSKey { |  | 
| 163         for k != nil && k.Parent() != nil { |  | 
| 164                 k = k.Parent() |  | 
| 165         } |  | 
| 166         return k |  | 
| 167 } |  | 
| 168 |  | 
| 169 // DSKeysEqual returns true iff the two keys represent identical key values. |  | 
| 170 func DSKeysEqual(a, b gae.DSKey) (ret bool) { |  | 
| 171         ret = (a.Kind() == b.Kind() && |  | 
| 172                 a.StringID() == b.StringID() && |  | 
| 173                 a.IntID() == b.IntID() && |  | 
| 174                 a.AppID() == b.AppID() && |  | 
| 175                 a.Namespace() == b.Namespace()) |  | 
| 176         if !ret { |  | 
| 177                 return |  | 
| 178         } |  | 
| 179         ap, bp := a.Parent(), b.Parent() |  | 
| 180         return (ap == nil && bp == nil) || DSKeysEqual(ap, bp) |  | 
| 181 } |  | 
| 182 |  | 
| 183 func marshalDSKey(b *bytes.Buffer, k gae.DSKey) { |  | 
| 184         if k.Parent() != nil { |  | 
| 185                 marshalDSKey(b, k.Parent()) |  | 
| 186         } |  | 
| 187         b.WriteByte('/') |  | 
| 188         b.WriteString(k.Kind()) |  | 
| 189         b.WriteByte(',') |  | 
| 190         if k.StringID() != "" { |  | 
| 191                 b.WriteString(k.StringID()) |  | 
| 192         } else { |  | 
| 193                 b.WriteString(strconv.FormatInt(k.IntID(), 10)) |  | 
| 194         } |  | 
| 195 } |  | 
| 196 |  | 
| 197 // DSKeyString returns a human-readable representation of the key, and is the |  | 
| 198 // typical implementation of DSKey.String() (though it isn't guaranteed to be) |  | 
| 199 func DSKeyString(k gae.DSKey) string { |  | 
| 200         if k == nil { |  | 
| 201                 return "" |  | 
| 202         } |  | 
| 203         b := bytes.NewBuffer(make([]byte, 0, 512)) |  | 
| 204         marshalDSKey(b, k) |  | 
| 205         return b.String() |  | 
| 206 } |  | 
| 207 |  | 
| 208 // DSKeySplit splits the key into its constituent parts. Note that if the key is |  | 
| 209 // not DSKeyValid, this method may not provide a round-trip for k. |  | 
| 210 func DSKeySplit(k gae.DSKey) (appID, namespace string, toks []gae.DSKeyTok) { |  | 
| 211         if k == nil { |  | 
| 212                 return |  | 
| 213         } |  | 
| 214 |  | 
| 215         if sk, ok := k.(*GenericDSKey); ok { |  | 
| 216                 if sk == nil { |  | 
| 217                         return |  | 
| 218                 } |  | 
| 219                 return sk.appID, sk.namespace, sk.toks |  | 
| 220         } |  | 
| 221 |  | 
| 222         n := 0 |  | 
| 223         for i := k; i != nil; i = i.Parent() { |  | 
| 224                 n++ |  | 
| 225         } |  | 
| 226         toks = make([]gae.DSKeyTok, n) |  | 
| 227         for i := k; i != nil; i = i.Parent() { |  | 
| 228                 n-- |  | 
| 229                 toks[n].IntID = i.IntID() |  | 
| 230                 toks[n].StringID = i.StringID() |  | 
| 231                 toks[n].Kind = i.Kind() |  | 
| 232         } |  | 
| 233         appID = k.AppID() |  | 
| 234         namespace = k.Namespace() |  | 
| 235         return |  | 
| 236 } |  | 
| OLD | NEW | 
|---|