Skip to content
Snippets Groups Projects
Commit df13b406 authored by Alena Petraki's avatar Alena Petraki :nail_care_tone1:
Browse files

Merge branch 'feature/PRXS-1066-PerxisGoUtil' into 'feature/1004-AddPublicEntities'

Добавлен util

See merge request perxis/perxis-go!13
parents 10aa1040 af1d5d1f
No related branches found
No related tags found
No related merge requests found
......@@ -16,4 +16,3 @@ run_tests:
when: always
reports:
junit: report.xml
......@@ -3,26 +3,35 @@ module git.perx.ru/perxis/perxis-go
go 1.18
require (
github.com/getsentry/sentry-go v0.12.0
github.com/go-kit/kit v0.12.0
github.com/golang/protobuf v1.5.2
github.com/hashicorp/go-multierror v1.1.1
github.com/json-iterator/go v1.1.12
github.com/pkg/errors v0.9.1
github.com/rs/xid v1.4.0
github.com/stretchr/testify v1.7.0
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf
go.uber.org/zap v1.19.1
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
golang.org/x/net v0.0.0-20211008194852-3b03d305991f
google.golang.org/grpc v1.45.0
google.golang.org/protobuf v1.28.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)
require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-kit/log v0.2.0 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/google/go-cmp v0.5.7 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.1.0 // indirect
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
This diff is collapsed.
package data
import (
"crypto/rand"
"crypto/sha512"
"encoding/base64"
"git.perx.ru/perxis/perxis-go/pkg/errors"
"golang.org/x/crypto/bcrypt"
)
func GetHash(value []byte) ([]byte, error) {
return bcrypt.GenerateFromPassword(value, 0)
}
func CompareHash(value, hash []byte) error {
return bcrypt.CompareHashAndPassword(hash, value)
}
func GetHashString(value string) (string, error) {
hash, err := GetHash([]byte(value))
if err != nil {
return "", err
}
return string(hash), nil
}
func CompareHashString(value, hash string) error {
return CompareHash([]byte(value), []byte(hash))
}
func GenerateRandomBytes(n int) []byte {
b := make([]byte, n)
_, err := rand.Read(b)
// Note that err == nil only if we read len(b) bytes.
if err != nil {
panic(errors.Wrap(err, "rand.Read error"))
}
return b
}
func GenerateRandomString(n int) string {
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
bytes := GenerateRandomBytes(n)
for i, b := range bytes {
bytes[i] = letters[b%byte(len(letters))]
}
return string(bytes)
}
func GenerateRandomStringURLSafe(n int) string {
b := GenerateRandomBytes(n)
return base64.URLEncoding.EncodeToString(b)
}
func SignValue(signature, value string) string {
hash := sha512.Sum512([]byte(value + "_" + signature))
encoded := base64.StdEncoding.EncodeToString(hash[:])
return encoded
}
package data
import (
"fmt"
"gopkg.in/yaml.v3"
)
func Contains[T comparable](s T, arr []T) bool {
for _, elem := range arr {
if elem == s {
return true
}
}
return false
}
func ElementsMatch[T comparable](s1, s2 []T) bool {
if len(s1) != len(s2) {
return false
}
exists := make(map[T]bool)
for _, value := range s1 {
exists[value] = true
}
for _, value := range s2 {
if !exists[value] {
return false
}
}
return true
}
// Difference returns the elements in `a` that aren't in `b`.
func Difference(a, b []string) []string {
mb := make(map[string]struct{}, len(b))
for _, x := range b {
mb[x] = struct{}{}
}
var diff []string
for _, x := range a {
if _, found := mb[x]; !found {
diff = append(diff, x)
}
}
return diff
}
// GetIntersection returns `a` elements that are in `b`.
func GetIntersection[T comparable](a, b []T) []T {
mb := make(map[T]struct{}, len(b))
for _, x := range b {
mb[x] = struct{}{}
}
var match []T
for _, x := range a {
if _, found := mb[x]; found {
match = append(match, x)
}
}
return match
}
func GetXOR[T comparable](s1, s2 []T) []T {
res := make([]T, 0)
m1 := make(map[T]struct{}, len(s1))
m2 := make(map[T]struct{}, len(s2))
for _, s := range s1 {
m1[s] = struct{}{}
}
for _, s := range s2 {
if _, ok := m1[s]; !ok {
res = append(res, s)
}
m2[s] = struct{}{}
}
for _, s := range s1 {
if _, ok := m2[s]; !ok {
res = append(res, s)
}
}
return res
}
func SetFromSlice[T comparable](input []T) []T {
s := make([]T, 0, len(input))
m := make(map[T]bool)
for _, val := range input {
if _, ok := m[val]; !ok {
m[val] = true
s = append(s, val)
}
}
return s
}
func ToSliceOfInterfaces[T any](s []T) []interface{} {
if s == nil {
return nil
}
res := make([]interface{}, len(s))
for i, e := range s {
res[i] = e
}
return res
}
func ToSliceOfStrings(s []any) []string {
if s == nil {
return nil
}
res := make([]string, len(s))
for i, v := range s {
res[i] = fmt.Sprintf("%v", v)
}
return res
}
func MergeYaml(source, override string) (string, error) {
if source == "" {
return override, nil
}
if override == "" {
return source, nil
}
var srcYaml map[string]interface{}
err := yaml.Unmarshal([]byte(source), &srcYaml)
if err != nil {
return "", err
}
var overrideYaml map[string]interface{}
err = yaml.Unmarshal([]byte(override), &overrideYaml)
if err != nil {
return "", err
}
mergedMap := mergeMaps(srcYaml, overrideYaml)
mergedString, err := yaml.Marshal(mergedMap)
if err != nil {
return "", err
}
return string(mergedString), nil
}
func mergeMaps(a, b map[string]interface{}) map[string]interface{} {
out := make(map[string]interface{}, len(a))
for k, v := range a {
out[k] = v
}
for k, v := range b {
if v, ok := v.(map[string]interface{}); ok {
if av, ok := out[k]; ok {
if av, ok := av.(map[string]interface{}); ok {
out[k] = mergeMaps(av, v)
continue
}
}
}
if v, ok := v.([]interface{}); ok {
if av, ok := out[k]; ok {
if av, ok := av.([]interface{}); ok {
out[k] = append(av, v...)
continue
}
}
}
out[k] = v
}
return out
}
package data
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMergeYaml(t *testing.T) {
tests := []struct {
name string
source string
override string
res string
wantErr bool
}{
{"simple change",
"a: 1\nb: cat",
"a: 2",
"a: 2\nb: cat\n",
false,
},
{"simple not change",
"a: 1\nb: cat",
"a: 1",
"a: 1\nb: cat\n",
false,
},
{"object merge",
`
keyA1: val1
keyA2: val2
keyB1: val1
keyB2: val2`,
`
keyС1: val1
keyС2: val2
keyВ1: val1
keyВ2: val2`,
`keyA1: val1
keyA2: val2
keyB1: val1
keyB2: val2
keyВ1: val1
keyВ2: val2
keyС1: val1
keyС2: val2
`,
false,
},
{"object change",
`
keyA1: val1
keyA2: val2
keyB1: val1
keyB2: val2`,
`
keyA1: val10
keyA2: val20
keyB1: val10
keyB2: val20`,
`keyA1: val10
keyA2: val20
keyB1: val10
keyB2: val20
`,
false,
},
{"slice of objects merge",
`entries:
- keyA1: val1
keyA2: val2
- keyB1: val1
keyB2: val2
- val3`,
`entries:
- keyC1: val1
keyC2: val2
- keyD1: val1
keyD2: val2
- val30`,
`entries:
- keyA1: val1
keyA2: val2
- keyB1: val1
keyB2: val2
- val3
- keyC1: val1
keyC2: val2
- keyD1: val1
keyD2: val2
- val30
`,
false,
},
{"slice of objects change",
`entries:
- keyA1: val1
keyA2: val2
- keyB1: val1
keyB2: val2
- val3`,
`entries:
- keyA1: val10
keyA2: val20
- keyB1: val10
keyB2: val20`,
`entries:
- keyA1: val1
keyA2: val2
- keyB1: val1
keyB2: val2
- val3
- keyA1: val10
keyA2: val20
- keyB1: val10
keyB2: val20
`,
false,
},
{"invalid string (missing space)",
"a:1",
"a:2",
"",
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res, err := MergeYaml(tt.source, tt.override)
assert.Equal(t, tt.res, res)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
package data
import (
"regexp"
"strings"
"unicode"
)
var (
replacer = strings.NewReplacer("?", ".", "*", ".*")
)
func IsRegExp(s string) bool {
var prev rune
if len(s) < 2 {
return false
}
if s[0] == '^' || s[len(s)-1] == '$' {
return true
}
for _, r := range s {
switch r {
case '*', '+':
if prev == '.' {
return true
}
}
prev = r
}
return false
}
func CamelCase(name string) string {
newName := []rune{}
for i, c := range name {
if i == 0 {
newName = append(newName, unicode.ToLower(c))
} else {
newName = append(newName, c)
}
}
return string(newName)
}
func GlobMatch(s string, matches ...string) bool {
for _, match := range matches {
if match == "" {
return false
}
if s == match || match == "*" {
return true
}
ok, err := regexp.MatchString(GlobToRegexp(match), s)
if err != nil {
return false
}
if ok {
return true
}
}
return false
}
func GlobToRegexp(s string) string {
s = strings.TrimSpace(s)
if s == "*" {
return s
}
// если вначале не звездочка, то ищем с начала (важно для применения индексов mongo)
if s[0] != '*' {
s = "^" + s
} else {
s = s[1:]
}
// убираем последую звездочку, работает быстрее в mongo
if s[len(s)-1] == '*' {
s = s[:len(s)-1]
} else {
s = s + "$"
}
return replacer.Replace(s)
}
package data
import (
"fmt"
"reflect"
"testing"
)
func TestIsRegExp(t *testing.T) {
tests := []struct {
str string
want bool
}{
{"^some", true},
{"some$", true},
{"some.*", true},
{"some.+", true},
{"some*", false},
{"some+", false},
{"so.me*", false},
{"some.", false},
{"s^om$e", false},
}
for i, tt := range tests {
t.Run(fmt.Sprintf("test%d", i), func(t *testing.T) {
if got := IsRegExp(tt.str); got != tt.want {
t.Errorf("IsRegExp(\"%s\") = %v, want %v", tt.str, got, tt.want)
}
})
}
}
func TestGlobToRegexp(t *testing.T) {
tests := []struct {
name string
args string
want interface{}
}{
{"from start", "aaa*", "^aaa"},
{"from end", "*aaa", "aaa$"},
{"middle", "*aaa*", "aaa"},
{"start end", "aa*bb", "^aa.*bb$"},
{"middle and chars", "aa*bb??", "^aa.*bb..$"},
{"anything", "*", "*"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GlobToRegexp(tt.args); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Match() = %v, want %v", got, tt.want)
}
})
}
}
func TestGlobMatch(t *testing.T) {
tests := []struct {
name string
s string
glob []string
want bool
}{
{"from start", "aaateststring", []string{"aaa*"}, true},
{"from end", "teststringaaa", []string{"*aaa"}, true},
{"middle", "testaaastring", []string{"*aaa*"}, true},
{"start end", "aateststringbb", []string{"aa*bb"}, true},
{"any of", "teststringaa", []string{"aa*", "aa*bb", "*aa"}, true},
{"none matches", "teststringaa", []string{"aa*", "aa*bb", "*bb"}, false},
{"middle and chars", "aateststringbbcc", []string{"aa*bb??"}, true},
{"anything", "teststring", []string{"*"}, true},
{"false from start", "aateststring", []string{"aaa*"}, false},
{"false from end", "teststringaa", []string{"*aaa"}, false},
{"false middle", "testaastring", []string{"*aaa*"}, false},
{"false start end", "aateststringb", []string{"aa*bb"}, false},
{"false middle and chars", "ateststringbbcc", []string{"aa*bb??"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GlobMatch(tt.s, tt.glob...); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Match() = %v, want %v", got, tt.want)
}
})
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment