| OLD | NEW |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package dm | 5 package dm |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "fmt" | 8 "fmt" |
| 9 "strconv" | 9 "strconv" |
| 10 "strings" | 10 "strings" |
| 11 | 11 |
| 12 "github.com/luci/gae/service/datastore" | 12 "github.com/luci/gae/service/datastore" |
| 13 ) | 13 ) |
| 14 | 14 |
| 15 const flipMask uint32 = 0xFFFFFFFF | 15 const flipMask uint32 = 0xFFFFFFFF |
| 16 | 16 |
| 17 var _ datastore.PropertyConverter = (*Attempt_ID)(nil) | 17 var _ datastore.PropertyConverter = (*Attempt_ID)(nil) |
| 18 var _ datastore.PropertyConverter = (*Execution_ID)(nil) | |
| 19 | 18 |
| 20 // NewQuestID is a shorthand to New a new *Quest_ID | 19 // NewQuestID is a shorthand to New a new *Quest_ID |
| 21 func NewQuestID(qst string) *Quest_ID { | 20 func NewQuestID(qst string) *Quest_ID { |
| 22 return &Quest_ID{qst} | 21 return &Quest_ID{qst} |
| 23 } | 22 } |
| 24 | 23 |
| 25 // NewAttemptID is a shorthand to New a new *Attempt_ID | 24 // NewAttemptID is a shorthand to New a new *Attempt_ID |
| 26 func NewAttemptID(qst string, aid uint32) *Attempt_ID { | 25 func NewAttemptID(qst string, aid uint32) *Attempt_ID { |
| 27 return &Attempt_ID{qst, aid} | 26 return &Attempt_ID{qst, aid} |
| 28 } | 27 } |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 an, err := strconv.ParseUint(toks[1], 16, 32) | 67 an, err := strconv.ParseUint(toks[1], 16, 32) |
| 69 if err != nil { | 68 if err != nil { |
| 70 return err | 69 return err |
| 71 } | 70 } |
| 72 | 71 |
| 73 a.Quest = toks[0] | 72 a.Quest = toks[0] |
| 74 a.Id = flipMask ^ uint32(an) | 73 a.Id = flipMask ^ uint32(an) |
| 75 return nil | 74 return nil |
| 76 } | 75 } |
| 77 | 76 |
| 78 // ToProperty implements datastore.PropertyConverter for the purpose of | |
| 79 // embedding this Execution_ID as the ID of a luci/gae compatible datastore | |
| 80 // object. The numerical id field is stored as an inverted, hex-encoded string, | |
| 81 // so that Execution_ID{"quest", 1, 2} would encode as "quest|fffffffe|fffffffd"
. | |
| 82 // This is done so that the __key__ ordering in the dm application prefers to | |
| 83 // order the most recent executions first. | |
| 84 // | |
| 85 // The go representation will always have the normal non-flipped numerical ids. | |
| 86 func (e *Execution_ID) ToProperty() (datastore.Property, error) { | |
| 87 return datastore.MkPropertyNI(e.DMEncoded()), nil | |
| 88 } | |
| 89 | |
| 90 // FromProperty implements datastore.PropertyConverter | |
| 91 func (e *Execution_ID) FromProperty(p datastore.Property) error { | |
| 92 if p.Type() != datastore.PTString { | |
| 93 return fmt.Errorf("wrong type for property: %s", p.Type()) | |
| 94 } | |
| 95 return e.SetDMEncoded(p.Value().(string)) | |
| 96 } | |
| 97 | |
| 98 // DMEncoded returns the encoded inverted string id for this Execution. Numeric | |
| 99 // values are inverted if flip is true. | |
| 100 func (e *Execution_ID) DMEncoded() string { | |
| 101 return fmt.Sprintf("%s|%08x|%08x", e.Quest, flipMask^e.Attempt, flipMask
^e.Id) | |
| 102 } | |
| 103 | |
| 104 // SetDMEncoded decodes val into this Execution_ID, returning an error if | |
| 105 // there's a problem. Numeric values are inverted if flip is true. | |
| 106 func (e *Execution_ID) SetDMEncoded(val string) error { | |
| 107 toks := strings.SplitN(val, "|", 3) | |
| 108 if len(toks) != 3 { | |
| 109 return fmt.Errorf("unable to parse Execution id: %q", val) | |
| 110 } | |
| 111 an, err := strconv.ParseUint(toks[1], 16, 32) | |
| 112 if err != nil { | |
| 113 return err | |
| 114 } | |
| 115 en, err := strconv.ParseUint(toks[2], 16, 32) | |
| 116 if err != nil { | |
| 117 return err | |
| 118 } | |
| 119 | |
| 120 e.Quest = toks[0] | |
| 121 e.Attempt = flipMask ^ uint32(an) | |
| 122 e.Id = flipMask ^ uint32(en) | |
| 123 return nil | |
| 124 } | |
| 125 | |
| 126 // GetQuest gets the specified quest from GraphData, if it's already there. If | 77 // GetQuest gets the specified quest from GraphData, if it's already there. If |
| 127 // it's not, then a new Quest will be created, added, and returned. | 78 // it's not, then a new Quest will be created, added, and returned. |
| 128 // | 79 // |
| 129 // If the Quests map is uninitialized, this will initialize it. | 80 // If the Quests map is uninitialized, this will initialize it. |
| 130 func (g *GraphData) GetQuest(qid string) (*Quest, bool) { | 81 func (g *GraphData) GetQuest(qid string) (*Quest, bool) { |
| 131 cur, ok := g.Quests[qid] | 82 cur, ok := g.Quests[qid] |
| 132 if !ok { | 83 if !ok { |
| 133 cur = &Quest{ | 84 cur = &Quest{ |
| 134 Id: NewQuestID(qid), | 85 Id: NewQuestID(qid), |
| 135 Attempts: map[uint32]*Attempt{}, | 86 Attempts: map[uint32]*Attempt{}, |
| 136 } | 87 } |
| 137 if g.Quests == nil { | 88 if g.Quests == nil { |
| 138 g.Quests = map[string]*Quest{} | 89 g.Quests = map[string]*Quest{} |
| 139 } | 90 } |
| 140 g.Quests[qid] = cur | 91 g.Quests[qid] = cur |
| 141 } | 92 } |
| 142 return cur, ok | 93 return cur, ok |
| 143 } | 94 } |
| 144 | 95 |
| 145 // NewQuestDesc is a shorthand method for building a new *Quest_Desc. | 96 // NewQuestDesc is a shorthand method for building a new *Quest_Desc. |
| 146 func NewQuestDesc(cfg string, js string) *Quest_Desc { | 97 func NewQuestDesc(cfg string, js string, meta *Quest_Desc_Meta) *Quest_Desc { |
| 147 » return &Quest_Desc{cfg, js} | 98 » return &Quest_Desc{cfg, js, meta} |
| 148 } | 99 } |
| 149 | 100 |
| 150 // NewTemplateSpec is a shorthand method for building a new *Quest_TemplateSpec. | 101 // NewTemplateSpec is a shorthand method for building a new *Quest_TemplateSpec. |
| 151 func NewTemplateSpec(project, ref, version, name string) *Quest_TemplateSpec { | 102 func NewTemplateSpec(project, ref, version, name string) *Quest_TemplateSpec { |
| 152 return &Quest_TemplateSpec{project, ref, version, name} | 103 return &Quest_TemplateSpec{project, ref, version, name} |
| 153 } | 104 } |
| 154 | 105 |
| 155 // Equals returns true iff this Quest_TemplateSpec matches all of the fields of | 106 // Equals returns true iff this Quest_TemplateSpec matches all of the fields of |
| 156 // the `o` Quest_TemplateSpec. | 107 // the `o` Quest_TemplateSpec. |
| 157 func (t *Quest_TemplateSpec) Equals(o *Quest_TemplateSpec) bool { | 108 func (t *Quest_TemplateSpec) Equals(o *Quest_TemplateSpec) bool { |
| 158 return (t.Project == o.Project && t.Ref == o.Ref && t.Version == o.Versi
on && | 109 return (t.Project == o.Project && t.Ref == o.Ref && t.Version == o.Versi
on && |
| 159 t.Name == o.Name) | 110 t.Name == o.Name) |
| 160 } | 111 } |
| OLD | NEW |