github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/toolrecord/toolrecord.go (about) 1 package toolrecord 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io/fs" 8 "os" 9 "path/filepath" 10 "time" 11 ) 12 13 type keydataset struct { 14 Name string // technical name from the tool 15 Value string // technical value 16 DisplayName string // user friendly name - optional 17 URL string // direct URL to navigate to this key in the tool backend - optional 18 } 19 20 // Toolrecord holds all data to locate a tool result 21 // in the tool's backend 22 type Toolrecord struct { 23 RecordVersion int 24 25 ToolName string 26 ToolInstance string 27 28 // tool agnostic convenience aggregations 29 // picks the most specific URL + concatenate the dimension names 30 // for easy dashboard / xls creation 31 DisplayName string 32 DisplayURL string 33 34 fileUtils fileWriteUtils 35 36 // detailed keydata - needs tool-specific parsing 37 Keys []keydataset 38 39 // place for additional context information 40 Context map[string]interface{} 41 42 // internal - not exported to the json 43 workspace string 44 reportFileName string 45 } 46 47 type fileWriteUtils interface { 48 MkdirAll(path string, perm fs.FileMode) error 49 WriteFile(name string, data []byte, perm fs.FileMode) error 50 } 51 52 // New - initialize a new toolrecord 53 func New(fileUtils fileWriteUtils, workspace, toolName, toolInstance string) *Toolrecord { 54 tr := Toolrecord{} 55 tr.fileUtils = fileUtils 56 57 tr.RecordVersion = 1 58 tr.ToolName = toolName 59 tr.ToolInstance = toolInstance 60 tr.Keys = []keydataset{} 61 tr.Context = make(map[string]interface{}) 62 63 tr.workspace = workspace 64 65 reportFileName := filepath.Join(workspace, 66 "toolruns", 67 "toolrun_"+toolName+"_all.json") 68 tr.reportFileName = reportFileName 69 70 // keep a timestamp inside all files 71 now := time.Now().UTC() 72 var otr = &tr 73 otr.AddContext("generatedOnUtc", now.Format("20060102150405")) 74 return otr 75 } 76 77 // AddKeyData - add one key to the current toolrecord 78 // calls must follow the tool's hierachy ( e.g. org -> project) 79 // as DisplayName & DisplayURL are based on the call sequence 80 func (tr *Toolrecord) AddKeyData(keyname, keyvalue, displayname, url string) error { 81 if keyname == "" { 82 return errors.New("TR_ADD_KEY: empty keyname") 83 } 84 if keyvalue == "" { 85 return fmt.Errorf("TR_ADD_KEY: empty keyvalue for %v", keyname) 86 } 87 keydata := keydataset{Name: keyname, Value: keyvalue, DisplayName: displayname, URL: url} 88 tr.Keys = append(tr.Keys, keydata) 89 return nil 90 } 91 92 // AddContext - add additional context information 93 // second call with the same label will overwrite the first call's data 94 func (tr *Toolrecord) AddContext(label string, data interface{}) error { 95 if label == "" { 96 return errors.New("TR_ADD_CONTEXT: no label supplied") 97 } 98 tr.Context[label] = data 99 return nil 100 } 101 102 // GetFileName - local filename for the current record 103 func (tr *Toolrecord) GetFileName() string { 104 return tr.reportFileName 105 } 106 107 // Persist - write the current record to file system 108 func (tr *Toolrecord) Persist() error { 109 if tr.workspace == "" { 110 return errors.New("TR_PERSIST: empty workspace ") 111 } 112 if tr.ToolName == "" { 113 return errors.New("TR_PERSIST: empty toolName") 114 } 115 if tr.ToolInstance == "" { 116 return errors.New("TR_PERSIST: empty instanceName") 117 } 118 // create workspace/toolrecord 119 dirPath := filepath.Join(tr.workspace, "toolruns") 120 err := tr.fileUtils.MkdirAll(dirPath, os.ModePerm) 121 if err != nil { 122 return fmt.Errorf("TR_PERSIST: %v", err) 123 } 124 125 // set default display data if required 126 if tr.DisplayName == "" { 127 tr.GenerateDefaultDisplayData() 128 } 129 130 file, err := json.Marshal(tr) 131 if err != nil { 132 return fmt.Errorf("TR_PERSIST: %v", err) 133 } 134 // no json generated ? 135 if len(file) == 0 { 136 return fmt.Errorf("TR_PERSIST: empty json content") 137 } 138 err = tr.fileUtils.WriteFile(tr.GetFileName(), file, 0o644) 139 if err != nil { 140 return fmt.Errorf("TR_PERSIST: %v", err) 141 } 142 return nil 143 } 144 145 // GenerateDefaultDisplayData - default aggregation for overall displayName and URL 146 // can be overriden by calling SetOverallDisplayData 147 func (tr *Toolrecord) GenerateDefaultDisplayData() { 148 displayName := "" 149 displayURL := "" 150 for _, keyset := range tr.Keys { 151 // create "name1 - name2 - name3" 152 subDisplayName := keyset.DisplayName 153 if subDisplayName != "" { 154 if displayName != "" { 155 displayName = displayName + " - " 156 } 157 displayName = displayName + subDisplayName 158 } 159 subURL := keyset.URL 160 if subURL != "" { 161 displayURL = subURL 162 } 163 } 164 tr.DisplayName = displayName 165 tr.DisplayURL = displayURL 166 } 167 168 // SetOverallDisplayData - override the default generation for DisplayName & DisplayURL 169 func (tr *Toolrecord) SetOverallDisplayData(newName, newURL string) { 170 tr.DisplayName = newName 171 tr.DisplayURL = newURL 172 }