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  }