gitlab.com/ignitionrobotics/web/ign-go@v1.0.0-rc4/utility.go (about) 1 package ign 2 3 import ( 4 "archive/zip" 5 "bytes" 6 "errors" 7 "fmt" 8 "github.com/dgrijalva/jwt-go" 9 htmlTemplate "html/template" 10 "io" 11 "math/rand" 12 "net/http" 13 "os" 14 "path/filepath" 15 "regexp" 16 "runtime" 17 "sort" 18 "strings" 19 "text/template" 20 "time" 21 ) 22 23 // GetUserIdentity returns the user identity found in the http request's JWT 24 // token. 25 func GetUserIdentity(r *http.Request) (identity string, ok bool) { 26 // We use the claimed subject contained in the JWT as the ID. 27 jwtUser := r.Context().Value("user") 28 if jwtUser == nil { 29 return 30 } 31 var sub interface{} 32 sub, ok = jwtUser.(*jwt.Token).Claims.(jwt.MapClaims)["sub"] 33 if !ok { 34 return 35 } 36 identity, ok = sub.(string) 37 return 38 } 39 40 // ReadEnvVar reads an environment variable and return an error if not present 41 func ReadEnvVar(name string) (string, error) { 42 value := os.Getenv(name) 43 if value == "" { 44 return "", errors.New("Missing " + name + " env variable.") 45 } 46 return value, nil 47 } 48 49 // Unzip a memory buffer 50 // TODO: remove. Unused code. 51 func Unzip(buff bytes.Buffer, size int64, dest string, verbose bool) error { 52 reader, err := zip.NewReader(bytes.NewReader(buff.Bytes()), size) 53 if err != nil { 54 return errors.New("unzip: Unable to read byte buffer") 55 } 56 return UnzipImpl(reader, dest, verbose) 57 } 58 59 // UnzipFile extracts a compressed .zip file 60 // TODO: remove. Unused code. 61 func UnzipFile(zipfile string, dest string, verbose bool) error { 62 reader, err := zip.OpenReader(zipfile) 63 if err != nil { 64 return errors.New("unzip: Unable to open [" + zipfile + "]") 65 } 66 defer reader.Close() 67 return UnzipImpl(&reader.Reader, dest, verbose) 68 } 69 70 // UnzipImpl is a helper unzip implementation 71 // TODO: remove. Unused code. 72 func UnzipImpl(reader *zip.Reader, dest string, verbose bool) error { 73 for _, f := range reader.File { 74 zipped, err := f.Open() 75 if err != nil { 76 return errors.New("unzip: Unable to open [" + f.Name + "]") 77 } 78 79 defer zipped.Close() 80 81 path := filepath.Join(dest, f.Name) 82 if f.FileInfo().IsDir() { 83 os.MkdirAll(path, f.Mode()) 84 if verbose { 85 fmt.Println("Creating directory", path) 86 } 87 } else { 88 // Ensure we create the parent folder 89 err := os.MkdirAll(filepath.Dir(path), os.ModePerm) 90 if err != nil { 91 return errors.New("unzip: Unable to create parent folder [" + path + "]") 92 } 93 94 writer, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, f.Mode()) 95 if err != nil { 96 return errors.New("unzip: Unable to create [" + path + "]") 97 } 98 99 defer writer.Close() 100 101 if _, err = io.Copy(writer, zipped); err != nil { 102 return errors.New("unzip: Unable to create content in [" + path + "]") 103 } 104 105 if verbose { 106 fmt.Println("Decompressing : ", path) 107 } 108 } 109 } 110 return nil 111 } 112 113 // Trace returns the filename, line and function name of its caller. The skip 114 // parameter is the number of stack frames to skip, with 1 identifying the 115 // Trace frame itself. Skip will be set to 1 if the passed in value is <= 0. 116 // Ref: http://stackoverflow.com/questions/25927660/golang-get-current-scope-of-function-name 117 func Trace(skip int64) string { 118 skip = Max(skip, int64(1)) 119 120 // At least one entry needed 121 pc := make([]uintptr, 10) 122 count := Min(int64(runtime.Callers(int(skip), pc)), int64(10)) 123 124 result := "" 125 for i := int64(0); i < count; i++ { 126 f := runtime.FuncForPC(pc[i]) 127 file, line := f.FileLine(pc[i]) 128 result = fmt.Sprintf("%s%d %s : %d in %s\n", result, i, 129 filepath.Base(file), line, f.Name()) 130 } 131 132 return result 133 } 134 135 // RandomString creates a random string of a given length. 136 // Ref: https://siongui.github.io/2015/04/13/go-generate-random-string/ 137 func RandomString(strlen int) string { 138 rand.Seed(time.Now().UTC().UnixNano()) 139 const chars = "abcdefghijklmnopqrstuvwxyz" 140 result := make([]byte, strlen) 141 for i := 0; i < strlen; i++ { 142 result[i] = chars[rand.Intn(len(chars))] 143 } 144 return string(result) 145 } 146 147 // Min is an implementation of "int" Min 148 // See https://mrekucci.blogspot.com.ar/2015/07/dont-abuse-mathmax-mathmin.html 149 func Min(x, y int64) int64 { 150 if x < y { 151 return x 152 } 153 return y 154 } 155 156 // Max is an implementation of "int" Max 157 // See https://mrekucci.blogspot.com.ar/2015/07/dont-abuse-mathmax-mathmin.html 158 func Max(x, y int64) int64 { 159 if x > y { 160 return x 161 } 162 return y 163 } 164 165 // StrToSlice returns the slice of strings with all tags parsed from the input 166 // string. 167 // It will trim leading and trailing whitespace, and reduce middle whitespaces to 1 space. 168 // It will also remove 'empty' tags (ie. whitespaces enclosed with commas, ', ,') 169 // The input string contains tags separated with commas. 170 // E.g. input string: " tag1, tag2, tag3 , , " 171 // E.g. output: ["tag1", "tag2", "tag3"] 172 func StrToSlice(tagsStr string) []string { 173 if tagsStr == "" { 174 return nil 175 } 176 noSpaces := strings.TrimSpace(tagsStr) 177 noSpaces = strings.TrimPrefix(noSpaces, ",") 178 noSpaces = strings.TrimSuffix(noSpaces, ",") 179 // regexp to remove duplicate spaces 180 reInsideWhtsp := regexp.MustCompile(`[\s\p{Zs}]{2,}`) 181 var result []string 182 for _, t := range strings.Split(noSpaces, ",") { 183 t = strings.TrimSpace(t) 184 if len(t) > 0 { 185 result = append(result, reInsideWhtsp.ReplaceAllString(t, " ")) 186 } 187 } 188 return result 189 } 190 191 // SameElements returns True if the two given string slices contain the same 192 // elements, even in different order. 193 func SameElements(a0, b0 []string) bool { 194 // shallow copy input arrays 195 a := append([]string(nil), a0...) 196 b := append([]string(nil), b0...) 197 198 if a == nil && b == nil { 199 return true 200 } 201 if a == nil || b == nil { 202 return false 203 } 204 if len(a) != len(b) { 205 return false 206 } 207 208 sort.Strings(a) 209 sort.Strings(b) 210 for i := range a { 211 if a[i] != b[i] { 212 return false 213 } 214 } 215 return true 216 } 217 218 // ParseTemplate opens a template and replaces placeholders with values. 219 func ParseTemplate(templateFilename string, data interface{}) (string, error) { 220 t, err := template.ParseFiles(templateFilename) 221 if err != nil { 222 return "", err 223 } 224 buf := new(bytes.Buffer) 225 if err = t.Execute(buf, data); err != nil { 226 return "", err 227 } 228 return buf.String(), nil 229 } 230 231 // ParseHTMLTemplate opens an HTML template and replaces placeholders with values. 232 func ParseHTMLTemplate(templateFilename string, data interface{}) (string, error) { 233 t, err := htmlTemplate.ParseFiles(templateFilename) 234 if err != nil { 235 return "", err 236 } 237 buf := new(bytes.Buffer) 238 if err = t.Execute(buf, data); err != nil { 239 return "", err 240 } 241 return buf.String(), nil 242 } 243 244 // IsError returns true when err is the target error. 245 func IsError(err error, target error) bool { 246 return strings.Contains(err.Error(), target.Error()) 247 }