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 |