Chromium Code Reviews| 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 |