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

Side by Side Diff: go/src/infra/gae/libs/wrapper/memory/key.go

Issue 1230303003: Revert "Refactor current GAE abstraction library to be free of the SDK*" (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: 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
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 package memory
6
7 import (
8 "bytes"
9 "errors"
10 "fmt"
11
12 "github.com/golang/protobuf/proto"
13 "github.com/luci/luci-go/common/cmpbin"
14 "github.com/mjibson/goon"
15
16 "appengine"
17 "appengine/datastore"
18 "appengine_internal"
19 basepb "appengine_internal/base"
20 )
21
22 const keyNumToksReasonableLimit = 50
23
24 ///////////////////////////// fakeGAECtxForNewKey //////////////////////////////
25
26 type fakeGAECtxForNewKey string
27
28 var _ = appengine.Context((fakeGAECtxForNewKey)(""))
29
30 func (fakeGAECtxForNewKey) Debugf(format string, args ...interface{}) {}
31 func (fakeGAECtxForNewKey) Infof(format string, args ...interface{}) {}
32 func (fakeGAECtxForNewKey) Warningf(format string, args ...interface{}) {}
33 func (fakeGAECtxForNewKey) Errorf(format string, args ...interface{}) {}
34 func (fakeGAECtxForNewKey) Criticalf(format string, args ...interface{}) {}
35 func (fakeGAECtxForNewKey) Request() interface{} { retur n nil }
36 func (f fakeGAECtxForNewKey) Call(service, method string, in, out appengine_inte rnal.ProtoMessage, opts *appengine_internal.CallOptions) error {
37 if service != "__go__" || method != "GetNamespace" {
38 panic(fmt.Errorf("fakeGAECtxForNewKey: cannot facilitate Call(%q , %q, ...)", service, method))
39 }
40 out.(*basepb.StringProto).Value = proto.String(string(f))
41 return nil
42 }
43 func (fakeGAECtxForNewKey) FullyQualifiedAppID() string { return "dev~my~app" }
44
45 /////////////////////////////// Key construction ///////////////////////////////
46
47 func newKey(ns, kind, stringID string, intID int64, parent *datastore.Key) *data store.Key {
48 return datastore.NewKey(fakeGAECtxForNewKey(ns), kind, stringID, intID, parent)
49 }
50 func newKeyObjError(ns string, knr goon.KindNameResolver, src interface{}) (*dat astore.Key, error) {
51 return (&goon.Goon{
52 Context: fakeGAECtxForNewKey(ns),
53 KindNameResolver: knr}).KeyError(src)
54 }
55 func newKeyObj(ns string, knr goon.KindNameResolver, obj interface{}) *datastore .Key {
56 k, err := newKeyObjError(ns, knr, obj)
57 if err != nil {
58 panic(err)
59 }
60 return k
61 }
62 func kind(ns string, knr goon.KindNameResolver, src interface{}) string {
63 return newKeyObj(ns, knr, src).Kind()
64 }
65
66 /////////////////////////////// Binary Encoding ////////////////////////////////
67
68 type keyTok struct {
69 kind string
70 intID uint64
71 stringID string
72 }
73
74 func keyToToks(key *datastore.Key) (namespace string, ret []*keyTok) {
75 var inner func(*datastore.Key)
76 inner = func(k *datastore.Key) {
77 if k.Parent() != nil {
78 inner(k.Parent())
79 }
80 ret = append(ret, &keyTok{k.Kind(), uint64(k.IntID()), k.StringI D()})
81 }
82 inner(key)
83 namespace = key.Namespace()
84 return
85 }
86
87 func toksToKey(ns string, toks []*keyTok) (ret *datastore.Key) {
88 for _, t := range toks {
89 ret = newKey(ns, t.kind, t.stringID, int64(t.intID), ret)
90 }
91 return
92 }
93
94 type nsOption bool
95
96 const (
97 withNS nsOption = true
98 noNS = false
99 )
100
101 func keyBytes(nso nsOption, k *datastore.Key) []byte {
102 buf := &bytes.Buffer{}
103 writeKey(buf, nso, k)
104 return buf.Bytes()
105 }
106
107 func keyFromByteString(nso nsOption, d string, ns string) (*datastore.Key, error ) {
108 return readKey(bytes.NewBufferString(d), nso, ns)
109 }
110
111 func writeKey(buf *bytes.Buffer, nso nsOption, k *datastore.Key) {
112 // namespace ++ #tokens ++ [tok.kind ++ tok.stringID ++ tok.intID?]*
113 namespace, toks := keyToToks(k)
114 if nso == withNS {
115 writeString(buf, namespace)
116 }
117 cmpbin.WriteUint(buf, uint64(len(toks)))
118 for _, tok := range toks {
119 writeString(buf, tok.kind)
120 writeString(buf, tok.stringID)
121 if tok.stringID == "" {
122 cmpbin.WriteUint(buf, tok.intID)
123 }
124 }
125 }
126
127 func readKey(buf *bytes.Buffer, nso nsOption, ns string) (*datastore.Key, error) {
128 namespace := ns
129 if nso == withNS {
130 err := error(nil)
131 if namespace, err = readString(buf); err != nil {
132 return nil, err
133 }
134 }
135
136 numToks, _, err := cmpbin.ReadUint(buf)
137 if err != nil {
138 return nil, err
139 }
140 if numToks > keyNumToksReasonableLimit {
141 return nil, fmt.Errorf("readKey: tried to decode huge key of len gth %d", numToks)
142 }
143
144 toks := make([]*keyTok, numToks)
145 for i := uint64(0); i < numToks; i++ {
146 tok := &keyTok{}
147 if tok.kind, err = readString(buf); err != nil {
148 return nil, err
149 }
150 if tok.stringID, err = readString(buf); err != nil {
151 return nil, err
152 }
153 if tok.stringID == "" {
154 if tok.intID, _, err = cmpbin.ReadUint(buf); err != nil {
155 return nil, err
156 }
157 if tok.intID == 0 {
158 return nil, errors.New("readKey: decoded key wit h empty stringID and empty intID")
159 }
160 }
161 toks[i] = tok
162 }
163
164 return toksToKey(namespace, toks), nil
165 }
166
167 //////////////////////////////// Key utilities /////////////////////////////////
168
169 func rootKey(key *datastore.Key) *datastore.Key {
170 for key.Parent() != nil {
171 key = key.Parent()
172 }
173 return key
174 }
175
176 type keyValidOption bool
177
178 const (
179 // userKeyOnly is used with keyValid, and ensures that the key is only o ne
180 // that's valid for a user program to write to.
181 userKeyOnly keyValidOption = false
182
183 // allowSpecialKeys is used with keyValid, and allows keys for special
184 // metadata objects (like "__entity_group__").
185 allowSpecialKeys = true
186 )
187
188 // keyValid checks to see if a key is valid by applying a bunch of constraint
189 // rules to it (e.g. can't have StringID and IntID set at the same time, can't
190 // have a parent key which is Incomplete(), etc.)
191 //
192 // It verifies that the key is also in the provided namespace. It can also
193 // reject keys which are 'special' e.g. have a Kind starting with "__". This
194 // behavior is controllable with opt.
195 func keyValid(ns string, k *datastore.Key, opt keyValidOption) bool {
196 // copied from the appengine SDK because why would any user program need to
197 // see if a key is valid? /s
198 if k == nil {
199 return false
200 }
201 // since we do "client-side" validation of namespaces, check this here.
202 if k.Namespace() != ns {
203 return false
204 }
205 for ; k != nil; k = k.Parent() {
206 if opt == userKeyOnly && len(k.Kind()) >= 2 && k.Kind()[:2] == " __" { // reserve all Kinds starting with __
207 return false
208 }
209 if k.Kind() == "" || k.AppID() == "" {
210 return false
211 }
212 if k.StringID() != "" && k.IntID() != 0 {
213 return false
214 }
215 if k.Parent() != nil {
216 if k.Parent().Incomplete() {
217 return false
218 }
219 if k.Parent().AppID() != k.AppID() || k.Parent().Namespa ce() != k.Namespace() {
220 return false
221 }
222 }
223 }
224 return true
225 }
226
227 // keyCouldBeValid is like keyValid, but it allows for the possibility that the
228 // last token of the key is Incomplete(). It returns true if the Key will become
229 // valid once it recieves an automatically-assigned ID.
230 func keyCouldBeValid(ns string, k *datastore.Key, opt keyValidOption) bool {
231 // adds an id to k if it's incomplete.
232 if k.Incomplete() {
233 k = newKey(ns, k.Kind(), "", 1, k.Parent())
234 }
235 return keyValid(ns, k, opt)
236 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698