| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The LUCI Authors. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 | |
| 15 package hierarchy | |
| 16 | |
| 17 import ( | |
| 18 ds "github.com/luci/gae/service/datastore" | |
| 19 "github.com/luci/luci-go/logdog/common/types" | |
| 20 | |
| 21 "golang.org/x/net/context" | |
| 22 ) | |
| 23 | |
| 24 // Component is a log stream hierarchy component. | |
| 25 type Component struct { | |
| 26 // Parent is the partial stream path parent of this component. | |
| 27 Parent types.StreamPath | |
| 28 // Name is the name of this Component. | |
| 29 Name string | |
| 30 // Stream is true if this is a stream component, false if it is a path | |
| 31 // component. | |
| 32 Stream bool | |
| 33 } | |
| 34 | |
| 35 // Components returns the set of hierarchy components for a given stream path. | |
| 36 // | |
| 37 // If isFullStream is false, all of the components in p will be considered | |
| 38 // intermediate components. If isFullStream is true, p is regarded as a full | |
| 39 // stream, and the last element will be marked as a stream component. | |
| 40 func Components(p types.StreamPath, isFullStream bool) []*Component { | |
| 41 segments := p.SegmentCount() | |
| 42 if segments == 0 { | |
| 43 return nil | |
| 44 } | |
| 45 | |
| 46 // Build all Component objects for our parts. | |
| 47 // | |
| 48 // The first component is the stream component. | |
| 49 components := make([]*Component, 0, segments) | |
| 50 for { | |
| 51 var last string | |
| 52 p, last = p.SplitLast() | |
| 53 components = append(components, &Component{ | |
| 54 Parent: p, | |
| 55 Name: last, | |
| 56 Stream: isFullStream && (len(components) == 0), | |
| 57 }) | |
| 58 | |
| 59 if p == "" { | |
| 60 break | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 return components | |
| 65 } | |
| 66 | |
| 67 // String returns a string representation for this Component. | |
| 68 // | |
| 69 // For path components, this is the path. For stream components, this is the | |
| 70 // path followed by a dollar sign. Note that the dollar sign is not a valid | |
| 71 // types.StreamName character, and so this will not conflict with other valid | |
| 72 // Components. | |
| 73 // | |
| 74 // For example, for {Parent="foo/bar", Name="baz"}, we get "foo/bar/baz". | |
| 75 // If {Stream=true}, we would get "foo/bar/baz$". | |
| 76 // | |
| 77 // Two Components with the same String result reference the same path component. | |
| 78 func (comp *Component) String() string { | |
| 79 s := string(comp.Path()) | |
| 80 if comp.Stream { | |
| 81 s += "$" | |
| 82 } | |
| 83 return s | |
| 84 } | |
| 85 | |
| 86 // Path returns the StreamPath for this Component. | |
| 87 func (comp *Component) Path() types.StreamPath { | |
| 88 return comp.Parent.Append(comp.Name) | |
| 89 } | |
| 90 | |
| 91 // Exists checks whether this Component exists in the datastore. | |
| 92 func (comp *Component) Exists(c context.Context) (bool, error) { | |
| 93 er, err := ds.Exists(c, ds.KeyForObj(c, comp.entity())) | |
| 94 if err != nil { | |
| 95 return false, err | |
| 96 } | |
| 97 return er.All(), nil | |
| 98 } | |
| 99 | |
| 100 // Put writes this Component to the datastore. | |
| 101 // | |
| 102 // Prior to writing, it will verify that the Component represents a valid | |
| 103 // partial log stream path. | |
| 104 func (comp *Component) Put(c context.Context) error { | |
| 105 path := comp.Path() | |
| 106 if err := path.ValidatePartial(); err != nil { | |
| 107 return err | |
| 108 } | |
| 109 return ds.Put(c, comp.entity()) | |
| 110 } | |
| 111 | |
| 112 // entity returns the componentEntity that this Component describes. | |
| 113 func (comp *Component) entity() *componentEntity { | |
| 114 return mkComponentEntity(comp.Parent, comp.Name, comp.Stream) | |
| 115 } | |
| 116 | |
| 117 // Missing checks the status of the supplied Components in the datastore. | |
| 118 // | |
| 119 // It mutates the supplied components array, shrinking it if necessary, to | |
| 120 // return only the Components that were not already present. | |
| 121 // | |
| 122 // If Missing failed, it will return the original components array and the | |
| 123 // failure error. | |
| 124 func Missing(c context.Context, components []*Component) ([]*Component, error) { | |
| 125 exists := make([]*ds.Key, len(components)) | |
| 126 for i, comp := range components { | |
| 127 exists[i] = ds.KeyForObj(c, comp.entity()) | |
| 128 } | |
| 129 er, err := ds.Exists(c, exists) | |
| 130 if err != nil { | |
| 131 return nil, err | |
| 132 } | |
| 133 | |
| 134 // Condense the components array. | |
| 135 nidx := 0 | |
| 136 for i, v := range er.List(0) { | |
| 137 if !v { | |
| 138 components[nidx] = components[i] | |
| 139 nidx++ | |
| 140 } | |
| 141 } | |
| 142 return components[:nidx], nil | |
| 143 } | |
| 144 | |
| 145 // PutMulti performs a datastore PutMulti on all of the hierarchy entities for | |
| 146 // components. | |
| 147 func PutMulti(c context.Context, components []*Component) error { | |
| 148 ents := make([]*componentEntity, len(components)) | |
| 149 for i, comp := range components { | |
| 150 ents[i] = comp.entity() | |
| 151 } | |
| 152 return ds.Put(c, ents) | |
| 153 } | |
| OLD | NEW |