mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-12 01:10:00 +08:00
Compare commits
62 Commits
v1.7.2
...
tools/goct
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfe03ea9e1 | ||
|
|
48d21ef8ad | ||
|
|
28a001c5f9 | ||
|
|
22a41cacc7 | ||
|
|
fcc246933c | ||
|
|
5c3679ffe7 | ||
|
|
eaa01ccb9f | ||
|
|
b8206fb46a | ||
|
|
1c3876810e | ||
|
|
1d9159ea39 | ||
|
|
2159d112c3 | ||
|
|
f57874a51f | ||
|
|
8625864d43 | ||
|
|
8f9ba3ec11 | ||
|
|
a1d9bc08f0 | ||
|
|
b9d7f1cc77 | ||
|
|
6700910f64 | ||
|
|
9c4ed394a7 | ||
|
|
fd07a9c6e4 | ||
|
|
b8c239630c | ||
|
|
672ea55736 | ||
|
|
f7097866bf | ||
|
|
796b2bd1b0 | ||
|
|
e1e5fb2071 | ||
|
|
89ecb50005 | ||
|
|
dbed1ea042 | ||
|
|
ad291daf78 | ||
|
|
13746a3706 | ||
|
|
f03b13f632 | ||
|
|
f6f64b1286 | ||
|
|
300a415f5d | ||
|
|
c5de546f8a | ||
|
|
cad243905f | ||
|
|
7c8f41d577 | ||
|
|
cbd118d55f | ||
|
|
9d2a1b8b0a | ||
|
|
f6ada979aa | ||
|
|
53a74759a5 | ||
|
|
1940f7bd58 | ||
|
|
18cb3141ba | ||
|
|
f822c9a94f | ||
|
|
1a3dc75874 | ||
|
|
796dd5b6e2 | ||
|
|
94e476ade7 | ||
|
|
2a74996e1b | ||
|
|
f52af1ebf9 | ||
|
|
24450f18bb | ||
|
|
f1a45d8a23 | ||
|
|
9aebba1566 | ||
|
|
4998479f9a | ||
|
|
873d1351ee | ||
|
|
afcbca8f24 | ||
|
|
5b8126c2cf | ||
|
|
4dfaf35151 | ||
|
|
00cd77c92b | ||
|
|
2145a7a93c | ||
|
|
d5302f2dbe | ||
|
|
6181594bc8 | ||
|
|
11c10e51ff | ||
|
|
3f03126d27 | ||
|
|
d43adc2823 | ||
|
|
656222b572 |
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
|
||||
test-win:
|
||||
name: Windows
|
||||
|
||||
@@ -189,7 +189,7 @@ func buildFieldsInfo(tp reflect.Type, fullName string) (*fieldInfo, error) {
|
||||
switch tp.Kind() {
|
||||
case reflect.Struct:
|
||||
return buildStructFieldsInfo(tp, fullName)
|
||||
case reflect.Array, reflect.Slice:
|
||||
case reflect.Array, reflect.Slice, reflect.Map:
|
||||
return buildFieldsInfo(mapping.Deref(tp.Elem()), fullName)
|
||||
case reflect.Chan, reflect.Func:
|
||||
return nil, fmt.Errorf("unsupported type: %s", tp.Kind())
|
||||
@@ -332,6 +332,8 @@ func toLowerCaseKeyMap(m map[string]any, info *fieldInfo) map[string]any {
|
||||
res[lk] = toLowerCaseInterface(v, ti)
|
||||
} else if info.mapField != nil {
|
||||
res[k] = toLowerCaseInterface(v, info.mapField)
|
||||
} else if vv, ok := v.(map[string]any); ok {
|
||||
res[k] = toLowerCaseKeyMap(vv, info)
|
||||
} else {
|
||||
res[k] = v
|
||||
}
|
||||
|
||||
@@ -1192,6 +1192,29 @@ Email = "bar"`)
|
||||
assert.Len(t, c.Value, 2)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("multi layer map", func(t *testing.T) {
|
||||
type Value struct {
|
||||
User struct {
|
||||
Name string
|
||||
}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Value map[string]map[string]Value
|
||||
}
|
||||
|
||||
var input = []byte(`
|
||||
[Value.first.User1.User]
|
||||
Name = "foo"
|
||||
[Value.second.User2.User]
|
||||
Name = "bar"
|
||||
`)
|
||||
var c Config
|
||||
if assert.NoError(t, LoadFromTomlBytes(input, &c)) {
|
||||
assert.Len(t, c.Value, 2)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_getFullName(t *testing.T) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
comma = ","
|
||||
defaultKeyName = "key"
|
||||
delimiter = '.'
|
||||
ignoreKey = "-"
|
||||
@@ -36,6 +37,7 @@ var (
|
||||
defaultCacheLock sync.Mutex
|
||||
emptyMap = map[string]any{}
|
||||
emptyValue = reflect.ValueOf(lang.Placeholder)
|
||||
stringSliceType = reflect.TypeOf([]string{})
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -50,6 +52,7 @@ type (
|
||||
|
||||
unmarshalOptions struct {
|
||||
fillDefault bool
|
||||
fromArray bool
|
||||
fromString bool
|
||||
opaqueKeys bool
|
||||
canonicalKey func(key string) string
|
||||
@@ -79,40 +82,11 @@ func (u *Unmarshaler) Unmarshal(i, v any) error {
|
||||
return u.unmarshal(i, v, "")
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) unmarshal(i, v any, fullName string) error {
|
||||
valueType := reflect.TypeOf(v)
|
||||
if valueType.Kind() != reflect.Ptr {
|
||||
return errValueNotSettable
|
||||
}
|
||||
|
||||
elemType := Deref(valueType)
|
||||
switch iv := i.(type) {
|
||||
case map[string]any:
|
||||
if elemType.Kind() != reflect.Struct {
|
||||
return errTypeMismatch
|
||||
}
|
||||
|
||||
return u.unmarshalValuer(mapValuer(iv), v, fullName)
|
||||
case []any:
|
||||
if elemType.Kind() != reflect.Slice {
|
||||
return errTypeMismatch
|
||||
}
|
||||
|
||||
return u.fillSlice(elemType, reflect.ValueOf(v).Elem(), iv, fullName)
|
||||
default:
|
||||
return errUnsupportedType
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalValuer unmarshals m into v.
|
||||
func (u *Unmarshaler) UnmarshalValuer(m Valuer, v any) error {
|
||||
return u.unmarshalValuer(simpleValuer{current: m}, v, "")
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) unmarshalValuer(m Valuer, v any, fullName string) error {
|
||||
return u.unmarshalWithFullName(simpleValuer{current: m}, v, fullName)
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) fillMap(fieldType reflect.Type, value reflect.Value,
|
||||
mapValue any, fullName string) error {
|
||||
if !value.CanSet() {
|
||||
@@ -172,13 +146,18 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value,
|
||||
baseType := fieldType.Elem()
|
||||
dereffedBaseType := Deref(baseType)
|
||||
dereffedBaseKind := dereffedBaseType.Kind()
|
||||
conv := reflect.MakeSlice(reflect.SliceOf(baseType), refValue.Len(), refValue.Cap())
|
||||
if refValue.Len() == 0 {
|
||||
value.Set(conv)
|
||||
value.Set(reflect.MakeSlice(reflect.SliceOf(baseType), 0, 0))
|
||||
return nil
|
||||
}
|
||||
|
||||
if u.opts.fromArray {
|
||||
refValue = makeStringSlice(refValue)
|
||||
}
|
||||
|
||||
var valid bool
|
||||
conv := reflect.MakeSlice(reflect.SliceOf(baseType), refValue.Len(), refValue.Cap())
|
||||
|
||||
for i := 0; i < refValue.Len(); i++ {
|
||||
ithValue := refValue.Index(i).Interface()
|
||||
if ithValue == nil {
|
||||
@@ -190,17 +169,9 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value,
|
||||
|
||||
switch dereffedBaseKind {
|
||||
case reflect.Struct:
|
||||
target := reflect.New(dereffedBaseType)
|
||||
val, ok := ithValue.(map[string]any)
|
||||
if !ok {
|
||||
return errTypeMismatch
|
||||
}
|
||||
|
||||
if err := u.unmarshal(val, target.Interface(), sliceFullName); err != nil {
|
||||
if err := u.fillStructElement(baseType, conv.Index(i), ithValue, sliceFullName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
SetValue(fieldType.Elem(), conv.Index(i), target.Elem())
|
||||
case reflect.Slice:
|
||||
if err := u.fillSlice(dereffedBaseType, conv.Index(i), ithValue, sliceFullName); err != nil {
|
||||
return err
|
||||
@@ -235,7 +206,7 @@ func (u *Unmarshaler) fillSliceFromString(fieldType reflect.Type, value reflect.
|
||||
return errUnsupportedType
|
||||
}
|
||||
|
||||
baseFieldType := Deref(fieldType.Elem())
|
||||
baseFieldType := fieldType.Elem()
|
||||
baseFieldKind := baseFieldType.Kind()
|
||||
conv := reflect.MakeSlice(reflect.SliceOf(baseFieldType), len(slice), cap(slice))
|
||||
|
||||
@@ -256,29 +227,39 @@ func (u *Unmarshaler) fillSliceValue(slice reflect.Value, index int,
|
||||
}
|
||||
|
||||
ithVal := slice.Index(index)
|
||||
ithValType := ithVal.Type()
|
||||
|
||||
switch v := value.(type) {
|
||||
case fmt.Stringer:
|
||||
return setValueFromString(baseKind, ithVal, v.String())
|
||||
case string:
|
||||
return setValueFromString(baseKind, ithVal, v)
|
||||
case map[string]any:
|
||||
return u.fillMap(ithVal.Type(), ithVal, value, fullName)
|
||||
// deref to handle both pointer and non-pointer types.
|
||||
switch Deref(ithValType).Kind() {
|
||||
case reflect.Struct:
|
||||
return u.fillStructElement(ithValType, ithVal, v, fullName)
|
||||
case reflect.Map:
|
||||
return u.fillMap(ithValType, ithVal, value, fullName)
|
||||
default:
|
||||
return errTypeMismatch
|
||||
}
|
||||
default:
|
||||
// don't need to consider the difference between int, int8, int16, int32, int64,
|
||||
// uint, uint8, uint16, uint32, uint64, because they're handled as json.Number.
|
||||
if ithVal.Kind() == reflect.Ptr {
|
||||
baseType := Deref(ithVal.Type())
|
||||
baseType := Deref(ithValType)
|
||||
if !reflect.TypeOf(value).AssignableTo(baseType) {
|
||||
return errTypeMismatch
|
||||
}
|
||||
|
||||
target := reflect.New(baseType).Elem()
|
||||
target.Set(reflect.ValueOf(value))
|
||||
SetValue(ithVal.Type(), ithVal, target)
|
||||
SetValue(ithValType, ithVal, target)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !reflect.TypeOf(value).AssignableTo(ithVal.Type()) {
|
||||
if !reflect.TypeOf(value).AssignableTo(ithValType) {
|
||||
return errTypeMismatch
|
||||
}
|
||||
|
||||
@@ -309,6 +290,23 @@ func (u *Unmarshaler) fillSliceWithDefault(derefedType reflect.Type, value refle
|
||||
return u.fillSlice(derefedType, value, slice, fullName)
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) fillStructElement(baseType reflect.Type, target reflect.Value,
|
||||
value any, fullName string) error {
|
||||
val, ok := value.(map[string]any)
|
||||
if !ok {
|
||||
return errTypeMismatch
|
||||
}
|
||||
|
||||
// use Deref(baseType) to get the base type in case the type is a pointer type.
|
||||
ptr := reflect.New(Deref(baseType))
|
||||
if err := u.unmarshal(val, ptr.Interface(), fullName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
SetValue(baseType, target, ptr.Elem())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) fillUnmarshalerStruct(fieldType reflect.Type,
|
||||
value reflect.Value, targetValue string) error {
|
||||
if !value.CanSet() {
|
||||
@@ -811,6 +809,19 @@ func (u *Unmarshaler) processNamedField(field reflect.StructField, value reflect
|
||||
return u.processNamedFieldWithoutValue(field.Type, value, opts, fullName)
|
||||
}
|
||||
|
||||
if u.opts.fromArray {
|
||||
fieldKind := field.Type.Kind()
|
||||
if fieldKind != reflect.Slice && fieldKind != reflect.Array {
|
||||
valueKind := reflect.TypeOf(mapValue).Kind()
|
||||
if valueKind == reflect.Slice || valueKind == reflect.Array {
|
||||
val := reflect.ValueOf(mapValue)
|
||||
if val.Len() > 0 {
|
||||
mapValue = val.Index(0).Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return u.processNamedFieldWithValue(field.Type, value, valueWithParent{
|
||||
value: mapValue,
|
||||
parent: valuer,
|
||||
@@ -938,6 +949,35 @@ func (u *Unmarshaler) processNamedFieldWithoutValue(fieldType reflect.Type, valu
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) unmarshal(i, v any, fullName string) error {
|
||||
valueType := reflect.TypeOf(v)
|
||||
if valueType.Kind() != reflect.Ptr {
|
||||
return errValueNotSettable
|
||||
}
|
||||
|
||||
elemType := Deref(valueType)
|
||||
switch iv := i.(type) {
|
||||
case map[string]any:
|
||||
if elemType.Kind() != reflect.Struct {
|
||||
return errTypeMismatch
|
||||
}
|
||||
|
||||
return u.unmarshalValuer(mapValuer(iv), v, fullName)
|
||||
case []any:
|
||||
if elemType.Kind() != reflect.Slice {
|
||||
return errTypeMismatch
|
||||
}
|
||||
|
||||
return u.fillSlice(elemType, reflect.ValueOf(v).Elem(), iv, fullName)
|
||||
default:
|
||||
return errUnsupportedType
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) unmarshalValuer(m Valuer, v any, fullName string) error {
|
||||
return u.unmarshalWithFullName(simpleValuer{current: m}, v, fullName)
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) unmarshalWithFullName(m valuerWithParent, v any, fullName string) error {
|
||||
rv := reflect.ValueOf(v)
|
||||
if err := ValidatePtr(rv); err != nil {
|
||||
@@ -990,6 +1030,16 @@ func WithDefault() UnmarshalOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithFromArray customizes an Unmarshaler with converting array values to non-array types.
|
||||
// For example, if the field type is []string, and the value is [hello],
|
||||
// the field type can be `string`, instead of `[]string`.
|
||||
// Typically, this option is used for unmarshaling from form values.
|
||||
func WithFromArray() UnmarshalOption {
|
||||
return func(opt *unmarshalOptions) {
|
||||
opt.fromArray = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithOpaqueKeys customizes an Unmarshaler with opaque keys.
|
||||
// Opaque keys are keys that are not processed by the unmarshaler.
|
||||
func WithOpaqueKeys() UnmarshalOption {
|
||||
@@ -1122,6 +1172,35 @@ func join(elem ...string) string {
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func makeStringSlice(refValue reflect.Value) reflect.Value {
|
||||
if refValue.Len() != 1 {
|
||||
return refValue
|
||||
}
|
||||
|
||||
element := refValue.Index(0)
|
||||
if element.Kind() != reflect.String {
|
||||
return refValue
|
||||
}
|
||||
|
||||
val, ok := element.Interface().(string)
|
||||
if !ok {
|
||||
return refValue
|
||||
}
|
||||
|
||||
splits := strings.Split(val, comma)
|
||||
if len(splits) <= 1 {
|
||||
return refValue
|
||||
}
|
||||
|
||||
slice := reflect.MakeSlice(stringSliceType, len(splits), len(splits))
|
||||
for i, split := range splits {
|
||||
// allow empty strings
|
||||
slice.Index(i).Set(reflect.ValueOf(split))
|
||||
}
|
||||
|
||||
return slice
|
||||
}
|
||||
|
||||
func newInitError(name string) error {
|
||||
return fmt.Errorf("field %q is not set", name)
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ func TestUnmarshalIntSliceOfPtr(t *testing.T) {
|
||||
assert.Error(t, UnmarshalKey(m, &in))
|
||||
})
|
||||
|
||||
t.Run("int slice with nil", func(t *testing.T) {
|
||||
t.Run("int slice with nil element", func(t *testing.T) {
|
||||
type inner struct {
|
||||
Ints []int `key:"ints"`
|
||||
}
|
||||
@@ -365,6 +365,21 @@ func TestUnmarshalIntSliceOfPtr(t *testing.T) {
|
||||
assert.Empty(t, in.Ints)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("int slice with nil", func(t *testing.T) {
|
||||
type inner struct {
|
||||
Ints []int `key:"ints"`
|
||||
}
|
||||
|
||||
m := map[string]any{
|
||||
"ints": []any(nil),
|
||||
}
|
||||
|
||||
var in inner
|
||||
if assert.NoError(t, UnmarshalKey(m, &in)) {
|
||||
assert.Empty(t, in.Ints)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnmarshalIntWithDefault(t *testing.T) {
|
||||
@@ -1374,20 +1389,82 @@ func TestUnmarshalWithFloatPtr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUnmarshalIntSlice(t *testing.T) {
|
||||
var v struct {
|
||||
Ages []int `key:"ages"`
|
||||
Slice []int `key:"slice"`
|
||||
}
|
||||
m := map[string]any{
|
||||
"ages": []int{1, 2},
|
||||
"slice": []any{},
|
||||
}
|
||||
t.Run("int slice from int", func(t *testing.T) {
|
||||
var v struct {
|
||||
Ages []int `key:"ages"`
|
||||
Slice []int `key:"slice"`
|
||||
}
|
||||
m := map[string]any{
|
||||
"ages": []int{1, 2},
|
||||
"slice": []any{},
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
if ast.NoError(UnmarshalKey(m, &v)) {
|
||||
ast.ElementsMatch([]int{1, 2}, v.Ages)
|
||||
ast.Equal([]int{}, v.Slice)
|
||||
}
|
||||
ast := assert.New(t)
|
||||
if ast.NoError(UnmarshalKey(m, &v)) {
|
||||
ast.ElementsMatch([]int{1, 2}, v.Ages)
|
||||
ast.Equal([]int{}, v.Slice)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("int slice from one int", func(t *testing.T) {
|
||||
var v struct {
|
||||
Ages []int `key:"ages"`
|
||||
}
|
||||
m := map[string]any{
|
||||
"ages": []int{2},
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray())
|
||||
if ast.NoError(unmarshaler.Unmarshal(m, &v)) {
|
||||
ast.ElementsMatch([]int{2}, v.Ages)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("int slice from one int string", func(t *testing.T) {
|
||||
var v struct {
|
||||
Ages []int `key:"ages"`
|
||||
}
|
||||
m := map[string]any{
|
||||
"ages": []string{"2"},
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray())
|
||||
if ast.NoError(unmarshaler.Unmarshal(m, &v)) {
|
||||
ast.ElementsMatch([]int{2}, v.Ages)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("int slice from one json.Number", func(t *testing.T) {
|
||||
var v struct {
|
||||
Ages []int `key:"ages"`
|
||||
}
|
||||
m := map[string]any{
|
||||
"ages": []json.Number{"2"},
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray())
|
||||
if ast.NoError(unmarshaler.Unmarshal(m, &v)) {
|
||||
ast.ElementsMatch([]int{2}, v.Ages)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("int slice from one int strings", func(t *testing.T) {
|
||||
var v struct {
|
||||
Ages []int `key:"ages"`
|
||||
}
|
||||
m := map[string]any{
|
||||
"ages": []string{"1,2"},
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray())
|
||||
if ast.NoError(unmarshaler.Unmarshal(m, &v)) {
|
||||
ast.ElementsMatch([]int{1, 2}, v.Ages)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnmarshalString(t *testing.T) {
|
||||
@@ -1442,6 +1519,36 @@ func TestUnmarshalStringSliceFromString(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice from empty string", func(t *testing.T) {
|
||||
var v struct {
|
||||
Names []string `key:"names"`
|
||||
}
|
||||
m := map[string]any{
|
||||
"names": []string{""},
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray())
|
||||
if ast.NoError(unmarshaler.Unmarshal(m, &v)) {
|
||||
ast.ElementsMatch([]string{""}, v.Names)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice from empty and valid string", func(t *testing.T) {
|
||||
var v struct {
|
||||
Names []string `key:"names"`
|
||||
}
|
||||
m := map[string]any{
|
||||
"names": []string{","},
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray())
|
||||
if ast.NoError(unmarshaler.Unmarshal(m, &v)) {
|
||||
ast.ElementsMatch([]string{"", ""}, v.Names)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice from string with slice error", func(t *testing.T) {
|
||||
var v struct {
|
||||
Names []int `key:"names"`
|
||||
@@ -5639,6 +5746,62 @@ func TestUnmarshalFromStringSliceForTypeMismatch(t *testing.T) {
|
||||
}, &v))
|
||||
}
|
||||
|
||||
func TestUnmarshalWithFromArray(t *testing.T) {
|
||||
t.Run("array", func(t *testing.T) {
|
||||
var v struct {
|
||||
Value []string `key:"value"`
|
||||
}
|
||||
unmarshaler := NewUnmarshaler("key", WithFromArray())
|
||||
if assert.NoError(t, unmarshaler.Unmarshal(map[string]any{
|
||||
"value": []string{"foo", "bar"},
|
||||
}, &v)) {
|
||||
assert.ElementsMatch(t, []string{"foo", "bar"}, v.Value)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("not array", func(t *testing.T) {
|
||||
var v struct {
|
||||
Value string `key:"value"`
|
||||
}
|
||||
unmarshaler := NewUnmarshaler("key", WithFromArray())
|
||||
if assert.NoError(t, unmarshaler.Unmarshal(map[string]any{
|
||||
"value": []string{"foo"},
|
||||
}, &v)) {
|
||||
assert.Equal(t, "foo", v.Value)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("not array and empty", func(t *testing.T) {
|
||||
var v struct {
|
||||
Value string `key:"value"`
|
||||
}
|
||||
unmarshaler := NewUnmarshaler("key", WithFromArray())
|
||||
if assert.NoError(t, unmarshaler.Unmarshal(map[string]any{
|
||||
"value": []string{""},
|
||||
}, &v)) {
|
||||
assert.Empty(t, v.Value)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("not array and no value", func(t *testing.T) {
|
||||
var v struct {
|
||||
Value string `key:"value"`
|
||||
}
|
||||
unmarshaler := NewUnmarshaler("key", WithFromArray())
|
||||
assert.Error(t, unmarshaler.Unmarshal(map[string]any{}, &v))
|
||||
})
|
||||
|
||||
t.Run("not array and no value and optional", func(t *testing.T) {
|
||||
var v struct {
|
||||
Value string `key:"value,optional"`
|
||||
}
|
||||
unmarshaler := NewUnmarshaler("key", WithFromArray())
|
||||
if assert.NoError(t, unmarshaler.Unmarshal(map[string]any{}, &v)) {
|
||||
assert.Empty(t, v.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnmarshalWithOpaqueKeys(t *testing.T) {
|
||||
var v struct {
|
||||
Opaque string `key:"opaque.key"`
|
||||
@@ -5806,6 +5969,38 @@ func TestUnmarshal_Unmarshaler(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseJsonStringValue(t *testing.T) {
|
||||
t.Run("string", func(t *testing.T) {
|
||||
type GoodsInfo struct {
|
||||
Sku int64 `json:"sku,optional"`
|
||||
}
|
||||
|
||||
type GetReq struct {
|
||||
GoodsList []*GoodsInfo `json:"goods_list"`
|
||||
}
|
||||
|
||||
input := map[string]any{"goods_list": "[{\"sku\":11},{\"sku\":22}]"}
|
||||
var v GetReq
|
||||
assert.NotPanics(t, func() {
|
||||
assert.NoError(t, UnmarshalJsonMap(input, &v))
|
||||
assert.Equal(t, 2, len(v.GoodsList))
|
||||
assert.ElementsMatch(t, []int64{11, 22}, []int64{v.GoodsList[0].Sku, v.GoodsList[1].Sku})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("string with invalid type", func(t *testing.T) {
|
||||
type GetReq struct {
|
||||
GoodsList []*int `json:"goods_list"`
|
||||
}
|
||||
|
||||
input := map[string]any{"goods_list": "[{\"sku\":11},{\"sku\":22}]"}
|
||||
var v GetReq
|
||||
assert.NotPanics(t, func() {
|
||||
assert.Error(t, UnmarshalJsonMap(input, &v))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDefaultValue(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var a struct {
|
||||
|
||||
@@ -14,17 +14,29 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
wrapUpTime = time.Second
|
||||
// why we use 5500 milliseconds is because most of our queue are blocking mode with 5 seconds
|
||||
waitTime = 5500 * time.Millisecond
|
||||
// defaultWrapUpTime is the default time to wait before calling wrap up listeners.
|
||||
defaultWrapUpTime = time.Second
|
||||
// defaultWaitTime is the default time to wait before force quitting.
|
||||
// why we use 5500 milliseconds is because most of our queues are blocking mode with 5 seconds
|
||||
defaultWaitTime = 5500 * time.Millisecond
|
||||
)
|
||||
|
||||
var (
|
||||
wrapUpListeners = new(listenerManager)
|
||||
shutdownListeners = new(listenerManager)
|
||||
delayTimeBeforeForceQuit = waitTime
|
||||
wrapUpListeners = new(listenerManager)
|
||||
shutdownListeners = new(listenerManager)
|
||||
wrapUpTime = defaultWrapUpTime
|
||||
waitTime = defaultWaitTime
|
||||
shutdownLock sync.Mutex
|
||||
)
|
||||
|
||||
// ShutdownConf defines the shutdown configuration for the process.
|
||||
type ShutdownConf struct {
|
||||
// WrapUpTime is the time to wait before calling shutdown listeners.
|
||||
WrapUpTime time.Duration `json:",default=1s"`
|
||||
// WaitTime is the time to wait before force quitting.
|
||||
WaitTime time.Duration `json:",default=5.5s"`
|
||||
}
|
||||
|
||||
// AddShutdownListener adds fn as a shutdown listener.
|
||||
// The returned func can be used to wait for fn getting called.
|
||||
func AddShutdownListener(fn func()) (waitForCalled func()) {
|
||||
@@ -39,7 +51,21 @@ func AddWrapUpListener(fn func()) (waitForCalled func()) {
|
||||
|
||||
// SetTimeToForceQuit sets the waiting time before force quitting.
|
||||
func SetTimeToForceQuit(duration time.Duration) {
|
||||
delayTimeBeforeForceQuit = duration
|
||||
shutdownLock.Lock()
|
||||
defer shutdownLock.Unlock()
|
||||
waitTime = duration
|
||||
}
|
||||
|
||||
func Setup(conf ShutdownConf) {
|
||||
shutdownLock.Lock()
|
||||
defer shutdownLock.Unlock()
|
||||
|
||||
if conf.WrapUpTime > 0 {
|
||||
wrapUpTime = conf.WrapUpTime
|
||||
}
|
||||
if conf.WaitTime > 0 {
|
||||
waitTime = conf.WaitTime
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown calls the registered shutdown listeners, only for test purpose.
|
||||
@@ -61,8 +87,12 @@ func gracefulStop(signals chan os.Signal, sig syscall.Signal) {
|
||||
time.Sleep(wrapUpTime)
|
||||
go shutdownListeners.notifyListeners()
|
||||
|
||||
time.Sleep(delayTimeBeforeForceQuit - wrapUpTime)
|
||||
logx.Infof("Still alive after %v, going to force kill the process...", delayTimeBeforeForceQuit)
|
||||
shutdownLock.Lock()
|
||||
remainingTime := waitTime - wrapUpTime
|
||||
shutdownLock.Unlock()
|
||||
|
||||
time.Sleep(remainingTime)
|
||||
logx.Infof("Still alive after %v, going to force kill the process...", waitTime)
|
||||
_ = syscall.Kill(syscall.Getpid(), sig)
|
||||
}
|
||||
|
||||
@@ -82,6 +112,9 @@ func (lm *listenerManager) addListener(fn func()) (waitForCalled func()) {
|
||||
})
|
||||
lm.lock.Unlock()
|
||||
|
||||
// we can return lm.waitGroup.Wait directly,
|
||||
// but we want to make the returned func more readable.
|
||||
// creating an extra closure would be negligible in practice.
|
||||
return func() {
|
||||
lm.waitGroup.Wait()
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -10,8 +11,12 @@ import (
|
||||
)
|
||||
|
||||
func TestShutdown(t *testing.T) {
|
||||
t.Cleanup(restoreSettings)
|
||||
|
||||
SetTimeToForceQuit(time.Hour)
|
||||
assert.Equal(t, time.Hour, delayTimeBeforeForceQuit)
|
||||
shutdownLock.Lock()
|
||||
assert.Equal(t, time.Hour, waitTime)
|
||||
shutdownLock.Unlock()
|
||||
|
||||
var val int
|
||||
called := AddWrapUpListener(func() {
|
||||
@@ -29,7 +34,53 @@ func TestShutdown(t *testing.T) {
|
||||
assert.Equal(t, 3, val)
|
||||
}
|
||||
|
||||
func TestShutdownWithMultipleServices(t *testing.T) {
|
||||
t.Cleanup(restoreSettings)
|
||||
|
||||
SetTimeToForceQuit(time.Hour)
|
||||
shutdownLock.Lock()
|
||||
assert.Equal(t, time.Hour, waitTime)
|
||||
shutdownLock.Unlock()
|
||||
|
||||
var val int32
|
||||
called1 := AddShutdownListener(func() {
|
||||
atomic.AddInt32(&val, 1)
|
||||
})
|
||||
called2 := AddShutdownListener(func() {
|
||||
atomic.AddInt32(&val, 2)
|
||||
})
|
||||
Shutdown()
|
||||
called1()
|
||||
called2()
|
||||
|
||||
assert.Equal(t, int32(3), atomic.LoadInt32(&val))
|
||||
}
|
||||
|
||||
func TestWrapUpWithMultipleServices(t *testing.T) {
|
||||
t.Cleanup(restoreSettings)
|
||||
|
||||
SetTimeToForceQuit(time.Hour)
|
||||
shutdownLock.Lock()
|
||||
assert.Equal(t, time.Hour, waitTime)
|
||||
shutdownLock.Unlock()
|
||||
|
||||
var val int32
|
||||
called1 := AddWrapUpListener(func() {
|
||||
atomic.AddInt32(&val, 1)
|
||||
})
|
||||
called2 := AddWrapUpListener(func() {
|
||||
atomic.AddInt32(&val, 2)
|
||||
})
|
||||
WrapUp()
|
||||
called1()
|
||||
called2()
|
||||
|
||||
assert.Equal(t, int32(3), atomic.LoadInt32(&val))
|
||||
}
|
||||
|
||||
func TestNotifyMoreThanOnce(t *testing.T) {
|
||||
t.Cleanup(restoreSettings)
|
||||
|
||||
ch := make(chan struct{}, 1)
|
||||
|
||||
go func() {
|
||||
@@ -58,3 +109,38 @@ func TestNotifyMoreThanOnce(t *testing.T) {
|
||||
t.Fatal("timeout, check error logs")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
t.Run("valid time", func(t *testing.T) {
|
||||
defer restoreSettings()
|
||||
|
||||
Setup(ShutdownConf{
|
||||
WrapUpTime: time.Second * 2,
|
||||
WaitTime: time.Second * 30,
|
||||
})
|
||||
|
||||
shutdownLock.Lock()
|
||||
assert.Equal(t, time.Second*2, wrapUpTime)
|
||||
assert.Equal(t, time.Second*30, waitTime)
|
||||
shutdownLock.Unlock()
|
||||
})
|
||||
|
||||
t.Run("valid time", func(t *testing.T) {
|
||||
defer restoreSettings()
|
||||
|
||||
Setup(ShutdownConf{})
|
||||
|
||||
shutdownLock.Lock()
|
||||
assert.Equal(t, defaultWrapUpTime, wrapUpTime)
|
||||
assert.Equal(t, defaultWaitTime, waitTime)
|
||||
shutdownLock.Unlock()
|
||||
})
|
||||
}
|
||||
|
||||
func restoreSettings() {
|
||||
shutdownLock.Lock()
|
||||
defer shutdownLock.Unlock()
|
||||
|
||||
wrapUpTime = defaultWrapUpTime
|
||||
waitTime = defaultWaitTime
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ type (
|
||||
Prometheus prometheus.Config `json:",optional"`
|
||||
Telemetry trace.Config `json:",optional"`
|
||||
DevServer DevServerConfig `json:",optional"`
|
||||
Shutdown proc.ShutdownConf `json:",optional"`
|
||||
}
|
||||
)
|
||||
|
||||
@@ -61,6 +62,7 @@ func (sc ServiceConf) SetUp() error {
|
||||
sc.Telemetry.Name = sc.Name
|
||||
}
|
||||
trace.StartAgent(sc.Telemetry)
|
||||
proc.Setup(sc.Shutdown)
|
||||
proc.AddShutdownListener(func() {
|
||||
trace.StopAgent()
|
||||
})
|
||||
|
||||
@@ -76,9 +76,14 @@ func (sg *ServiceGroup) doStart() {
|
||||
}
|
||||
|
||||
func (sg *ServiceGroup) doStop() {
|
||||
group := threading.NewRoutineGroup()
|
||||
for _, service := range sg.services {
|
||||
service.Stop()
|
||||
// new variable to avoid closure problems, can be removed after go 1.22
|
||||
// see https://golang.org/doc/faq#closures_and_goroutines
|
||||
service := service
|
||||
group.Run(service.Stop)
|
||||
}
|
||||
group.Wait()
|
||||
}
|
||||
|
||||
// WithStart wraps a start func as a Service.
|
||||
|
||||
@@ -1197,6 +1197,18 @@ func (s *Redis) PipelinedCtx(ctx context.Context, fn func(Pipeliner) error) erro
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Redis) Publish(channel string, message interface{}) (int64, error) {
|
||||
return s.PublishCtx(context.Background(), channel, message)
|
||||
}
|
||||
|
||||
func (s *Redis) PublishCtx(ctx context.Context, channel string, message interface{}) (int64, error) {
|
||||
conn, err := getRedis(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return conn.Publish(ctx, channel, message).Result()
|
||||
}
|
||||
|
||||
// Rpop is the implementation of redis rpop command.
|
||||
func (s *Redis) Rpop(key string) (string, error) {
|
||||
return s.RpopCtx(context.Background(), key)
|
||||
@@ -1247,6 +1259,18 @@ func (s *Redis) RpushCtx(ctx context.Context, key string, values ...any) (int, e
|
||||
return int(v), nil
|
||||
}
|
||||
|
||||
func (s *Redis) RPopLPush(source string, destination string) (string, error) {
|
||||
return s.RPopLPushCtx(context.Background(), source, destination)
|
||||
}
|
||||
|
||||
func (s *Redis) RPopLPushCtx(ctx context.Context, source string, destination string) (string, error) {
|
||||
conn, err := getRedis(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return conn.RPopLPush(ctx, source, destination).Result()
|
||||
}
|
||||
|
||||
// Sadd is the implementation of redis sadd command.
|
||||
func (s *Redis) Sadd(key string, values ...any) (int, error) {
|
||||
return s.SaddCtx(context.Background(), key, values...)
|
||||
@@ -1645,6 +1669,26 @@ func (s *Redis) TtlCtx(ctx context.Context, key string) (int, error) {
|
||||
return int(duration), nil
|
||||
}
|
||||
|
||||
func (s *Redis) TxPipeline() (pipe Pipeliner, err error) {
|
||||
conn, err := getRedis(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn.TxPipeline(), nil
|
||||
}
|
||||
|
||||
func (s *Redis) Unlink(keys ...string) (int64, error) {
|
||||
return s.UnlinkCtx(context.Background(), keys...)
|
||||
}
|
||||
|
||||
func (s *Redis) UnlinkCtx(ctx context.Context, keys ...string) (int64, error) {
|
||||
conn, err := getRedis(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return conn.Unlink(ctx, keys...).Result()
|
||||
}
|
||||
|
||||
// Zadd is the implementation of redis zadd command.
|
||||
func (s *Redis) Zadd(key string, score int64, value string) (bool, error) {
|
||||
return s.ZaddCtx(context.Background(), key, score, value)
|
||||
|
||||
@@ -2080,3 +2080,70 @@ func (n mockedNode) BLPop(_ context.Context, _ time.Duration, _ ...string) *red.
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func TestRedisPublish(t *testing.T) {
|
||||
runOnRedis(t, func(client *Redis) {
|
||||
_, err := newRedis(client.Addr, badType()).Publish("Test", "message")
|
||||
assert.NotNil(t, err)
|
||||
_, err = client.Publish("Test", "message")
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRedisRPopLPush(t *testing.T) {
|
||||
runOnRedis(t, func(client *Redis) {
|
||||
_, err := newRedis(client.Addr, badType()).RPopLPush("Source", "Destination")
|
||||
assert.NotNil(t, err)
|
||||
_, err = client.Rpush("Source", "Destination")
|
||||
assert.Nil(t, err)
|
||||
_, err = client.RPopLPush("Source", "Destination")
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRedisUnlink(t *testing.T) {
|
||||
runOnRedis(t, func(client *Redis) {
|
||||
_, err := newRedis(client.Addr, badType()).Unlink("Key1", "Key2")
|
||||
assert.NotNil(t, err)
|
||||
err = client.Set("Key1", "Key2")
|
||||
assert.Nil(t, err)
|
||||
get, err := client.Get("Key1")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Key2", get)
|
||||
res, err := client.Unlink("Key1")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(1), res)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRedisTxPipeline(t *testing.T) {
|
||||
runOnRedis(t, func(client *Redis) {
|
||||
ctx := context.Background()
|
||||
pipe, err := newRedis(client.Addr, badType()).TxPipeline()
|
||||
assert.NotNil(t, err)
|
||||
pipe, err = client.TxPipeline()
|
||||
assert.Nil(t, err)
|
||||
key := "key"
|
||||
hashKey := "field"
|
||||
hashValue := "value"
|
||||
|
||||
// setting value
|
||||
pipe.HSet(ctx, key, hashKey, hashValue)
|
||||
|
||||
existsCmd := pipe.Exists(ctx, key)
|
||||
getCmd := pipe.HGet(ctx, key, hashKey)
|
||||
|
||||
// execution
|
||||
_, err = pipe.Exec(ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// verification results
|
||||
exists, err := existsCmd.Result()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(1), exists)
|
||||
|
||||
value, err := getCmd.Result()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, hashValue, value)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/jhump/protoreflect/grpcreflect"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
"github.com/zeromicro/go-zero/core/threading"
|
||||
"github.com/zeromicro/go-zero/gateway/internal"
|
||||
"github.com/zeromicro/go-zero/rest"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
@@ -23,6 +24,7 @@ type (
|
||||
Server struct {
|
||||
*rest.Server
|
||||
upstreams []Upstream
|
||||
conns []zrpc.Client
|
||||
processHeader func(http.Header) []string
|
||||
dialer func(conf zrpc.RpcClientConf) zrpc.Client
|
||||
}
|
||||
@@ -51,8 +53,24 @@ func (s *Server) Start() {
|
||||
}
|
||||
|
||||
// Stop stops the gateway server.
|
||||
// To get a graceful shutdown, it stops the HTTP server first, then closes gRPC connections.
|
||||
func (s *Server) Stop() {
|
||||
// stop the HTTP server first, then close gRPC connections.
|
||||
// in case the gRPC server is stopped first,
|
||||
// the HTTP server may still be running to accept requests.
|
||||
s.Server.Stop()
|
||||
|
||||
group := threading.NewRoutineGroup()
|
||||
for _, conn := range s.conns {
|
||||
// new variable to avoid closure problems, can be removed after go 1.22
|
||||
// see https://golang.org/doc/faq#closures_and_goroutines
|
||||
conn := conn
|
||||
group.Run(func() {
|
||||
// ignore the error when closing the connection
|
||||
_ = conn.Conn().Close()
|
||||
})
|
||||
}
|
||||
group.Wait()
|
||||
}
|
||||
|
||||
func (s *Server) build() error {
|
||||
@@ -71,6 +89,7 @@ func (s *Server) build() error {
|
||||
} else {
|
||||
cli = zrpc.MustNewClient(up.Grpc)
|
||||
}
|
||||
s.conns = append(s.conns, cli)
|
||||
|
||||
source, err := s.createDescriptorSource(cli, up)
|
||||
if err != nil {
|
||||
|
||||
@@ -46,7 +46,7 @@ func dialer() func(context.Context, string) (net.Conn, error) {
|
||||
func TestMustNewServer(t *testing.T) {
|
||||
var c GatewayConf
|
||||
assert.NoError(t, conf.FillDefault(&c))
|
||||
// avoid popup alert on macos for asking permissions
|
||||
// avoid popup alert on MacOS for asking permissions
|
||||
c.DevServer.Host = "localhost"
|
||||
c.Host = "localhost"
|
||||
c.Port = 18881
|
||||
|
||||
40
go.mod
40
go.mod
@@ -4,25 +4,25 @@ go 1.20
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
github.com/alicebob/miniredis/v2 v2.33.0
|
||||
github.com/fatih/color v1.17.0
|
||||
github.com/fullstorydev/grpcurl v1.9.1
|
||||
github.com/alicebob/miniredis/v2 v2.34.0
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/fullstorydev/grpcurl v1.9.2
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jackc/pgx/v5 v5.6.0
|
||||
github.com/jhump/protoreflect v1.16.0
|
||||
github.com/jhump/protoreflect v1.17.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/pelletier/go-toml/v2 v2.2.2
|
||||
github.com/prometheus/client_golang v1.20.2
|
||||
github.com/redis/go-redis/v9 v9.6.1
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/redis/go-redis/v9 v9.7.0
|
||||
github.com/spaolacci/murmur3 v1.1.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
go.etcd.io/etcd/api/v3 v3.5.15
|
||||
go.etcd.io/etcd/client/v3 v3.5.15
|
||||
go.mongodb.org/mongo-driver v1.16.1
|
||||
go.mongodb.org/mongo-driver v1.17.1
|
||||
go.opentelemetry.io/otel v1.24.0
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
|
||||
@@ -31,14 +31,14 @@ require (
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.24.0
|
||||
go.opentelemetry.io/otel/sdk v1.24.0
|
||||
go.opentelemetry.io/otel/trace v1.24.0
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
go.uber.org/goleak v1.3.0
|
||||
golang.org/x/net v0.28.0
|
||||
golang.org/x/sys v0.24.0
|
||||
golang.org/x/time v0.6.0
|
||||
golang.org/x/net v0.33.0
|
||||
golang.org/x/sys v0.28.0
|
||||
golang.org/x/time v0.8.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
google.golang.org/protobuf v1.36.1
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
@@ -52,7 +52,7 @@ require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bufbuild/protocompile v0.10.0 // indirect
|
||||
github.com/bufbuild/protocompile v0.14.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect
|
||||
@@ -100,7 +100,7 @@ require (
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/yuin/gopher-lua v1.1.1 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
|
||||
@@ -109,11 +109,11 @@ require (
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/term v0.27.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
80
go.sum
80
go.sum
@@ -4,15 +4,15 @@ github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7Oputl
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA=
|
||||
github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0=
|
||||
github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
|
||||
github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bufbuild/protocompile v0.10.0 h1:+jW/wnLMLxaCEG8AX9lD0bQ5v9h1RUiMKOBOT5ll9dM=
|
||||
github.com/bufbuild/protocompile v0.10.0/go.mod h1:G9qQIQo0xZ6Uyj6CMNz0saGmx2so+KONo8/KrELABiY=
|
||||
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
|
||||
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
@@ -35,10 +35,10 @@ github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjl
|
||||
github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fullstorydev/grpcurl v1.9.1 h1:YxX1aCcCc4SDBQfj9uoWcTLe8t4NWrZe1y+mk83BQgo=
|
||||
github.com/fullstorydev/grpcurl v1.9.1/go.mod h1:i8gKLIC6s93WdU3LSmkE5vtsCxyRmihUj5FK1cNW5EM=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fullstorydev/grpcurl v1.9.2 h1:ObqVQTZW7aFnhuqQoppUrvep2duMBanB0UYK2Mm8euo=
|
||||
github.com/fullstorydev/grpcurl v1.9.2/go.mod h1:jLfcF55HAz6TYIJY9xFFWgsl0D7o2HlxA5Z4lUG0Tdo=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
@@ -58,8 +58,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
@@ -89,8 +89,8 @@ github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+TBDg=
|
||||
github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8=
|
||||
github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94=
|
||||
github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
@@ -141,16 +141,16 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
|
||||
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
|
||||
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
|
||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
@@ -168,16 +168,17 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
@@ -190,8 +191,8 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
|
||||
go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
|
||||
go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
|
||||
go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8=
|
||||
go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
|
||||
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
|
||||
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
|
||||
@@ -216,8 +217,8 @@ go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeX
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
@@ -226,11 +227,10 @@ go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -242,8 +242,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -251,8 +251,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -264,20 +264,20 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
@@ -295,8 +295,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -299,6 +299,8 @@ go-zero 已被许多公司用于生产部署,接入场景如在线教育、电
|
||||
>100. 上海邑脉科技有限公司
|
||||
>101. 上海巨瓴科技有限公司
|
||||
>102. 深圳市兴海物联科技有限公司
|
||||
>103. 爱芯元智半导体股份有限公司
|
||||
>104. 杭州升恒科技有限公司
|
||||
|
||||
如果贵公司也已使用 go-zero,欢迎在 [登记地址](https://github.com/zeromicro/go-zero/issues/602) 登记,仅仅为了推广,不做其它用途。
|
||||
|
||||
|
||||
@@ -158,9 +158,9 @@ func logDetails(r *http.Request, response *detailLoggedResponseWriter, timer *ut
|
||||
logger := logx.WithContext(r.Context())
|
||||
buf.WriteString(fmt.Sprintf("[HTTP] %s - %d - %s - %s\n=> %s\n",
|
||||
r.Method, code, r.RemoteAddr, timex.ReprOfDuration(duration), dumpRequest(r)))
|
||||
if duration > defaultSlowThreshold {
|
||||
if duration > slowThreshold.Load() {
|
||||
logger.Slowf("[HTTP] %s - %d - %s - slowcall(%s)\n=> %s\n", r.Method, code, r.RemoteAddr,
|
||||
fmt.Sprintf("slowcall(%s)", timex.ReprOfDuration(duration)), dumpRequest(r))
|
||||
timex.ReprOfDuration(duration), dumpRequest(r))
|
||||
}
|
||||
|
||||
body := logs.Flush()
|
||||
|
||||
@@ -3,6 +3,7 @@ package httpx
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
@@ -23,9 +24,16 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
formUnmarshaler = mapping.NewUnmarshaler(formKey, mapping.WithStringValues(), mapping.WithOpaqueKeys())
|
||||
pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues(), mapping.WithOpaqueKeys())
|
||||
validator atomic.Value
|
||||
formUnmarshaler = mapping.NewUnmarshaler(
|
||||
formKey,
|
||||
mapping.WithStringValues(),
|
||||
mapping.WithOpaqueKeys(),
|
||||
mapping.WithFromArray())
|
||||
pathUnmarshaler = mapping.NewUnmarshaler(
|
||||
pathKey,
|
||||
mapping.WithStringValues(),
|
||||
mapping.WithOpaqueKeys())
|
||||
validator atomic.Value
|
||||
)
|
||||
|
||||
// Validator defines the interface for validating the request.
|
||||
@@ -36,16 +44,19 @@ type Validator interface {
|
||||
|
||||
// Parse parses the request.
|
||||
func Parse(r *http.Request, v any) error {
|
||||
if err := ParsePath(r, v); err != nil {
|
||||
return err
|
||||
}
|
||||
kind := mapping.Deref(reflect.TypeOf(v)).Kind()
|
||||
if kind != reflect.Array && kind != reflect.Slice {
|
||||
if err := ParsePath(r, v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ParseForm(r, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ParseForm(r, v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ParseHeaders(r, v); err != nil {
|
||||
return err
|
||||
if err := ParseHeaders(r, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := ParseJsonBody(r, v); err != nil {
|
||||
|
||||
@@ -49,6 +49,151 @@ func TestParseForm(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseFormArray(t *testing.T) {
|
||||
t.Run("slice", func(t *testing.T) {
|
||||
var v struct {
|
||||
Name []string `form:"name"`
|
||||
Age []int `form:"age"`
|
||||
Percent []float64 `form:"percent,optional"`
|
||||
}
|
||||
|
||||
r, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"/a?name=hello&name=world&age=18&age=19&percent=3.4&percent=4.5",
|
||||
http.NoBody)
|
||||
assert.NoError(t, err)
|
||||
if assert.NoError(t, Parse(r, &v)) {
|
||||
assert.ElementsMatch(t, []string{"hello", "world"}, v.Name)
|
||||
assert.ElementsMatch(t, []int{18, 19}, v.Age)
|
||||
assert.ElementsMatch(t, []float64{3.4, 4.5}, v.Percent)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice with single value", func(t *testing.T) {
|
||||
var v struct {
|
||||
Name []string `form:"name"`
|
||||
Age []int `form:"age"`
|
||||
Percent []float64 `form:"percent,optional"`
|
||||
}
|
||||
|
||||
r, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"/a?name=hello&age=18&percent=3.4",
|
||||
http.NoBody)
|
||||
assert.NoError(t, err)
|
||||
if assert.NoError(t, Parse(r, &v)) {
|
||||
assert.ElementsMatch(t, []string{"hello"}, v.Name)
|
||||
assert.ElementsMatch(t, []int{18}, v.Age)
|
||||
assert.ElementsMatch(t, []float64{3.4}, v.Percent)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice with empty", func(t *testing.T) {
|
||||
var v struct {
|
||||
Name []string `form:"name,optional"`
|
||||
}
|
||||
|
||||
r, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"/a",
|
||||
http.NoBody)
|
||||
assert.NoError(t, err)
|
||||
if assert.NoError(t, Parse(r, &v)) {
|
||||
assert.ElementsMatch(t, []string{}, v.Name)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice with empty", func(t *testing.T) {
|
||||
var v struct {
|
||||
Name []string `form:"name,optional"`
|
||||
}
|
||||
|
||||
r, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"/a?name=",
|
||||
http.NoBody)
|
||||
assert.NoError(t, err)
|
||||
if assert.NoError(t, Parse(r, &v)) {
|
||||
assert.ElementsMatch(t, []string{""}, v.Name)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice with empty and non-empty", func(t *testing.T) {
|
||||
var v struct {
|
||||
Name []string `form:"name"`
|
||||
}
|
||||
|
||||
r, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"/a?name=&name=1",
|
||||
http.NoBody)
|
||||
assert.NoError(t, err)
|
||||
if assert.NoError(t, Parse(r, &v)) {
|
||||
assert.ElementsMatch(t, []string{"", "1"}, v.Name)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice with one value on array format", func(t *testing.T) {
|
||||
var v struct {
|
||||
Names []string `form:"names"`
|
||||
}
|
||||
|
||||
r, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"/a?names=1,2,3",
|
||||
http.NoBody)
|
||||
assert.NoError(t, err)
|
||||
if assert.NoError(t, Parse(r, &v)) {
|
||||
assert.ElementsMatch(t, []string{"1", "2", "3"}, v.Names)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice with one value on combined array format", func(t *testing.T) {
|
||||
var v struct {
|
||||
Names []string `form:"names"`
|
||||
}
|
||||
|
||||
r, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"/a?names=[1,2,3]&names=4",
|
||||
http.NoBody)
|
||||
assert.NoError(t, err)
|
||||
if assert.NoError(t, Parse(r, &v)) {
|
||||
assert.ElementsMatch(t, []string{"[1,2,3]", "4"}, v.Names)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice with one value on integer array format", func(t *testing.T) {
|
||||
var v struct {
|
||||
Numbers []int `form:"numbers"`
|
||||
}
|
||||
|
||||
r, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"/a?numbers=1,2,3",
|
||||
http.NoBody)
|
||||
assert.NoError(t, err)
|
||||
if assert.NoError(t, Parse(r, &v)) {
|
||||
assert.ElementsMatch(t, []int{1, 2, 3}, v.Numbers)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice with one value on array format brackets", func(t *testing.T) {
|
||||
var v struct {
|
||||
Names []string `form:"names"`
|
||||
}
|
||||
|
||||
r, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"/a?names[]=1&names[]=2&names[]=3",
|
||||
http.NoBody)
|
||||
assert.NoError(t, err)
|
||||
if assert.NoError(t, Parse(r, &v)) {
|
||||
assert.ElementsMatch(t, []string{"1", "2", "3"}, v.Names)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseForm_Error(t *testing.T) {
|
||||
var v struct {
|
||||
Name string `form:"name"`
|
||||
@@ -270,11 +415,31 @@ func TestParseJsonBody(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
|
||||
r.Header.Set(ContentType, header.JsonContentType)
|
||||
|
||||
assert.NoError(t, ParseJsonBody(r, &v))
|
||||
assert.NoError(t, Parse(r, &v))
|
||||
assert.Equal(t, 1, len(v))
|
||||
assert.Equal(t, "kevin", v[0].Name)
|
||||
assert.Equal(t, 18, v[0].Age)
|
||||
})
|
||||
|
||||
t.Run("form and array body", func(t *testing.T) {
|
||||
var v []struct {
|
||||
// we can only ignore the form tag,
|
||||
// because if the value is a slice, it should be in the body,
|
||||
// but it's hard to detect when we treat it as a json body.
|
||||
Product string `form:"product"`
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
}
|
||||
|
||||
body := `[{"name":"apple", "age": 18}]`
|
||||
r := httptest.NewRequest(http.MethodPost, "/a?product=tree", strings.NewReader(body))
|
||||
r.Header.Set(ContentType, header.JsonContentType)
|
||||
|
||||
assert.NoError(t, Parse(r, &v))
|
||||
assert.Equal(t, 1, len(v))
|
||||
assert.Equal(t, "apple", v[0].Name)
|
||||
assert.Equal(t, 18, v[0].Age)
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseRequired(t *testing.T) {
|
||||
@@ -453,6 +618,26 @@ func TestCustomUnmarshalerStructRequest(t *testing.T) {
|
||||
assert.Equal(t, "hello", v.Foo.Name)
|
||||
}
|
||||
|
||||
func TestParseJsonStringRequest(t *testing.T) {
|
||||
type GoodsInfo struct {
|
||||
Sku int64 `json:"sku,optional"`
|
||||
}
|
||||
|
||||
type GetReq struct {
|
||||
GoodsList []*GoodsInfo `json:"goods_list"`
|
||||
}
|
||||
|
||||
input := `{"goods_list":"[{\"sku\":11},{\"sku\":22}]"}`
|
||||
r := httptest.NewRequest(http.MethodPost, "/a", strings.NewReader(input))
|
||||
r.Header.Set(ContentType, JsonContentType)
|
||||
var v GetReq
|
||||
assert.NotPanics(t, func() {
|
||||
assert.NoError(t, Parse(r, &v))
|
||||
assert.Equal(t, 2, len(v.GoodsList))
|
||||
assert.ElementsMatch(t, []int64{11, 22}, []int64{v.GoodsList[0].Sku, v.GoodsList[1].Sku})
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkParseRaw(b *testing.B) {
|
||||
r, err := http.NewRequest(http.MethodGet, "http://hello.com/a?name=hello&age=18&percent=3.4", http.NoBody)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,12 +2,23 @@ package httpx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const xForwardedFor = "X-Forwarded-For"
|
||||
const (
|
||||
xForwardedFor = "X-Forwarded-For"
|
||||
arraySuffix = "[]"
|
||||
// most servers and clients have a limit of 8192 bytes (8 KB)
|
||||
// one parameter at least take 4 chars, for example `?a=b&c=d`
|
||||
maxFormParamCount = 2048
|
||||
)
|
||||
|
||||
// GetFormValues returns the form values.
|
||||
// GetFormValues returns the form values supporting three array notation formats:
|
||||
// 1. Standard notation: /api?names=alice&names=bob
|
||||
// 2. Comma notation: /api?names=alice,bob
|
||||
// 3. Bracket notation: /api?names[]=alice&names[]=bob
|
||||
func GetFormValues(r *http.Request) (map[string]any, error) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return nil, err
|
||||
@@ -19,11 +30,24 @@ func GetFormValues(r *http.Request) (map[string]any, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var n int
|
||||
params := make(map[string]any, len(r.Form))
|
||||
for name := range r.Form {
|
||||
formValue := r.Form.Get(name)
|
||||
if len(formValue) > 0 {
|
||||
params[name] = formValue
|
||||
for name, values := range r.Form {
|
||||
filtered := make([]string, 0, len(values))
|
||||
for _, v := range values {
|
||||
if n < maxFormParamCount {
|
||||
filtered = append(filtered, v)
|
||||
n++
|
||||
} else {
|
||||
return nil, fmt.Errorf("too many form values, error: %s", r.Form.Encode())
|
||||
}
|
||||
}
|
||||
|
||||
if len(filtered) > 0 {
|
||||
if strings.HasSuffix(name, arraySuffix) {
|
||||
name = name[:len(name)-2]
|
||||
}
|
||||
params[name] = filtered
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package httpx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -23,3 +25,23 @@ func TestGetRemoteAddrNoHeader(t *testing.T) {
|
||||
|
||||
assert.True(t, len(GetRemoteAddr(r)) == 0)
|
||||
}
|
||||
|
||||
func TestGetFormValues_TooManyValues(t *testing.T) {
|
||||
form := url.Values{}
|
||||
|
||||
// Add more values than the limit
|
||||
for i := 0; i < maxFormParamCount+10; i++ {
|
||||
form.Add("param", fmt.Sprintf("value%d", i))
|
||||
}
|
||||
|
||||
// Create a new request with the form data
|
||||
req, err := http.NewRequest("POST", "/test", strings.NewReader(form.Encode()))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Set the content type for form data
|
||||
req.Header.Set(ContentType, "application/x-www-form-urlencoded")
|
||||
|
||||
_, err = GetFormValues(req)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "too many form values")
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ func TestPatRouter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseSlice(t *testing.T) {
|
||||
body := `names=%5B%22first%22%2C%22second%22%5D`
|
||||
body := `names=first&names=second`
|
||||
reader := strings.NewReader(body)
|
||||
r, err := http.NewRequest(http.MethodPost, "http://hello.com/", reader)
|
||||
assert.Nil(t, err)
|
||||
@@ -388,7 +388,7 @@ func TestParseQueryRequired(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseOptional(t *testing.T) {
|
||||
r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=", nil)
|
||||
r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever", nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
router := NewRouter()
|
||||
@@ -516,28 +516,55 @@ func TestParsePtrInRequestEmpty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseQueryOptional(t *testing.T) {
|
||||
r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=", nil)
|
||||
assert.Nil(t, err)
|
||||
t.Run("optional with string", func(t *testing.T) {
|
||||
r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=", nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
router := NewRouter()
|
||||
err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
v := struct {
|
||||
Nickname string `form:"nickname"`
|
||||
Zipcode int64 `form:"zipcode,optional"`
|
||||
}{}
|
||||
router := NewRouter()
|
||||
err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
v := struct {
|
||||
Nickname string `form:"nickname"`
|
||||
Zipcode string `form:"zipcode,optional"`
|
||||
}{}
|
||||
|
||||
err = httpx.Parse(r, &v)
|
||||
assert.Nil(t, err)
|
||||
_, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Nickname, v.Zipcode))
|
||||
assert.Nil(t, err)
|
||||
}))
|
||||
assert.Nil(t, err)
|
||||
err = httpx.Parse(r, &v)
|
||||
assert.Nil(t, err)
|
||||
_, err = io.WriteString(w, fmt.Sprintf("%s:%s", v.Nickname, v.Zipcode))
|
||||
assert.Nil(t, err)
|
||||
}))
|
||||
assert.Nil(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
router.ServeHTTP(rr, r)
|
||||
rr := httptest.NewRecorder()
|
||||
router.ServeHTTP(rr, r)
|
||||
|
||||
assert.Equal(t, "whatever:0", rr.Body.String())
|
||||
assert.Equal(t, "whatever:", rr.Body.String())
|
||||
})
|
||||
|
||||
t.Run("optional with int", func(t *testing.T) {
|
||||
r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever", nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
router := NewRouter()
|
||||
err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
v := struct {
|
||||
Nickname string `form:"nickname"`
|
||||
Zipcode int `form:"zipcode,optional"`
|
||||
}{}
|
||||
|
||||
err = httpx.Parse(r, &v)
|
||||
assert.Nil(t, err)
|
||||
_, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Nickname, v.Zipcode))
|
||||
assert.Nil(t, err)
|
||||
}))
|
||||
assert.Nil(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
router.ServeHTTP(rr, r)
|
||||
|
||||
assert.Equal(t, "whatever:0", rr.Body.String())
|
||||
})
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
|
||||
@@ -2,8 +2,8 @@ FROM golang:alpine AS builder
|
||||
|
||||
LABEL stage=gobuilder
|
||||
|
||||
ENV CGO_ENABLED 0
|
||||
ENV GOPROXY https://goproxy.cn,direct
|
||||
ENV CGO_ENABLED=0
|
||||
ENV GOPROXY=https://goproxy.cn,direct
|
||||
|
||||
RUN apk update --no-cache && apk add --no-cache tzdata
|
||||
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
@@ -24,7 +24,7 @@ RUN apk update --no-cache && apk add --no-cache protoc
|
||||
COPY --from=builder /etc/passwd /etc/group /etc/
|
||||
COPY --from=builder /usr/share/zoneinfo/ /usr/share/zoneinfo/
|
||||
COPY --from=builder --chown=1000:1000 /go/bin/protoc-gen-go* /app/goctl /usr/local/bin/
|
||||
ENV TZ Asia/Shanghai
|
||||
ENV TZ=Asia/Shanghai
|
||||
|
||||
WORKDIR /app
|
||||
USER app
|
||||
|
||||
@@ -139,12 +139,7 @@ rest.WithPrefix("%s"),`, g.prefix)
|
||||
return err
|
||||
}
|
||||
|
||||
// why we check this, maybe some users set value 1, it's 1ns, not 1s.
|
||||
if duration < timeoutThreshold {
|
||||
return fmt.Errorf("timeout should not less than 1ms, now %v", duration)
|
||||
}
|
||||
|
||||
timeout = fmt.Sprintf("\n rest.WithTimeout(%d * time.Millisecond),", duration.Milliseconds())
|
||||
timeout = fmt.Sprintf("\n rest.WithTimeout(%s),", formatDuration(duration))
|
||||
hasTimeout = true
|
||||
}
|
||||
|
||||
@@ -211,6 +206,16 @@ rest.WithPrefix("%s"),`, g.prefix)
|
||||
})
|
||||
}
|
||||
|
||||
func formatDuration(duration time.Duration) string {
|
||||
if duration < time.Microsecond {
|
||||
return fmt.Sprintf("%d * time.Nanosecond", duration.Nanoseconds())
|
||||
}
|
||||
if duration < time.Millisecond {
|
||||
return fmt.Sprintf("%d * time.Microsecond", duration.Microseconds())
|
||||
}
|
||||
return fmt.Sprintf("%d * time.Millisecond", duration.Milliseconds())
|
||||
}
|
||||
|
||||
func genRouteImports(parentPkg string, api *spec.ApiSpec) string {
|
||||
importSet := collection.NewSet()
|
||||
importSet.AddStr(fmt.Sprintf("\"%s\"", pathx.JoinPackages(parentPkg, contextDir)))
|
||||
|
||||
27
tools/goctl/api/gogen/genroutes_test.go
Normal file
27
tools/goctl/api/gogen/genroutes_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package gogen
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_formatDuration(t *testing.T) {
|
||||
tests := []struct {
|
||||
duration time.Duration
|
||||
expected string
|
||||
}{
|
||||
{0, "0 * time.Nanosecond"},
|
||||
{time.Nanosecond, "1 * time.Nanosecond"},
|
||||
{100 * time.Nanosecond, "100 * time.Nanosecond"},
|
||||
{500 * time.Microsecond, "500 * time.Microsecond"},
|
||||
{2 * time.Millisecond, "2 * time.Millisecond"},
|
||||
{time.Second, "1000 * time.Millisecond"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result := formatDuration(test.duration)
|
||||
if result != test.expected {
|
||||
t.Errorf("formatDuration(%v) = %v; want %v", test.duration, result, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export function parseParams(url: string): Array<string> {
|
||||
* @param url
|
||||
* @param params
|
||||
*/
|
||||
export function genUrl(url: string, params: unknown) {
|
||||
export function genUrl(url: string, params: any) {
|
||||
if (!params) {
|
||||
return url;
|
||||
}
|
||||
@@ -113,7 +113,7 @@ export const webapi = {
|
||||
return api<T>('delete', url, req, config);
|
||||
},
|
||||
put<T>(url: string, req: unknown, config?: unknown): Promise<T> {
|
||||
return api<T>('get', url, req, config);
|
||||
return api<T>('put', url, req, config);
|
||||
},
|
||||
post<T>(url: string, req: unknown, config?: unknown): Promise<T> {
|
||||
return api<T>('post', url, req, config);
|
||||
|
||||
@@ -6,4 +6,4 @@ import (
|
||||
)
|
||||
|
||||
// Cmd describes a bug command.
|
||||
var Cmd = cobrax.NewCommand("bug", cobrax.WithRunE(cobra.NoArgs), cobrax.WithArgs(cobra.NoArgs))
|
||||
var Cmd = cobrax.NewCommand("bug", cobrax.WithRunE(runE), cobrax.WithArgs(cobra.NoArgs))
|
||||
|
||||
@@ -4,29 +4,34 @@ LABEL stage=gobuilder
|
||||
|
||||
ENV CGO_ENABLED 0
|
||||
{{if .Chinese}}ENV GOPROXY https://goproxy.cn,direct
|
||||
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||
{{end}}{{if .HasTimezone}}
|
||||
{{- end}}{{if .HasTimezone}}
|
||||
RUN apk update --no-cache && apk add --no-cache tzdata
|
||||
{{end}}
|
||||
{{- end}}
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
ADD go.mod .
|
||||
ADD go.sum .
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
{{if .Argument}}COPY {{.GoRelPath}}/etc /app/etc
|
||||
{{end}}RUN go build -ldflags="-s -w" -o /app/{{.ExeFile}} {{.GoMainFrom}}
|
||||
|
||||
RUN go build -ldflags="-s -w" -o /app/{{.ExeFile}} {{.GoMainFrom}}
|
||||
|
||||
|
||||
FROM {{.BaseImage}}
|
||||
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
{{if .HasTimezone}}COPY --from=builder /usr/share/zoneinfo/{{.Timezone}} /usr/share/zoneinfo/{{.Timezone}}
|
||||
{{if .HasTimezone -}}
|
||||
COPY --from=builder /usr/share/zoneinfo/{{.Timezone}} /usr/share/zoneinfo/{{.Timezone}}
|
||||
ENV TZ {{.Timezone}}
|
||||
{{end}}
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/{{.ExeFile}} /app/{{.ExeFile}}{{if .Argument}}
|
||||
COPY --from=builder /app/etc /app/etc{{end}}
|
||||
COPY --from=builder /app/{{.ExeFile}} /app/{{.ExeFile}}
|
||||
{{if .Argument -}}
|
||||
COPY {{.GoRelPath}}/etc /app/etc
|
||||
{{- end}}
|
||||
{{if .HasPort}}
|
||||
EXPOSE {{.Port}}
|
||||
{{end}}
|
||||
|
||||
@@ -4,28 +4,28 @@ go 1.20
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
github.com/emicklei/proto v1.13.2
|
||||
github.com/emicklei/proto v1.14.0
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1
|
||||
github.com/zeromicro/antlr v0.0.1
|
||||
github.com/zeromicro/ddl-parser v1.0.5
|
||||
github.com/zeromicro/go-zero v1.7.1
|
||||
golang.org/x/text v0.17.0
|
||||
github.com/zeromicro/go-zero v1.7.5
|
||||
golang.org/x/text v0.21.0
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
google.golang.org/protobuf v1.36.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
|
||||
github.com/alicebob/miniredis/v2 v2.33.0 // indirect
|
||||
github.com/alicebob/miniredis/v2 v2.34.0 // indirect
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
@@ -35,7 +35,7 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
@@ -67,11 +67,11 @@ require (
|
||||
github.com/openzipkin/zipkin-go v0.4.3 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.20.2 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.6.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.7.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||
github.com/yuin/gopher-lua v1.1.1 // indirect
|
||||
@@ -90,16 +90,16 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/term v0.27.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
|
||||
@@ -4,8 +4,8 @@ github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7Oputl
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA=
|
||||
github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0=
|
||||
github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
|
||||
github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec h1:EEyRvzmpEUZ+I8WmD5cw/vY8EqhambkOqy5iFr0908A=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
@@ -30,10 +30,10 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY=
|
||||
github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/emicklei/proto v1.14.0 h1:WYxC0OrBuuC+FUCTZvb8+fzEHdZMwLEF+OnVfZA3LXU=
|
||||
github.com/emicklei/proto v1.14.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -129,16 +129,16 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
|
||||
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
|
||||
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
|
||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
@@ -158,8 +158,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 h1:+dBg5k7nuTE38VVdoroRsT0Z88fmvdYrI2EjzJst35I=
|
||||
github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1/go.mod h1:nmuySobZb4kFgFy6BptpXp/BBw+xFSyvVPP6auoJB4k=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||
@@ -173,8 +174,8 @@ github.com/zeromicro/antlr v0.0.1 h1:CQpIn/dc0pUjgGQ81y98s/NGOm2Hfru2NNio2I9mQgk
|
||||
github.com/zeromicro/antlr v0.0.1/go.mod h1:nfpjEwFR6Q4xGDJMcZnCL9tEfQRgszMwu3rDz2Z+p5M=
|
||||
github.com/zeromicro/ddl-parser v1.0.5 h1:LaVqHdzMTjasua1yYpIYaksxKqRzFrEukj2Wi2EbWaQ=
|
||||
github.com/zeromicro/ddl-parser v1.0.5/go.mod h1:ISU/8NuPyEpl9pa17Py9TBPetMjtsiHrb9f5XGiYbo8=
|
||||
github.com/zeromicro/go-zero v1.7.1 h1:2JNWRg/C91SPvY6tugSkBo2IrsfCfnnNjqcVFj9tCtA=
|
||||
github.com/zeromicro/go-zero v1.7.1/go.mod h1:WFXfF92Exw0O7WECifS6r99JSzv4KEN49x9RhAfgkMc=
|
||||
github.com/zeromicro/go-zero v1.7.5 h1:B7Z2WszPQXHRhZTFbNQEt5Did2i/1jKTk8qNVRVQyY8=
|
||||
github.com/zeromicro/go-zero v1.7.5/go.mod h1:SmGykRm5e0Z4CGNj+GaSKDffaHzQV56fel0FkymTLlE=
|
||||
go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk=
|
||||
go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=
|
||||
@@ -205,8 +206,8 @@ go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeX
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
@@ -215,8 +216,8 @@ go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -225,16 +226,16 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -243,17 +244,17 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
@@ -270,8 +271,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d h1:
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
// BuildVersion is the version of goctl.
|
||||
const BuildVersion = "1.7.1"
|
||||
const BuildVersion = "1.7.4"
|
||||
|
||||
var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ spec:
|
||||
|
||||
---
|
||||
|
||||
apiVersion: autoscaling/v2beta2
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{.Name}}-hpa-c
|
||||
|
||||
@@ -176,11 +176,7 @@ func (a *Analyzer) convertKV(kv []*ast.KVExpr) map[string]string {
|
||||
var ret = map[string]string{}
|
||||
for _, v := range kv {
|
||||
key := strings.TrimSuffix(v.Key.Token.Text, ":")
|
||||
if key == summaryKeyText {
|
||||
ret[key] = v.Value.RawText()
|
||||
} else {
|
||||
ret[key] = v.Value.Token.Text
|
||||
}
|
||||
ret[key] = v.Value.RawText()
|
||||
}
|
||||
|
||||
return ret
|
||||
|
||||
@@ -13,15 +13,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
idAPI = "api"
|
||||
summaryKeyExprText = "summary:"
|
||||
summaryKeyText = "summary"
|
||||
groupKeyText = "group"
|
||||
infoTitleKey = "Title"
|
||||
infoDescKey = "Desc"
|
||||
infoVersionKey = "Version"
|
||||
infoAuthorKey = "Author"
|
||||
infoEmailKey = "Email"
|
||||
idAPI = "api"
|
||||
groupKeyText = "group"
|
||||
infoTitleKey = "Title"
|
||||
infoDescKey = "Desc"
|
||||
infoVersionKey = "Version"
|
||||
infoAuthorKey = "Author"
|
||||
infoEmailKey = "Email"
|
||||
)
|
||||
|
||||
// Parser is the parser for api file.
|
||||
@@ -1201,12 +1199,6 @@ func (p *Parser) parseAtServerKVExpression() *ast.KVExpr {
|
||||
expr.Value = node
|
||||
return expr
|
||||
} else if p.peekTokenIs(token.STRING) {
|
||||
if expr.Key.Token.Text != summaryKeyExprText {
|
||||
if p.notExpectPeekToken(token.QUO, token.DURATION, token.IDENT, token.INT) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if !p.nextToken() {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -305,6 +305,7 @@ func TestParser_Parse_atServerStmt(t *testing.T) {
|
||||
"prefix3:": "v1/v2_",
|
||||
"prefix4:": "a-b-c",
|
||||
"summary:": `"test"`,
|
||||
"key:": `"bar"`,
|
||||
}
|
||||
|
||||
p := New("foo.api", atServerTestAPI)
|
||||
|
||||
@@ -19,4 +19,5 @@
|
||||
prefix3: v1/v2_
|
||||
prefix4: a-b-c
|
||||
summary:"test"
|
||||
key:"bar"
|
||||
)
|
||||
|
||||
@@ -129,6 +129,7 @@ service test {
|
||||
@server (
|
||||
jwt: Auth
|
||||
group: Group1
|
||||
foo:"bar"
|
||||
)
|
||||
service test {
|
||||
@doc "ping"
|
||||
|
||||
@@ -29,8 +29,6 @@ const (
|
||||
// string mode end
|
||||
)
|
||||
|
||||
var missingInput = errors.New("missing input")
|
||||
|
||||
type mode int
|
||||
|
||||
// Scanner is a lexical scanner.
|
||||
@@ -629,7 +627,7 @@ func NewScanner(filename string, src interface{}) (*Scanner, error) {
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
return nil, missingInput
|
||||
return nil, fmt.Errorf("filename: %s,missing input", filename)
|
||||
}
|
||||
|
||||
var runeList []rune
|
||||
|
||||
@@ -62,13 +62,13 @@ func TestNewScanner(t *testing.T) {
|
||||
{
|
||||
filename: "foo",
|
||||
src: "",
|
||||
expected: missingInput,
|
||||
expected: "missing input",
|
||||
},
|
||||
}
|
||||
for _, v := range testData {
|
||||
s, err := NewScanner(v.filename, v.src)
|
||||
if err != nil {
|
||||
assert.Equal(t, v.expected.(error).Error(), err.Error())
|
||||
assert.Contains(t, err.Error(), v.expected)
|
||||
} else {
|
||||
assert.Equal(t, v.expected, s.filename)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
|
||||
)
|
||||
|
||||
// GenPb generates the pb.go file, which is a layer of packaging for protoc to generate gprc,
|
||||
@@ -88,7 +89,7 @@ func findPbFile(current string, src string, grpc bool) (string, error) {
|
||||
return nil
|
||||
})
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return filepath.Dir(filepath.Join(current, ret)), nil
|
||||
return pathx.ReadLink(filepath.Dir(filepath.Join(current, ret)))
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -82,13 +82,15 @@ func getRealModule(workDir string, execRun execx.RunFunc) (*Module, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if workDir[len(workDir)-1] != os.PathSeparator {
|
||||
workDir = workDir + string(os.PathSeparator)
|
||||
}
|
||||
for _, m := range modules {
|
||||
realDir, err := pathx.ReadLink(m.Dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read go.mod, dir: %s, error: %w", m.Dir, err)
|
||||
}
|
||||
|
||||
realDir += string(os.PathSeparator)
|
||||
if strings.HasPrefix(workDir, realDir) {
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
@@ -98,6 +98,60 @@ func Test_getRealModule(t *testing.T) {
|
||||
GoVersion: "go1.20",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "go work duplicate prefix",
|
||||
args: args{
|
||||
workDir: "D:\\code\\company\\core-ee\\service",
|
||||
execRun: func(arg, dir string, in ...*bytes.Buffer) (string, error) {
|
||||
return `
|
||||
{
|
||||
"Path": "gitee.com/unitedrhino/core",
|
||||
"Dir": "D:\\code\\company\\core",
|
||||
"GoMod": "D:\\code\\company\\core\\go.mod",
|
||||
"GoVersion": "1.21.4"
|
||||
}
|
||||
{
|
||||
"Path": "gitee.com/unitedrhino/core-ee",
|
||||
"Dir": "D:\\code\\company\\core-ee",
|
||||
"GoMod": "D:\\code\\company\\core-ee\\go.mod",
|
||||
"GoVersion": "1.21.4"
|
||||
}`, nil
|
||||
},
|
||||
},
|
||||
want: &Module{
|
||||
Path: "gitee.com/unitedrhino/core-ee",
|
||||
Dir: "D:\\code\\company\\core-ee",
|
||||
GoMod: "D:\\code\\company\\core-ee\\go.mod",
|
||||
GoVersion: "1.21.4",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "go work duplicate prefix2",
|
||||
args: args{
|
||||
workDir: "D:\\code\\company\\core-ee",
|
||||
execRun: func(arg, dir string, in ...*bytes.Buffer) (string, error) {
|
||||
return `
|
||||
{
|
||||
"Path": "gitee.com/unitedrhino/core",
|
||||
"Dir": "D:\\code\\company\\core",
|
||||
"GoMod": "D:\\code\\company\\core\\go.mod",
|
||||
"GoVersion": "1.21.4"
|
||||
}
|
||||
{
|
||||
"Path": "gitee.com/unitedrhino/core-ee",
|
||||
"Dir": "D:\\code\\company\\core-ee",
|
||||
"GoMod": "D:\\code\\company\\core-ee\\go.mod",
|
||||
"GoVersion": "1.21.4"
|
||||
}`, nil
|
||||
},
|
||||
},
|
||||
want: &Module{
|
||||
Path: "gitee.com/unitedrhino/core-ee",
|
||||
Dir: "D:\\code\\company\\core-ee",
|
||||
GoMod: "D:\\code\\company\\core-ee\\go.mod",
|
||||
GoVersion: "1.21.4",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user