mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-25 07:38:18 +08:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -18,6 +18,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
comma = ","
|
||||||
defaultKeyName = "key"
|
defaultKeyName = "key"
|
||||||
delimiter = '.'
|
delimiter = '.'
|
||||||
ignoreKey = "-"
|
ignoreKey = "-"
|
||||||
@@ -36,6 +37,7 @@ var (
|
|||||||
defaultCacheLock sync.Mutex
|
defaultCacheLock sync.Mutex
|
||||||
emptyMap = map[string]any{}
|
emptyMap = map[string]any{}
|
||||||
emptyValue = reflect.ValueOf(lang.Placeholder)
|
emptyValue = reflect.ValueOf(lang.Placeholder)
|
||||||
|
stringSliceType = reflect.TypeOf([]string{})
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -80,40 +82,11 @@ func (u *Unmarshaler) Unmarshal(i, v any) error {
|
|||||||
return u.unmarshal(i, v, "")
|
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.
|
// UnmarshalValuer unmarshals m into v.
|
||||||
func (u *Unmarshaler) UnmarshalValuer(m Valuer, v any) error {
|
func (u *Unmarshaler) UnmarshalValuer(m Valuer, v any) error {
|
||||||
return u.unmarshalValuer(simpleValuer{current: m}, v, "")
|
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,
|
func (u *Unmarshaler) fillMap(fieldType reflect.Type, value reflect.Value,
|
||||||
mapValue any, fullName string) error {
|
mapValue any, fullName string) error {
|
||||||
if !value.CanSet() {
|
if !value.CanSet() {
|
||||||
@@ -173,13 +146,18 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value,
|
|||||||
baseType := fieldType.Elem()
|
baseType := fieldType.Elem()
|
||||||
dereffedBaseType := Deref(baseType)
|
dereffedBaseType := Deref(baseType)
|
||||||
dereffedBaseKind := dereffedBaseType.Kind()
|
dereffedBaseKind := dereffedBaseType.Kind()
|
||||||
conv := reflect.MakeSlice(reflect.SliceOf(baseType), refValue.Len(), refValue.Cap())
|
|
||||||
if refValue.Len() == 0 {
|
if refValue.Len() == 0 {
|
||||||
value.Set(conv)
|
value.Set(reflect.MakeSlice(reflect.SliceOf(baseType), 0, 0))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if u.opts.fromArray {
|
||||||
|
refValue = makeStringSlice(refValue)
|
||||||
|
}
|
||||||
|
|
||||||
var valid bool
|
var valid bool
|
||||||
|
conv := reflect.MakeSlice(reflect.SliceOf(baseType), refValue.Len(), refValue.Cap())
|
||||||
|
|
||||||
for i := 0; i < refValue.Len(); i++ {
|
for i := 0; i < refValue.Len(); i++ {
|
||||||
ithValue := refValue.Index(i).Interface()
|
ithValue := refValue.Index(i).Interface()
|
||||||
if ithValue == nil {
|
if ithValue == nil {
|
||||||
@@ -191,17 +169,9 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value,
|
|||||||
|
|
||||||
switch dereffedBaseKind {
|
switch dereffedBaseKind {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
target := reflect.New(dereffedBaseType)
|
if err := u.fillStructElement(baseType, conv.Index(i), ithValue, sliceFullName); err != nil {
|
||||||
val, ok := ithValue.(map[string]any)
|
|
||||||
if !ok {
|
|
||||||
return errTypeMismatch
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := u.unmarshal(val, target.Interface(), sliceFullName); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
SetValue(fieldType.Elem(), conv.Index(i), target.Elem())
|
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
if err := u.fillSlice(dereffedBaseType, conv.Index(i), ithValue, sliceFullName); err != nil {
|
if err := u.fillSlice(dereffedBaseType, conv.Index(i), ithValue, sliceFullName); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -236,7 +206,7 @@ func (u *Unmarshaler) fillSliceFromString(fieldType reflect.Type, value reflect.
|
|||||||
return errUnsupportedType
|
return errUnsupportedType
|
||||||
}
|
}
|
||||||
|
|
||||||
baseFieldType := Deref(fieldType.Elem())
|
baseFieldType := fieldType.Elem()
|
||||||
baseFieldKind := baseFieldType.Kind()
|
baseFieldKind := baseFieldType.Kind()
|
||||||
conv := reflect.MakeSlice(reflect.SliceOf(baseFieldType), len(slice), cap(slice))
|
conv := reflect.MakeSlice(reflect.SliceOf(baseFieldType), len(slice), cap(slice))
|
||||||
|
|
||||||
@@ -257,29 +227,39 @@ func (u *Unmarshaler) fillSliceValue(slice reflect.Value, index int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ithVal := slice.Index(index)
|
ithVal := slice.Index(index)
|
||||||
|
ithValType := ithVal.Type()
|
||||||
|
|
||||||
switch v := value.(type) {
|
switch v := value.(type) {
|
||||||
case fmt.Stringer:
|
case fmt.Stringer:
|
||||||
return setValueFromString(baseKind, ithVal, v.String())
|
return setValueFromString(baseKind, ithVal, v.String())
|
||||||
case string:
|
case string:
|
||||||
return setValueFromString(baseKind, ithVal, v)
|
return setValueFromString(baseKind, ithVal, v)
|
||||||
case map[string]any:
|
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:
|
default:
|
||||||
// don't need to consider the difference between int, int8, int16, int32, int64,
|
// 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.
|
// uint, uint8, uint16, uint32, uint64, because they're handled as json.Number.
|
||||||
if ithVal.Kind() == reflect.Ptr {
|
if ithVal.Kind() == reflect.Ptr {
|
||||||
baseType := Deref(ithVal.Type())
|
baseType := Deref(ithValType)
|
||||||
if !reflect.TypeOf(value).AssignableTo(baseType) {
|
if !reflect.TypeOf(value).AssignableTo(baseType) {
|
||||||
return errTypeMismatch
|
return errTypeMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
target := reflect.New(baseType).Elem()
|
target := reflect.New(baseType).Elem()
|
||||||
target.Set(reflect.ValueOf(value))
|
target.Set(reflect.ValueOf(value))
|
||||||
SetValue(ithVal.Type(), ithVal, target)
|
SetValue(ithValType, ithVal, target)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.TypeOf(value).AssignableTo(ithVal.Type()) {
|
if !reflect.TypeOf(value).AssignableTo(ithValType) {
|
||||||
return errTypeMismatch
|
return errTypeMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,6 +290,23 @@ func (u *Unmarshaler) fillSliceWithDefault(derefedType reflect.Type, value refle
|
|||||||
return u.fillSlice(derefedType, value, slice, fullName)
|
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,
|
func (u *Unmarshaler) fillUnmarshalerStruct(fieldType reflect.Type,
|
||||||
value reflect.Value, targetValue string) error {
|
value reflect.Value, targetValue string) error {
|
||||||
if !value.CanSet() {
|
if !value.CanSet() {
|
||||||
@@ -952,6 +949,35 @@ func (u *Unmarshaler) processNamedFieldWithoutValue(fieldType reflect.Type, valu
|
|||||||
return nil
|
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 {
|
func (u *Unmarshaler) unmarshalWithFullName(m valuerWithParent, v any, fullName string) error {
|
||||||
rv := reflect.ValueOf(v)
|
rv := reflect.ValueOf(v)
|
||||||
if err := ValidatePtr(rv); err != nil {
|
if err := ValidatePtr(rv); err != nil {
|
||||||
@@ -1146,6 +1172,35 @@ func join(elem ...string) string {
|
|||||||
return builder.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 {
|
func newInitError(name string) error {
|
||||||
return fmt.Errorf("field %q is not set", name)
|
return fmt.Errorf("field %q is not set", name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ func TestUnmarshalIntSliceOfPtr(t *testing.T) {
|
|||||||
assert.Error(t, UnmarshalKey(m, &in))
|
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 {
|
type inner struct {
|
||||||
Ints []int `key:"ints"`
|
Ints []int `key:"ints"`
|
||||||
}
|
}
|
||||||
@@ -365,6 +365,21 @@ func TestUnmarshalIntSliceOfPtr(t *testing.T) {
|
|||||||
assert.Empty(t, in.Ints)
|
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) {
|
func TestUnmarshalIntWithDefault(t *testing.T) {
|
||||||
@@ -1374,20 +1389,82 @@ func TestUnmarshalWithFloatPtr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalIntSlice(t *testing.T) {
|
func TestUnmarshalIntSlice(t *testing.T) {
|
||||||
var v struct {
|
t.Run("int slice from int", func(t *testing.T) {
|
||||||
Ages []int `key:"ages"`
|
var v struct {
|
||||||
Slice []int `key:"slice"`
|
Ages []int `key:"ages"`
|
||||||
}
|
Slice []int `key:"slice"`
|
||||||
m := map[string]any{
|
}
|
||||||
"ages": []int{1, 2},
|
m := map[string]any{
|
||||||
"slice": []any{},
|
"ages": []int{1, 2},
|
||||||
}
|
"slice": []any{},
|
||||||
|
}
|
||||||
|
|
||||||
ast := assert.New(t)
|
ast := assert.New(t)
|
||||||
if ast.NoError(UnmarshalKey(m, &v)) {
|
if ast.NoError(UnmarshalKey(m, &v)) {
|
||||||
ast.ElementsMatch([]int{1, 2}, v.Ages)
|
ast.ElementsMatch([]int{1, 2}, v.Ages)
|
||||||
ast.Equal([]int{}, v.Slice)
|
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) {
|
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) {
|
t.Run("slice from string with slice error", func(t *testing.T) {
|
||||||
var v struct {
|
var v struct {
|
||||||
Names []int `key:"names"`
|
Names []int `key:"names"`
|
||||||
@@ -5862,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) {
|
func BenchmarkDefaultValue(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
var a struct {
|
var a struct {
|
||||||
|
|||||||
@@ -14,17 +14,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
wrapUpTime = time.Second
|
// defaultWrapUpTime is the default time to wait before calling wrap up listeners.
|
||||||
// why we use 5500 milliseconds is because most of our queue are blocking mode with 5 seconds
|
defaultWrapUpTime = time.Second
|
||||||
waitTime = 5500 * time.Millisecond
|
// 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 (
|
var (
|
||||||
wrapUpListeners = new(listenerManager)
|
wrapUpListeners = new(listenerManager)
|
||||||
shutdownListeners = new(listenerManager)
|
shutdownListeners = new(listenerManager)
|
||||||
delayTimeBeforeForceQuit = waitTime
|
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.
|
// AddShutdownListener adds fn as a shutdown listener.
|
||||||
// The returned func can be used to wait for fn getting called.
|
// The returned func can be used to wait for fn getting called.
|
||||||
func AddShutdownListener(fn func()) (waitForCalled func()) {
|
func AddShutdownListener(fn func()) (waitForCalled func()) {
|
||||||
@@ -39,7 +51,21 @@ func AddWrapUpListener(fn func()) (waitForCalled func()) {
|
|||||||
|
|
||||||
// SetTimeToForceQuit sets the waiting time before force quitting.
|
// SetTimeToForceQuit sets the waiting time before force quitting.
|
||||||
func SetTimeToForceQuit(duration time.Duration) {
|
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.
|
// 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)
|
time.Sleep(wrapUpTime)
|
||||||
go shutdownListeners.notifyListeners()
|
go shutdownListeners.notifyListeners()
|
||||||
|
|
||||||
time.Sleep(delayTimeBeforeForceQuit - wrapUpTime)
|
shutdownLock.Lock()
|
||||||
logx.Infof("Still alive after %v, going to force kill the process...", delayTimeBeforeForceQuit)
|
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)
|
_ = syscall.Kill(syscall.Getpid(), sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +112,9 @@ func (lm *listenerManager) addListener(fn func()) (waitForCalled func()) {
|
|||||||
})
|
})
|
||||||
lm.lock.Unlock()
|
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() {
|
return func() {
|
||||||
lm.waitGroup.Wait()
|
lm.waitGroup.Wait()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -10,8 +11,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestShutdown(t *testing.T) {
|
func TestShutdown(t *testing.T) {
|
||||||
|
t.Cleanup(restoreSettings)
|
||||||
|
|
||||||
SetTimeToForceQuit(time.Hour)
|
SetTimeToForceQuit(time.Hour)
|
||||||
assert.Equal(t, time.Hour, delayTimeBeforeForceQuit)
|
shutdownLock.Lock()
|
||||||
|
assert.Equal(t, time.Hour, waitTime)
|
||||||
|
shutdownLock.Unlock()
|
||||||
|
|
||||||
var val int
|
var val int
|
||||||
called := AddWrapUpListener(func() {
|
called := AddWrapUpListener(func() {
|
||||||
@@ -29,7 +34,53 @@ func TestShutdown(t *testing.T) {
|
|||||||
assert.Equal(t, 3, val)
|
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) {
|
func TestNotifyMoreThanOnce(t *testing.T) {
|
||||||
|
t.Cleanup(restoreSettings)
|
||||||
|
|
||||||
ch := make(chan struct{}, 1)
|
ch := make(chan struct{}, 1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -58,3 +109,38 @@ func TestNotifyMoreThanOnce(t *testing.T) {
|
|||||||
t.Fatal("timeout, check error logs")
|
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"`
|
Prometheus prometheus.Config `json:",optional"`
|
||||||
Telemetry trace.Config `json:",optional"`
|
Telemetry trace.Config `json:",optional"`
|
||||||
DevServer DevServerConfig `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
|
sc.Telemetry.Name = sc.Name
|
||||||
}
|
}
|
||||||
trace.StartAgent(sc.Telemetry)
|
trace.StartAgent(sc.Telemetry)
|
||||||
|
proc.Setup(sc.Shutdown)
|
||||||
proc.AddShutdownListener(func() {
|
proc.AddShutdownListener(func() {
|
||||||
trace.StopAgent()
|
trace.StopAgent()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -76,9 +76,14 @@ func (sg *ServiceGroup) doStart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sg *ServiceGroup) doStop() {
|
func (sg *ServiceGroup) doStop() {
|
||||||
|
group := threading.NewRoutineGroup()
|
||||||
for _, service := range sg.services {
|
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.
|
// WithStart wraps a start func as a Service.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/jhump/protoreflect/grpcreflect"
|
"github.com/jhump/protoreflect/grpcreflect"
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
"github.com/zeromicro/go-zero/core/mr"
|
"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/gateway/internal"
|
||||||
"github.com/zeromicro/go-zero/rest"
|
"github.com/zeromicro/go-zero/rest"
|
||||||
"github.com/zeromicro/go-zero/rest/httpx"
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
@@ -23,6 +24,7 @@ type (
|
|||||||
Server struct {
|
Server struct {
|
||||||
*rest.Server
|
*rest.Server
|
||||||
upstreams []Upstream
|
upstreams []Upstream
|
||||||
|
conns []zrpc.Client
|
||||||
processHeader func(http.Header) []string
|
processHeader func(http.Header) []string
|
||||||
dialer func(conf zrpc.RpcClientConf) zrpc.Client
|
dialer func(conf zrpc.RpcClientConf) zrpc.Client
|
||||||
}
|
}
|
||||||
@@ -51,8 +53,24 @@ func (s *Server) Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the gateway server.
|
// Stop stops the gateway server.
|
||||||
|
// To get a graceful shutdown, it stops the HTTP server first, then closes gRPC connections.
|
||||||
func (s *Server) Stop() {
|
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()
|
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 {
|
func (s *Server) build() error {
|
||||||
@@ -71,6 +89,7 @@ func (s *Server) build() error {
|
|||||||
} else {
|
} else {
|
||||||
cli = zrpc.MustNewClient(up.Grpc)
|
cli = zrpc.MustNewClient(up.Grpc)
|
||||||
}
|
}
|
||||||
|
s.conns = append(s.conns, cli)
|
||||||
|
|
||||||
source, err := s.createDescriptorSource(cli, up)
|
source, err := s.createDescriptorSource(cli, up)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func dialer() func(context.Context, string) (net.Conn, error) {
|
|||||||
func TestMustNewServer(t *testing.T) {
|
func TestMustNewServer(t *testing.T) {
|
||||||
var c GatewayConf
|
var c GatewayConf
|
||||||
assert.NoError(t, conf.FillDefault(&c))
|
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.DevServer.Host = "localhost"
|
||||||
c.Host = "localhost"
|
c.Host = "localhost"
|
||||||
c.Port = 18881
|
c.Port = 18881
|
||||||
|
|||||||
20
go.mod
20
go.mod
@@ -4,9 +4,9 @@ go 1.20
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||||
github.com/alicebob/miniredis/v2 v2.33.0
|
github.com/alicebob/miniredis/v2 v2.34.0
|
||||||
github.com/fatih/color v1.18.0
|
github.com/fatih/color v1.18.0
|
||||||
github.com/fullstorydev/grpcurl v1.9.1
|
github.com/fullstorydev/grpcurl v1.9.2
|
||||||
github.com/go-sql-driver/mysql v1.8.1
|
github.com/go-sql-driver/mysql v1.8.1
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.1
|
github.com/golang-jwt/jwt/v4 v4.5.1
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
@@ -19,7 +19,7 @@ require (
|
|||||||
github.com/prometheus/client_golang v1.20.5
|
github.com/prometheus/client_golang v1.20.5
|
||||||
github.com/redis/go-redis/v9 v9.7.0
|
github.com/redis/go-redis/v9 v9.7.0
|
||||||
github.com/spaolacci/murmur3 v1.1.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/api/v3 v3.5.15
|
||||||
go.etcd.io/etcd/client/v3 v3.5.15
|
go.etcd.io/etcd/client/v3 v3.5.15
|
||||||
go.mongodb.org/mongo-driver v1.17.1
|
go.mongodb.org/mongo-driver v1.17.1
|
||||||
@@ -33,12 +33,12 @@ require (
|
|||||||
go.opentelemetry.io/otel/trace v1.24.0
|
go.opentelemetry.io/otel/trace v1.24.0
|
||||||
go.uber.org/automaxprocs v1.6.0
|
go.uber.org/automaxprocs v1.6.0
|
||||||
go.uber.org/goleak v1.3.0
|
go.uber.org/goleak v1.3.0
|
||||||
golang.org/x/net v0.31.0
|
golang.org/x/net v0.33.0
|
||||||
golang.org/x/sys v0.27.0
|
golang.org/x/sys v0.28.0
|
||||||
golang.org/x/time v0.8.0
|
golang.org/x/time v0.8.0
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d
|
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d
|
||||||
google.golang.org/grpc v1.65.0
|
google.golang.org/grpc v1.65.0
|
||||||
google.golang.org/protobuf v1.35.2
|
google.golang.org/protobuf v1.36.1
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||||
gopkg.in/h2non/gock.v1 v1.1.2
|
gopkg.in/h2non/gock.v1 v1.1.2
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
@@ -109,11 +109,11 @@ require (
|
|||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
go.uber.org/zap v1.24.0 // indirect
|
go.uber.org/zap v1.24.0 // indirect
|
||||||
golang.org/x/crypto v0.29.0 // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
golang.org/x/oauth2 v0.21.0 // indirect
|
golang.org/x/oauth2 v0.21.0 // indirect
|
||||||
golang.org/x/sync v0.9.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/term v0.26.0 // indirect
|
golang.org/x/term v0.27.0 // indirect
|
||||||
golang.org/x/text v0.20.0 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|||||||
39
go.sum
39
go.sum
@@ -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/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 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
|
||||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
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.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
|
||||||
github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0=
|
github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8=
|
||||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
@@ -37,8 +37,8 @@ github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU
|
|||||||
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
|
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
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/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
github.com/fullstorydev/grpcurl v1.9.1 h1:YxX1aCcCc4SDBQfj9uoWcTLe8t4NWrZe1y+mk83BQgo=
|
github.com/fullstorydev/grpcurl v1.9.2 h1:ObqVQTZW7aFnhuqQoppUrvep2duMBanB0UYK2Mm8euo=
|
||||||
github.com/fullstorydev/grpcurl v1.9.1/go.mod h1:i8gKLIC6s93WdU3LSmkE5vtsCxyRmihUj5FK1cNW5EM=
|
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.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.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
@@ -168,8 +168,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.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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
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.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 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
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 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||||
@@ -228,8 +229,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
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.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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.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=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
@@ -241,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-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-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.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
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 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -250,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-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-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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
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-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -263,18 +264,18 @@ 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-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.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
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-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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
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.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.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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
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 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -294,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/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 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
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 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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|||||||
@@ -299,6 +299,8 @@ go-zero 已被许多公司用于生产部署,接入场景如在线教育、电
|
|||||||
>100. 上海邑脉科技有限公司
|
>100. 上海邑脉科技有限公司
|
||||||
>101. 上海巨瓴科技有限公司
|
>101. 上海巨瓴科技有限公司
|
||||||
>102. 深圳市兴海物联科技有限公司
|
>102. 深圳市兴海物联科技有限公司
|
||||||
|
>103. 爱芯元智半导体股份有限公司
|
||||||
|
>104. 杭州升恒科技有限公司
|
||||||
|
|
||||||
如果贵公司也已使用 go-zero,欢迎在 [登记地址](https://github.com/zeromicro/go-zero/issues/602) 登记,仅仅为了推广,不做其它用途。
|
如果贵公司也已使用 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())
|
logger := logx.WithContext(r.Context())
|
||||||
buf.WriteString(fmt.Sprintf("[HTTP] %s - %d - %s - %s\n=> %s\n",
|
buf.WriteString(fmt.Sprintf("[HTTP] %s - %d - %s - %s\n=> %s\n",
|
||||||
r.Method, code, r.RemoteAddr, timex.ReprOfDuration(duration), dumpRequest(r)))
|
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,
|
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()
|
body := logs.Flush()
|
||||||
|
|||||||
@@ -88,6 +88,36 @@ func TestParseFormArray(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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) {
|
t.Run("slice with empty and non-empty", func(t *testing.T) {
|
||||||
var v struct {
|
var v struct {
|
||||||
Name []string `form:"name"`
|
Name []string `form:"name"`
|
||||||
@@ -99,7 +129,67 @@ func TestParseFormArray(t *testing.T) {
|
|||||||
http.NoBody)
|
http.NoBody)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
if assert.NoError(t, Parse(r, &v)) {
|
if assert.NoError(t, Parse(r, &v)) {
|
||||||
assert.ElementsMatch(t, []string{"1"}, v.Name)
|
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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -528,6 +618,26 @@ func TestCustomUnmarshalerStructRequest(t *testing.T) {
|
|||||||
assert.Equal(t, "hello", v.Foo.Name)
|
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) {
|
func BenchmarkParseRaw(b *testing.B) {
|
||||||
r, err := http.NewRequest(http.MethodGet, "http://hello.com/a?name=hello&age=18&percent=3.4", http.NoBody)
|
r, err := http.NewRequest(http.MethodGet, "http://hello.com/a?name=hello&age=18&percent=3.4", http.NoBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,12 +2,23 @@ package httpx
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"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) {
|
func GetFormValues(r *http.Request) (map[string]any, error) {
|
||||||
if err := r.ParseForm(); err != nil {
|
if err := r.ParseForm(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -19,16 +30,23 @@ func GetFormValues(r *http.Request) (map[string]any, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var n int
|
||||||
params := make(map[string]any, len(r.Form))
|
params := make(map[string]any, len(r.Form))
|
||||||
for name, values := range r.Form {
|
for name, values := range r.Form {
|
||||||
filtered := make([]string, 0, len(values))
|
filtered := make([]string, 0, len(values))
|
||||||
for _, v := range values {
|
for _, v := range values {
|
||||||
if len(v) > 0 {
|
if n < maxFormParamCount {
|
||||||
filtered = append(filtered, v)
|
filtered = append(filtered, v)
|
||||||
|
n++
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("too many form values, error: %s", r.Form.Encode())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(filtered) > 0 {
|
if len(filtered) > 0 {
|
||||||
|
if strings.HasSuffix(name, arraySuffix) {
|
||||||
|
name = name[:len(name)-2]
|
||||||
|
}
|
||||||
params[name] = filtered
|
params[name] = filtered
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package httpx
|
package httpx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -23,3 +25,23 @@ func TestGetRemoteAddrNoHeader(t *testing.T) {
|
|||||||
|
|
||||||
assert.True(t, len(GetRemoteAddr(r)) == 0)
|
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")
|
||||||
|
}
|
||||||
|
|||||||
@@ -516,28 +516,55 @@ func TestParsePtrInRequestEmpty(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseQueryOptional(t *testing.T) {
|
func TestParseQueryOptional(t *testing.T) {
|
||||||
r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=", nil)
|
t.Run("optional with string", func(t *testing.T) {
|
||||||
assert.Nil(t, err)
|
r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
router := NewRouter()
|
router := NewRouter()
|
||||||
err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
|
err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
v := struct {
|
v := struct {
|
||||||
Nickname string `form:"nickname"`
|
Nickname string `form:"nickname"`
|
||||||
Zipcode int64 `form:"zipcode,optional"`
|
Zipcode string `form:"zipcode,optional"`
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err = httpx.Parse(r, &v)
|
err = httpx.Parse(r, &v)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
_, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Nickname, v.Zipcode))
|
_, err = io.WriteString(w, fmt.Sprintf("%s:%s", v.Nickname, v.Zipcode))
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}))
|
}))
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
router.ServeHTTP(rr, r)
|
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) {
|
func TestParse(t *testing.T) {
|
||||||
|
|||||||
@@ -139,12 +139,7 @@ rest.WithPrefix("%s"),`, g.prefix)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// why we check this, maybe some users set value 1, it's 1ns, not 1s.
|
timeout = fmt.Sprintf("\n rest.WithTimeout(%s),", formatDuration(duration))
|
||||||
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())
|
|
||||||
hasTimeout = true
|
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 {
|
func genRouteImports(parentPkg string, api *spec.ApiSpec) string {
|
||||||
importSet := collection.NewSet()
|
importSet := collection.NewSet()
|
||||||
importSet.AddStr(fmt.Sprintf("\"%s\"", pathx.JoinPackages(parentPkg, contextDir)))
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,4 +6,4 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Cmd describes a bug command.
|
// 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
|
ENV CGO_ENABLED 0
|
||||||
{{if .Chinese}}ENV GOPROXY https://goproxy.cn,direct
|
{{if .Chinese}}ENV GOPROXY https://goproxy.cn,direct
|
||||||
|
|
||||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
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
|
RUN apk update --no-cache && apk add --no-cache tzdata
|
||||||
{{end}}
|
{{- end}}
|
||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
ADD go.mod .
|
ADD go.mod .
|
||||||
ADD go.sum .
|
ADD go.sum .
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
COPY . .
|
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}}
|
FROM {{.BaseImage}}
|
||||||
|
|
||||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
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}}
|
ENV TZ {{.Timezone}}
|
||||||
{{end}}
|
{{end}}
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=builder /app/{{.ExeFile}} /app/{{.ExeFile}}{{if .Argument}}
|
COPY --from=builder /app/{{.ExeFile}} /app/{{.ExeFile}}
|
||||||
COPY --from=builder /app/etc /app/etc{{end}}
|
{{if .Argument -}}
|
||||||
|
COPY {{.GoRelPath}}/etc /app/etc
|
||||||
|
{{- end}}
|
||||||
{{if .HasPort}}
|
{{if .HasPort}}
|
||||||
EXPOSE {{.Port}}
|
EXPOSE {{.Port}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -4,21 +4,21 @@ go 1.20
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
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/fatih/structtag v1.2.0
|
||||||
github.com/go-sql-driver/mysql v1.8.1
|
github.com/go-sql-driver/mysql v1.8.1
|
||||||
github.com/gookit/color v1.5.4
|
github.com/gookit/color v1.5.4
|
||||||
github.com/iancoleman/strcase v0.3.0
|
github.com/iancoleman/strcase v0.3.0
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/spf13/pflag v1.0.5
|
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/withfig/autocomplete-tools/integrations/cobra v1.2.1
|
||||||
github.com/zeromicro/antlr v0.0.1
|
github.com/zeromicro/antlr v0.0.1
|
||||||
github.com/zeromicro/ddl-parser v1.0.5
|
github.com/zeromicro/ddl-parser v1.0.5
|
||||||
github.com/zeromicro/go-zero v1.7.3
|
github.com/zeromicro/go-zero v1.7.4
|
||||||
golang.org/x/text v0.19.0
|
golang.org/x/text v0.21.0
|
||||||
google.golang.org/grpc v1.65.0
|
google.golang.org/grpc v1.65.0
|
||||||
google.golang.org/protobuf v1.35.1
|
google.golang.org/protobuf v1.36.1
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 // 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/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||||
@@ -93,13 +93,13 @@ require (
|
|||||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
go.uber.org/zap v1.24.0 // indirect
|
go.uber.org/zap v1.24.0 // indirect
|
||||||
golang.org/x/crypto v0.28.0 // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
golang.org/x/net v0.30.0 // indirect
|
golang.org/x/net v0.31.0 // indirect
|
||||||
golang.org/x/oauth2 v0.21.0 // indirect
|
golang.org/x/oauth2 v0.21.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.26.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
golang.org/x/term v0.25.0 // indirect
|
golang.org/x/term v0.27.0 // indirect
|
||||||
golang.org/x/time v0.7.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/api v0.0.0-20240711142825-46eb208f015d // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc 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
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
|||||||
@@ -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/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 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
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.14.0 h1:WYxC0OrBuuC+FUCTZvb8+fzEHdZMwLEF+OnVfZA3LXU=
|
||||||
github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
github.com/emicklei/proto v1.14.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
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 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
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=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
@@ -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.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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
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.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 h1:+dBg5k7nuTE38VVdoroRsT0Z88fmvdYrI2EjzJst35I=
|
||||||
github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1/go.mod h1:nmuySobZb4kFgFy6BptpXp/BBw+xFSyvVPP6auoJB4k=
|
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=
|
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/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 h1:LaVqHdzMTjasua1yYpIYaksxKqRzFrEukj2Wi2EbWaQ=
|
||||||
github.com/zeromicro/ddl-parser v1.0.5/go.mod h1:ISU/8NuPyEpl9pa17Py9TBPetMjtsiHrb9f5XGiYbo8=
|
github.com/zeromicro/ddl-parser v1.0.5/go.mod h1:ISU/8NuPyEpl9pa17Py9TBPetMjtsiHrb9f5XGiYbo8=
|
||||||
github.com/zeromicro/go-zero v1.7.3 h1:yDUQF2DXDhUHc77/NZF6mzsoRPMBfldjPmG2O/ZSzss=
|
github.com/zeromicro/go-zero v1.7.4 h1:lyIUsqbpVRzM4NmXu5pRM3XrdRdUuWOkQmHiNmJF0VU=
|
||||||
github.com/zeromicro/go-zero v1.7.3/go.mod h1:9JIW3gHBGuc9LzvjZnNwINIq9QdiKu3AigajLtkJamQ=
|
github.com/zeromicro/go-zero v1.7.4/go.mod h1:jmv4hTdUBkDn6kxgI+WrKQw0q6LKxDElGPMfCLOeeEY=
|
||||||
go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk=
|
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/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM=
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=
|
go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=
|
||||||
@@ -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-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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.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=
|
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-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-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.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
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/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-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-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-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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
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-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/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-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.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
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.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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
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-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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
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/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 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
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 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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
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.
|
// BuildVersion is the version of goctl.
|
||||||
const BuildVersion = "1.7.3"
|
const BuildVersion = "1.7.4"
|
||||||
|
|
||||||
var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5}
|
var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5}
|
||||||
|
|
||||||
|
|||||||
@@ -176,11 +176,7 @@ func (a *Analyzer) convertKV(kv []*ast.KVExpr) map[string]string {
|
|||||||
var ret = map[string]string{}
|
var ret = map[string]string{}
|
||||||
for _, v := range kv {
|
for _, v := range kv {
|
||||||
key := strings.TrimSuffix(v.Key.Token.Text, ":")
|
key := strings.TrimSuffix(v.Key.Token.Text, ":")
|
||||||
if key == summaryKeyText {
|
ret[key] = v.Value.RawText()
|
||||||
ret[key] = v.Value.RawText()
|
|
||||||
} else {
|
|
||||||
ret[key] = v.Value.Token.Text
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|||||||
@@ -13,15 +13,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
idAPI = "api"
|
idAPI = "api"
|
||||||
summaryKeyExprText = "summary:"
|
groupKeyText = "group"
|
||||||
summaryKeyText = "summary"
|
infoTitleKey = "Title"
|
||||||
groupKeyText = "group"
|
infoDescKey = "Desc"
|
||||||
infoTitleKey = "Title"
|
infoVersionKey = "Version"
|
||||||
infoDescKey = "Desc"
|
infoAuthorKey = "Author"
|
||||||
infoVersionKey = "Version"
|
infoEmailKey = "Email"
|
||||||
infoAuthorKey = "Author"
|
|
||||||
infoEmailKey = "Email"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parser is the parser for api file.
|
// Parser is the parser for api file.
|
||||||
@@ -1201,12 +1199,6 @@ func (p *Parser) parseAtServerKVExpression() *ast.KVExpr {
|
|||||||
expr.Value = node
|
expr.Value = node
|
||||||
return expr
|
return expr
|
||||||
} else if p.peekTokenIs(token.STRING) {
|
} 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() {
|
if !p.nextToken() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -305,6 +305,7 @@ func TestParser_Parse_atServerStmt(t *testing.T) {
|
|||||||
"prefix3:": "v1/v2_",
|
"prefix3:": "v1/v2_",
|
||||||
"prefix4:": "a-b-c",
|
"prefix4:": "a-b-c",
|
||||||
"summary:": `"test"`,
|
"summary:": `"test"`,
|
||||||
|
"key:": `"bar"`,
|
||||||
}
|
}
|
||||||
|
|
||||||
p := New("foo.api", atServerTestAPI)
|
p := New("foo.api", atServerTestAPI)
|
||||||
|
|||||||
@@ -19,4 +19,5 @@
|
|||||||
prefix3: v1/v2_
|
prefix3: v1/v2_
|
||||||
prefix4: a-b-c
|
prefix4: a-b-c
|
||||||
summary:"test"
|
summary:"test"
|
||||||
|
key:"bar"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ service test {
|
|||||||
@server (
|
@server (
|
||||||
jwt: Auth
|
jwt: Auth
|
||||||
group: Group1
|
group: Group1
|
||||||
|
foo:"bar"
|
||||||
)
|
)
|
||||||
service test {
|
service test {
|
||||||
@doc "ping"
|
@doc "ping"
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ const (
|
|||||||
// string mode end
|
// string mode end
|
||||||
)
|
)
|
||||||
|
|
||||||
var missingInput = errors.New("missing input")
|
|
||||||
|
|
||||||
type mode int
|
type mode int
|
||||||
|
|
||||||
// Scanner is a lexical scanner.
|
// Scanner is a lexical scanner.
|
||||||
@@ -629,7 +627,7 @@ func NewScanner(filename string, src interface{}) (*Scanner, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil, missingInput
|
return nil, fmt.Errorf("filename: %s,missing input", filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
var runeList []rune
|
var runeList []rune
|
||||||
|
|||||||
@@ -62,13 +62,13 @@ func TestNewScanner(t *testing.T) {
|
|||||||
{
|
{
|
||||||
filename: "foo",
|
filename: "foo",
|
||||||
src: "",
|
src: "",
|
||||||
expected: missingInput,
|
expected: "missing input",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, v := range testData {
|
for _, v := range testData {
|
||||||
s, err := NewScanner(v.filename, v.src)
|
s, err := NewScanner(v.filename, v.src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
assert.Equal(t, v.expected.(error).Error(), err.Error())
|
assert.Contains(t, err.Error(), v.expected)
|
||||||
} else {
|
} else {
|
||||||
assert.Equal(t, v.expected, s.filename)
|
assert.Equal(t, v.expected, s.filename)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,13 +82,15 @@ func getRealModule(workDir string, execRun execx.RunFunc) (*Module, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if workDir[len(workDir)-1] != os.PathSeparator {
|
||||||
|
workDir = workDir + string(os.PathSeparator)
|
||||||
|
}
|
||||||
for _, m := range modules {
|
for _, m := range modules {
|
||||||
realDir, err := pathx.ReadLink(m.Dir)
|
realDir, err := pathx.ReadLink(m.Dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read go.mod, dir: %s, error: %w", m.Dir, err)
|
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) {
|
if strings.HasPrefix(workDir, realDir) {
|
||||||
return &m, nil
|
return &m, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,60 @@ func Test_getRealModule(t *testing.T) {
|
|||||||
GoVersion: "go1.20",
|
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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user