Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(87)

Side by Side Diff: helper/datastore_key.go

Issue 1243323002: Refactor a bit. (Closed) Base URL: https://github.com/luci/gae.git@master
Patch Set: fix golint Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « helper/datastore_impl.go ('k') | helper/datastore_key_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « helper/datastore_impl.go ('k') | helper/datastore_key_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698