package utils import ( "bytes" "github.com/mohae/deepcopy" "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors" "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/string_utils" "net/mail" "net/url" "os" "reflect" "regexp" "strings" ) // GetEnv is a helper function for getting environment variables with a default func GetEnv(name string, def string) (env string) { // If variable not set, provide default if env = os.Getenv(name); env == "" { env = def } return } // CorsHeaders returns a map to allow Cors func CorsHeaders() map[string]string { return map[string]string{ "Access-Control-Allow-Origin": "*", // do not wildcard: https://stackoverflow.com/questions/13146892/cors-access-control-allow-headers-wildcard-being-ignored "Access-Control-Allow-Headers": "authorization, content-type, referer, sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform, user-agent, x-amz-date, x-amz-security-token", "Access-Control-Allow-Methods": "OPTIONS,GET,PUT,POST,DELETE,PATCH,HEAD", "Access-Control-Max-Age": "86400", "Access-Control-Allow-Credentials": "true", } } func DeepCopy(fromValue interface{}) (toValue interface{}) { return deepcopy.Copy(fromValue) } func Clone[T any](fromValue T) (toValue T) { return deepcopy.Copy(fromValue) } func ValueToPointer[V any](value V) *V { return &value } func PointerToValue[V any](value *V) V { if value != nil { return *value } return *new(V) // zero value of V } func ValidateEmailAddress(email string) (string, error) { if email == "" { return "", errors.Error("email address is empty") } cleanEmail := strings.ToLower(strings.TrimSpace(email)) cleanEmail = string_utils.RemoveAllWhiteSpaces(cleanEmail) // We validate it but still return it since in some cases we don't want to break everything if the email is bad _, err := mail.ParseAddress(cleanEmail) if err != nil { return cleanEmail, errors.Wrap(err, "could not parse email address") } return cleanEmail, nil } func StripEmail(email string) (strippedEmail string, strippedDomain string) { // Strip the email address from the + to the @ // Define a regular expression pattern to match the "+" to "@" part emailPattern := `(\+.*@)` // Define the regular expression pattern to match the domain part after "@" domainPattern := `@(.+)` // Compile the regular expression emailRegex := regexp.MustCompile(emailPattern) domainRegex := regexp.MustCompile(domainPattern) // Replace the matched part with an empty string strippedEmail = emailRegex.ReplaceAllString(email, "@") // Find the first match in the email address match := domainRegex.FindStringSubmatch(email) // Check if a match was found if len(match) > 1 { // The domain part (excluding "@") is in the first capture group (index 1) strippedDomain = match[1] } return strippedEmail, strippedDomain } // IsUrlStrict Returns whether a URL is valid in a strict way (Must have scheme and host) func IsUrlStrict(str string) bool { u, err := url.Parse(str) return err == nil && u.Scheme != "" && u.Host != "" } func IsValidUsername(str string) bool { regex := regexp.MustCompile("^[a-zA-Z0-9-.@+_]*$") return regex.MatchString(str) } // StandardisePhoneNumber standardises phone numbers with +27 instead of 0 prefix func StandardisePhoneNumber(number string) string { number = strings.TrimSpace(number) if number == "" { return number } // is the first rune/char of the string a 0 if []rune(number)[0] == []rune("0")[0] { // Add south african country code (hardcoded for now) number = "+27" + number[1:] } return number } func FormatPhoneNumber(phoneNumber string) string { if len(phoneNumber) > 7 { return phoneNumber } // Format as 076 453 2188 phoneNumber = insertInto(phoneNumber, 3, " ") phoneNumber = insertInto(phoneNumber, 7, " ") return phoneNumber } func insertInto(s string, index int, characters string) string { return s[:index] + characters + s[index:] } // RemoveUrlScheme Removes http:// or https:// from a URL func RemoveUrlScheme(str string) string { newStr := strings.Replace(str, "http://", "", 1) newStr = strings.Replace(str, "https://", "", 1) return newStr } // StripQueryString - Strips the query parameters from a URL func StripQueryString(inputUrl string) (string, error) { u, err := url.Parse(inputUrl) if err != nil { return inputUrl, err } u.RawQuery = "" return u.String(), nil } // EscapeOpenSearchSearchString See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#_reserved_characters func EscapeOpenSearchSearchString(str string) string { searchString := str // Reserved characters // NOTE: first char must be "\" to prevent replacing it again after replacing other chars with "\\" reservedCharacters := []string{"\\", "+", "-", "=", "&&", "||", "!", "(", ")", "{", "}", "[", "]", "^", "\"", "~", "*", "?", ":", "/"} // Remove "<" and ">" strings.ReplaceAll(searchString, "<", "") strings.ReplaceAll(searchString, ">", "") // Escape the reserved characters with double backslashes ("\\") for _, char := range reservedCharacters { if strings.Contains(searchString, char) { re := regexp.MustCompile(char) searchString = re.ReplaceAllString(searchString, "\\"+char) } } return searchString } // IsEqual returns if the two objects are equal func IsEqual(expected interface{}, actual interface{}) bool { if expected == nil || actual == nil { return expected == actual } if exp, ok := expected.([]byte); ok { act, ok := actual.([]byte) if !ok { return false } if exp == nil || act == nil { return true } return bytes.Equal(exp, act) } return reflect.DeepEqual(expected, actual) }