OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be 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) error { |
| 105 key := datastore.GetMetaDefault(pm, "key", nil).(*datastore.Key) |
| 106 if !cfg.WithSpecial && strings.HasPrefix(key.Kind(), "__") && st
rings.HasSuffix(key.Kind(), "__") { |
| 107 return nil |
| 108 } |
| 109 if err := prnt("\n%s:\n", key); err != nil { |
| 110 return err |
| 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 err |
| 120 } |
| 121 } |
| 122 return nil |
| 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 err |
| 134 } |
| 135 } |
| 136 return nil |
| 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 |