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