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