OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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 dumper implements a very VERY dumb datastore-dumping debugging aid. | |
6 // You shouldn't plan on having this work with the production datastore with any | |
7 // appreciable amount of data. | |
8 // | |
9 // This will take an arbitrary query (or even a query for every entity in the | |
10 // entire datastore), and print every entity to some output stream. | |
11 package dumper | |
12 | |
13 import ( | |
14 "fmt" | |
15 "io" | |
16 "os" | |
17 "sort" | |
18 "strings" | |
19 | |
20 "github.com/luci/gae/service/datastore" | |
21 "golang.org/x/net/context" | |
22 ) | |
23 | |
24 // Key is a key into a PropFilterMap | |
25 type Key struct { | |
26 Kind string | |
27 PropName string | |
28 } | |
29 | |
30 // A PropFilterMap maps from Kind+PropertyName tuples to a formatting function. You | |
31 // may use this to specially format particular properties. | |
32 type PropFilterMap map[Key]func(datastore.Property) string | |
33 | |
34 // KindFilterMap maps from a Kind to a formatting function. You may use this to | |
35 // specially format particular Kinds. If this function returns an empty string, | |
36 // the default formatting function (including any PropFilterMap entries) will be | |
37 // used. | |
38 type KindFilterMap map[string]func(*datastore.Key, datastore.PropertyMap) string | |
39 | |
40 // Config is a configured dumper. | |
41 type Config struct { | |
42 // OutStream is the output stream to use. If this is nil, os.Stdout will be | |
43 // used. | |
44 OutStream io.Writer | |
45 | |
46 // WithSpecial, if true, includes entities which have kinds that begin a nd | |
47 // end with "__". By default, these entities are skipped. | |
48 WithSpecial bool | |
49 | |
50 // PropFilters is an optional property filter map for controlling the | |
51 // rendering of certain Kind/Property values. | |
52 PropFilters PropFilterMap | |
53 | |
54 // KindFilters is an optional kind filter for controlling the rendering of | |
55 // certain Kind values. | |
56 KindFilters KindFilterMap | |
57 } | |
58 | |
59 // Query will dump everything matching the provided query. | |
60 // | |
61 // If the provided query is nil, a kindless query without any filters will be | |
62 // used. | |
63 func (cfg Config) Query(c context.Context, q *datastore.Query) (n int, err error ) { | |
64 ds := datastore.Get(c) | |
65 | |
66 if q == nil { | |
67 q = datastore.NewQuery("") | |
68 } | |
69 | |
70 out := cfg.OutStream | |
71 if out == nil { | |
72 out = os.Stdout | |
73 } | |
74 | |
75 fmtVal := func(kind, name string, prop datastore.Property) string { | |
76 if fn := cfg.PropFilters[Key{kind, name}]; fn != nil { | |
77 return fn(prop) | |
78 } | |
79 return prop.String() | |
80 } | |
81 | |
82 prnt := func(format string, args ...interface{}) (err error) { | |
83 var amt int | |
84 amt, err = fmt.Fprintf(out, format, args...) | |
85 n += amt | |
86 return | |
87 } | |
88 | |
89 prop := func(kind, name string, vals []datastore.Property) (err error) { | |
90 if len(vals) <= 1 { | |
91 return prnt(" %q: [%s]\n", name, fmtVal(kind, name, val s[0])) | |
92 } | |
93 if err = prnt(" %q: [\n %s", name, fmtVal(kind, name, vals[0 ])); err != nil { | |
94 return | |
95 } | |
96 for _, v := range vals[1:] { | |
97 if err = prnt(",\n %s", fmtVal(kind, name, v)); err ! = nil { | |
98 return | |
99 } | |
100 } | |
101 return prnt("\n ]\n") | |
102 } | |
103 | |
104 err = ds.Run(q, func(pm datastore.PropertyMap) (err error) { | |
105 key := datastore.GetMetaDefault(pm, "key", nil).(*datastore.Key) | |
dnj
2016/06/06 14:01:38
nit: I think "err" and "err" are a bit awkward her
| |
106 if !cfg.WithSpecial && strings.HasPrefix(key.Kind(), "__") && st rings.HasSuffix(key.Kind(), "__") { | |
107 return | |
108 } | |
109 if err = prnt("\n%s:\n", key); err != nil { | |
110 return | |
111 } | |
112 pm, _ = pm.Save(false) | |
113 | |
114 // See if we have a KindFilter for this | |
115 if flt, ok := cfg.KindFilters[key.Kind()]; ok { | |
116 if kindOut := flt(key, pm); kindOut != "" { | |
117 for _, l := range strings.Split(kindOut, "\n") { | |
118 if err = prnt(" %s\n", l); err != nil { | |
119 return | |
120 } | |
121 } | |
122 return | |
123 } | |
124 } | |
125 | |
126 keys := make([]string, 0, len(pm)) | |
127 for k := range pm { | |
128 keys = append(keys, k) | |
129 } | |
130 sort.Strings(keys) | |
131 for _, k := range keys { | |
132 if err = prop(key.Kind(), k, pm[k]); err != nil { | |
133 return | |
134 } | |
135 } | |
136 return | |
137 }) | |
138 return | |
139 } | |
140 | |
141 // Query dumps the provided query to stdout without special entities and with | |
142 // default rendering. | |
143 func Query(c context.Context, q *datastore.Query) { | |
144 Config{}.Query(c, q) | |
145 } | |
146 | |
147 // All dumps all entities to stdout without special entities and with default | |
148 // rendering. | |
149 func All(c context.Context) { | |
150 Config{}.Query(c, nil) | |
151 } | |
OLD | NEW |