mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-11 00:40:00 +08:00
Compare commits
4 Commits
dependabot
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
910421c792 | ||
|
|
4d18efc0aa | ||
|
|
5b974b82f2 | ||
|
|
a74928a478 |
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v6
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: ./coverage.txt
|
||||
flags: unittests
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
// ErrPaddingSize indicates bad padding size.
|
||||
@@ -25,8 +27,7 @@ func newECB(b cipher.Block) *ecb {
|
||||
|
||||
type ecbEncrypter ecb
|
||||
|
||||
// Deprecated: NewECBEncrypter returns an ECB encrypter.
|
||||
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
|
||||
// NewECBEncrypter returns an ECB encrypter.
|
||||
func NewECBEncrypter(b cipher.Block) cipher.BlockMode {
|
||||
return (*ecbEncrypter)(newECB(b))
|
||||
}
|
||||
@@ -38,10 +39,12 @@ func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
|
||||
// the block size. Dst and src must overlap entirely or not at all.
|
||||
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
|
||||
if len(src)%x.blockSize != 0 {
|
||||
panic("crypto/cipher: input not full blocks")
|
||||
logx.Error("crypto/cipher: input not full blocks")
|
||||
return
|
||||
}
|
||||
if len(dst) < len(src) {
|
||||
panic("crypto/cipher: output smaller than input")
|
||||
logx.Error("crypto/cipher: output smaller than input")
|
||||
return
|
||||
}
|
||||
|
||||
for len(src) > 0 {
|
||||
@@ -53,8 +56,7 @@ func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
|
||||
|
||||
type ecbDecrypter ecb
|
||||
|
||||
// Deprecated: NewECBDecrypter returns an ECB decrypter.
|
||||
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
|
||||
// NewECBDecrypter returns an ECB decrypter.
|
||||
func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
|
||||
return (*ecbDecrypter)(newECB(b))
|
||||
}
|
||||
@@ -68,10 +70,12 @@ func (x *ecbDecrypter) BlockSize() int {
|
||||
// the block size. Dst and src must overlap entirely or not at all.
|
||||
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
|
||||
if len(src)%x.blockSize != 0 {
|
||||
panic("crypto/cipher: input not full blocks")
|
||||
logx.Error("crypto/cipher: input not full blocks")
|
||||
return
|
||||
}
|
||||
if len(dst) < len(src) {
|
||||
panic("crypto/cipher: output smaller than input")
|
||||
logx.Error("crypto/cipher: output smaller than input")
|
||||
return
|
||||
}
|
||||
|
||||
for len(src) > 0 {
|
||||
@@ -81,18 +85,14 @@ func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: EcbDecrypt decrypts src with the given key.
|
||||
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
|
||||
// EcbDecrypt decrypts src with the given key.
|
||||
func EcbDecrypt(key, src []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
logx.Errorf("Decrypt key error: % x", key)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(src)%block.BlockSize() != 0 {
|
||||
return nil, ErrPaddingSize
|
||||
}
|
||||
|
||||
decrypter := NewECBDecrypter(block)
|
||||
decrypted := make([]byte, len(src))
|
||||
decrypter.CryptBlocks(decrypted, src)
|
||||
@@ -100,9 +100,8 @@ func EcbDecrypt(key, src []byte) ([]byte, error) {
|
||||
return pkcs5Unpadding(decrypted, decrypter.BlockSize())
|
||||
}
|
||||
|
||||
// Deprecated: EcbDecryptBase64 decrypts base64 encoded src with the given base64 encoded key.
|
||||
// EcbDecryptBase64 decrypts base64 encoded src with the given base64 encoded key.
|
||||
// The returned string is also base64 encoded.
|
||||
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
|
||||
func EcbDecryptBase64(key, src string) (string, error) {
|
||||
keyBytes, err := getKeyBytes(key)
|
||||
if err != nil {
|
||||
@@ -122,11 +121,11 @@ func EcbDecryptBase64(key, src string) (string, error) {
|
||||
return base64.StdEncoding.EncodeToString(decryptedBytes), nil
|
||||
}
|
||||
|
||||
// Deprecated: EcbEncrypt encrypts src with the given key.
|
||||
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
|
||||
// EcbEncrypt encrypts src with the given key.
|
||||
func EcbEncrypt(key, src []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
logx.Errorf("Encrypt key error: % x", key)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -138,9 +137,8 @@ func EcbEncrypt(key, src []byte) ([]byte, error) {
|
||||
return crypted, nil
|
||||
}
|
||||
|
||||
// Deprecated: EcbEncryptBase64 encrypts base64 encoded src with the given base64 encoded key.
|
||||
// EcbEncryptBase64 encrypts base64 encoded src with the given base64 encoded key.
|
||||
// The returned string is also base64 encoded.
|
||||
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
|
||||
func EcbEncryptBase64(key, src string) (string, error) {
|
||||
keyBytes, err := getKeyBytes(key)
|
||||
if err != nil {
|
||||
@@ -181,20 +179,10 @@ func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
|
||||
|
||||
func pkcs5Unpadding(src []byte, blockSize int) ([]byte, error) {
|
||||
length := len(src)
|
||||
if length == 0 {
|
||||
return nil, ErrPaddingSize
|
||||
}
|
||||
|
||||
unpadding := int(src[length-1])
|
||||
if unpadding < 1 || unpadding > blockSize || unpadding > length {
|
||||
if unpadding >= length || unpadding > blockSize {
|
||||
return nil, ErrPaddingSize
|
||||
}
|
||||
|
||||
for _, b := range src[length-unpadding:] {
|
||||
if int(b) != unpadding {
|
||||
return nil, ErrPaddingSize
|
||||
}
|
||||
}
|
||||
|
||||
return src[:length-unpadding], nil
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ func TestAesEcb(t *testing.T) {
|
||||
_, err = EcbDecrypt(badKey2, dst)
|
||||
assert.NotNil(t, err)
|
||||
_, err = EcbDecrypt(key, val)
|
||||
// not a multiple of block size
|
||||
assert.NotNil(t, err)
|
||||
// not enough block, just nil
|
||||
assert.Nil(t, err)
|
||||
src, err := EcbDecrypt(key, dst)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, src)
|
||||
@@ -41,28 +41,33 @@ func TestAesEcb(t *testing.T) {
|
||||
assert.Equal(t, 16, decrypter.BlockSize())
|
||||
|
||||
dst = make([]byte, 8)
|
||||
assert.Panics(t, func() {
|
||||
encrypter.CryptBlocks(dst, val)
|
||||
})
|
||||
encrypter.CryptBlocks(dst, val)
|
||||
for _, b := range dst {
|
||||
assert.Equal(t, byte(0), b)
|
||||
}
|
||||
|
||||
dst = make([]byte, 8)
|
||||
assert.Panics(t, func() {
|
||||
encrypter.CryptBlocks(dst, valLong)
|
||||
})
|
||||
encrypter.CryptBlocks(dst, valLong)
|
||||
for _, b := range dst {
|
||||
assert.Equal(t, byte(0), b)
|
||||
}
|
||||
|
||||
dst = make([]byte, 8)
|
||||
assert.Panics(t, func() {
|
||||
decrypter.CryptBlocks(dst, val)
|
||||
})
|
||||
decrypter.CryptBlocks(dst, val)
|
||||
for _, b := range dst {
|
||||
assert.Equal(t, byte(0), b)
|
||||
}
|
||||
|
||||
dst = make([]byte, 8)
|
||||
assert.Panics(t, func() {
|
||||
decrypter.CryptBlocks(dst, valLong)
|
||||
})
|
||||
decrypter.CryptBlocks(dst, valLong)
|
||||
for _, b := range dst {
|
||||
assert.Equal(t, byte(0), b)
|
||||
}
|
||||
|
||||
_, err = EcbEncryptBase64("cTR0N3dDKkYtSmFOZFJnVWpYbjJyNXU4eC9BP0QK", "aGVsbG93b3JsZGxvbmcuLgo=")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestAesEcbBase64(t *testing.T) {
|
||||
const (
|
||||
val = "hello"
|
||||
@@ -93,44 +98,3 @@ func TestAesEcbBase64(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, string(b))
|
||||
}
|
||||
|
||||
func TestPkcs5UnpaddingEmptyInput(t *testing.T) {
|
||||
_, err := pkcs5Unpadding([]byte{}, 16)
|
||||
assert.Equal(t, ErrPaddingSize, err)
|
||||
}
|
||||
|
||||
func TestPkcs5UnpaddingMalformedPadding(t *testing.T) {
|
||||
// Valid PKCS5 padding of 3: last 3 bytes should all be 0x03
|
||||
// Here we corrupt one padding byte
|
||||
malformed := []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x02, 0x03, 0x03}
|
||||
_, err := pkcs5Unpadding(malformed, 16)
|
||||
assert.Equal(t, ErrPaddingSize, err)
|
||||
|
||||
// All padding bytes correct
|
||||
valid := []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x03, 0x03, 0x03}
|
||||
result, err := pkcs5Unpadding(valid, 16)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, valid[:13], result)
|
||||
}
|
||||
|
||||
func TestPkcs5UnpaddingInvalidPaddingValue(t *testing.T) {
|
||||
// padding value = 0 (< 1)
|
||||
_, err := pkcs5Unpadding([]byte{0x41, 0x00}, 16)
|
||||
assert.Equal(t, ErrPaddingSize, err)
|
||||
|
||||
// padding value > blockSize
|
||||
_, err = pkcs5Unpadding([]byte{0x41, 0x41, 0x41, 0x41, 17}, 4)
|
||||
assert.Equal(t, ErrPaddingSize, err)
|
||||
|
||||
// padding value > length
|
||||
_, err = pkcs5Unpadding([]byte{0x41, 0x03}, 16)
|
||||
assert.Equal(t, ErrPaddingSize, err)
|
||||
}
|
||||
|
||||
func TestEcbDecryptEmptyInput(t *testing.T) {
|
||||
key := []byte("q4t7w!z%C*F-JaNdRgUjXn2r5u8x/A?D")
|
||||
_, err := EcbDecrypt(key, []byte{})
|
||||
assert.Equal(t, ErrPaddingSize, err)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func ComputeKey(pubKey, priKey *big.Int) (*big.Int, error) {
|
||||
return nil, ErrInvalidPubKey
|
||||
}
|
||||
|
||||
if pubKey.Sign() <= 0 || p.Cmp(pubKey) <= 0 {
|
||||
if pubKey.Sign() <= 0 && p.Cmp(pubKey) <= 0 {
|
||||
return nil, ErrPubKeyOutOfBound
|
||||
}
|
||||
|
||||
|
||||
@@ -94,32 +94,3 @@ func TestDHOnErrors(t *testing.T) {
|
||||
|
||||
assert.NotNil(t, NewPublicKey([]byte("")))
|
||||
}
|
||||
|
||||
func TestDHPubKeyBoundary(t *testing.T) {
|
||||
key, err := GenerateKey()
|
||||
assert.Nil(t, err)
|
||||
|
||||
// pubKey = 0 should be rejected
|
||||
_, err = ComputeKey(big.NewInt(0), key.PriKey)
|
||||
assert.ErrorIs(t, err, ErrPubKeyOutOfBound)
|
||||
|
||||
// pubKey = -1 should be rejected
|
||||
_, err = ComputeKey(big.NewInt(-1), key.PriKey)
|
||||
assert.ErrorIs(t, err, ErrPubKeyOutOfBound)
|
||||
|
||||
// pubKey = p should be rejected
|
||||
_, err = ComputeKey(new(big.Int).Set(p), key.PriKey)
|
||||
assert.ErrorIs(t, err, ErrPubKeyOutOfBound)
|
||||
|
||||
// pubKey = p+1 should be rejected
|
||||
_, err = ComputeKey(new(big.Int).Add(p, big.NewInt(1)), key.PriKey)
|
||||
assert.ErrorIs(t, err, ErrPubKeyOutOfBound)
|
||||
|
||||
// pubKey = 1 should be accepted
|
||||
_, err = ComputeKey(big.NewInt(1), key.PriKey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// pubKey = p-1 should be accepted
|
||||
_, err = ComputeKey(new(big.Int).Sub(p, big.NewInt(1)), key.PriKey)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package codec
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
@@ -47,9 +46,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
// Deprecated: NewRsaDecrypter returns a RsaDecrypter with the given file.
|
||||
// PKCS#1 v1.5 padding is vulnerable to padding oracle attacks.
|
||||
// Use NewRsaOAEPDecrypter instead.
|
||||
// NewRsaDecrypter returns a RsaDecrypter with the given file.
|
||||
func NewRsaDecrypter(file string) (RsaDecrypter, error) {
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
@@ -93,9 +90,7 @@ func (r *rsaDecrypter) DecryptBase64(input string) ([]byte, error) {
|
||||
return r.Decrypt(base64Decoded)
|
||||
}
|
||||
|
||||
// Deprecated: NewRsaEncrypter returns a RsaEncrypter with the given key.
|
||||
// PKCS#1 v1.5 padding is vulnerable to padding oracle attacks.
|
||||
// Use NewRsaOAEPEncrypter instead.
|
||||
// NewRsaEncrypter returns a RsaEncrypter with the given key.
|
||||
func NewRsaEncrypter(key []byte) (RsaEncrypter, error) {
|
||||
block, _ := pem.Decode(key)
|
||||
if block == nil {
|
||||
@@ -159,90 +154,3 @@ func rsaDecryptBlock(privateKey *rsa.PrivateKey, block []byte) ([]byte, error) {
|
||||
func rsaEncryptBlock(publicKey *rsa.PublicKey, msg []byte) ([]byte, error) {
|
||||
return rsa.EncryptPKCS1v15(rand.Reader, publicKey, msg)
|
||||
}
|
||||
|
||||
// NewRsaOAEPDecrypter returns a RsaDecrypter using OAEP with SHA-256.
|
||||
func NewRsaOAEPDecrypter(file string) (RsaDecrypter, error) {
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(content)
|
||||
if block == nil {
|
||||
return nil, ErrPrivateKey
|
||||
}
|
||||
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rsaOAEPDecrypter{
|
||||
rsaBase: rsaBase{
|
||||
bytesLimit: privateKey.N.BitLen() >> 3,
|
||||
},
|
||||
privateKey: privateKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewRsaOAEPEncrypter returns a RsaEncrypter using OAEP with SHA-256.
|
||||
func NewRsaOAEPEncrypter(key []byte) (RsaEncrypter, error) {
|
||||
block, _ := pem.Decode(key)
|
||||
if block == nil {
|
||||
return nil, ErrPublicKey
|
||||
}
|
||||
|
||||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch pubKey := pub.(type) {
|
||||
case *rsa.PublicKey:
|
||||
// OAEP overhead: 2*hash_size + 2
|
||||
hashSize := sha256.New().Size()
|
||||
return &rsaOAEPEncrypter{
|
||||
rsaBase: rsaBase{
|
||||
bytesLimit: (pubKey.N.BitLen() >> 3) - 2*hashSize - 2,
|
||||
},
|
||||
publicKey: pubKey,
|
||||
}, nil
|
||||
default:
|
||||
return nil, ErrNotRsaKey
|
||||
}
|
||||
}
|
||||
|
||||
type rsaOAEPDecrypter struct {
|
||||
rsaBase
|
||||
privateKey *rsa.PrivateKey
|
||||
}
|
||||
|
||||
func (r *rsaOAEPDecrypter) Decrypt(input []byte) ([]byte, error) {
|
||||
return r.crypt(input, func(block []byte) ([]byte, error) {
|
||||
return rsa.DecryptOAEP(sha256.New(), rand.Reader, r.privateKey, block, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *rsaOAEPDecrypter) DecryptBase64(input string) ([]byte, error) {
|
||||
if len(input) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
base64Decoded, err := base64.StdEncoding.DecodeString(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.Decrypt(base64Decoded)
|
||||
}
|
||||
|
||||
type rsaOAEPEncrypter struct {
|
||||
rsaBase
|
||||
publicKey *rsa.PublicKey
|
||||
}
|
||||
|
||||
func (r *rsaOAEPEncrypter) Encrypt(input []byte) ([]byte, error) {
|
||||
return r.crypt(input, func(block []byte) ([]byte, error) {
|
||||
return rsa.EncryptOAEP(sha256.New(), rand.Reader, r.publicKey, block, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -63,78 +58,3 @@ func TestBadPubKey(t *testing.T) {
|
||||
_, err := NewRsaEncrypter([]byte("foo"))
|
||||
assert.Equal(t, ErrPublicKey, err)
|
||||
}
|
||||
|
||||
func TestOAEPCryption(t *testing.T) {
|
||||
enc, err := NewRsaOAEPEncrypter([]byte(pubKey))
|
||||
assert.Nil(t, err)
|
||||
ret, err := enc.Encrypt([]byte(testBody))
|
||||
assert.Nil(t, err)
|
||||
|
||||
file, err := fs.TempFilenameWithText(priKey)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(file)
|
||||
dec, err := NewRsaOAEPDecrypter(file)
|
||||
assert.Nil(t, err)
|
||||
actual, err := dec.Decrypt(ret)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, testBody, string(actual))
|
||||
|
||||
actual, err = dec.DecryptBase64(base64.StdEncoding.EncodeToString(ret))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, testBody, string(actual))
|
||||
|
||||
// empty input
|
||||
actual, err = dec.DecryptBase64("")
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, actual)
|
||||
}
|
||||
|
||||
func TestOAEPBadKeys(t *testing.T) {
|
||||
_, err := NewRsaOAEPEncrypter([]byte("bad"))
|
||||
assert.Equal(t, ErrPublicKey, err)
|
||||
|
||||
_, err = NewRsaOAEPDecrypter("nonexistent")
|
||||
assert.Error(t, err)
|
||||
|
||||
// valid PEM but invalid private key content
|
||||
badPem, err := fs.TempFilenameWithText("-----BEGIN RSA PRIVATE KEY-----\nYmFk\n-----END RSA PRIVATE KEY-----")
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(badPem)
|
||||
_, err = NewRsaOAEPDecrypter(badPem)
|
||||
assert.Error(t, err)
|
||||
|
||||
// not PEM content at all
|
||||
notPem, err := fs.TempFilenameWithText("not a pem file")
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(notPem)
|
||||
_, err = NewRsaOAEPDecrypter(notPem)
|
||||
assert.Equal(t, ErrPrivateKey, err)
|
||||
}
|
||||
|
||||
func TestOAEPEncrypterParseError(t *testing.T) {
|
||||
// valid PEM block but invalid public key content
|
||||
badPub := []byte("-----BEGIN PUBLIC KEY-----\nYmFk\n-----END PUBLIC KEY-----")
|
||||
_, err := NewRsaOAEPEncrypter(badPub)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestOAEPEncrypterNonRsaKey(t *testing.T) {
|
||||
ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
assert.Nil(t, err)
|
||||
derBytes, err := x509.MarshalPKIXPublicKey(&ecKey.PublicKey)
|
||||
assert.Nil(t, err)
|
||||
ecPem := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: derBytes})
|
||||
_, err = NewRsaOAEPEncrypter(ecPem)
|
||||
assert.Equal(t, ErrNotRsaKey, err)
|
||||
}
|
||||
|
||||
func TestOAEPDecryptBase64Error(t *testing.T) {
|
||||
file, err := fs.TempFilenameWithText(priKey)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(file)
|
||||
dec, err := NewRsaOAEPDecrypter(file)
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, err = dec.DecryptBase64("not-valid-base64!!!")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -21,11 +21,10 @@ const (
|
||||
var (
|
||||
fillDefaultUnmarshaler = mapping.NewUnmarshaler(jsonTagKey, mapping.WithDefault())
|
||||
loaders = map[string]func([]byte, any) error{
|
||||
".json": LoadFromJsonBytes,
|
||||
".json5": LoadFromJson5Bytes,
|
||||
".toml": LoadFromTomlBytes,
|
||||
".yaml": LoadFromYamlBytes,
|
||||
".yml": LoadFromYamlBytes,
|
||||
".json": LoadFromJsonBytes,
|
||||
".toml": LoadFromTomlBytes,
|
||||
".yaml": LoadFromYamlBytes,
|
||||
".yml": LoadFromYamlBytes,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -42,7 +41,7 @@ func FillDefault(v any) error {
|
||||
return fillDefaultUnmarshaler.Unmarshal(map[string]any{}, v)
|
||||
}
|
||||
|
||||
// Load loads config into v from file, .json, .json5, .toml, .yaml and .yml are acceptable.
|
||||
// Load loads config into v from file, .json, .yaml and .yml are acceptable.
|
||||
func Load(file string, v any, opts ...Option) error {
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
@@ -66,7 +65,7 @@ func Load(file string, v any, opts ...Option) error {
|
||||
return loader(content, v)
|
||||
}
|
||||
|
||||
// LoadConfig loads config into v from file, .json, .json5, .toml, .yaml and .yml are acceptable.
|
||||
// LoadConfig loads config into v from file, .json, .yaml and .yml are acceptable.
|
||||
// Deprecated: use Load instead.
|
||||
func LoadConfig(file string, v any, opts ...Option) error {
|
||||
return Load(file, v, opts...)
|
||||
@@ -120,16 +119,6 @@ func LoadFromYamlBytes(content []byte, v any) error {
|
||||
return LoadFromJsonBytes(b, v)
|
||||
}
|
||||
|
||||
// LoadFromJson5Bytes loads config into v from content json5 bytes.
|
||||
func LoadFromJson5Bytes(content []byte, v any) error {
|
||||
b, err := encoding.Json5ToJson(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return LoadFromJsonBytes(b, v)
|
||||
}
|
||||
|
||||
// LoadConfigFromYamlBytes loads config into v from content yaml bytes.
|
||||
// Deprecated: use LoadFromYamlBytes instead.
|
||||
func LoadConfigFromYamlBytes(content []byte, v any) error {
|
||||
|
||||
@@ -75,160 +75,6 @@ func TestLoadFromJsonBytesArray(t *testing.T) {
|
||||
assert.EqualValues(t, []string{"foo", "bar"}, expect)
|
||||
}
|
||||
|
||||
func TestConfigJson5(t *testing.T) {
|
||||
// JSON5 with comments, trailing commas, and unquoted keys
|
||||
text := `{
|
||||
// This is a comment
|
||||
a: 'foo', // single quotes
|
||||
b: 1,
|
||||
c: "${FOO}",
|
||||
d: "abcd!@#$112", // trailing comma
|
||||
}`
|
||||
t.Setenv("FOO", "2")
|
||||
|
||||
tmpfile, err := createTempFile(t, ".json5", text)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var val struct {
|
||||
A string `json:"a"`
|
||||
B int `json:"b"`
|
||||
C string `json:"c"`
|
||||
D string `json:"d"`
|
||||
}
|
||||
MustLoad(tmpfile, &val)
|
||||
assert.Equal(t, "foo", val.A)
|
||||
assert.Equal(t, 1, val.B)
|
||||
assert.Equal(t, "${FOO}", val.C)
|
||||
assert.Equal(t, "abcd!@#$112", val.D)
|
||||
}
|
||||
|
||||
func TestConfigJsonStandardParser(t *testing.T) {
|
||||
// Standard JSON uses standard JSON parser (not JSON5) for backward compatibility
|
||||
text := `{
|
||||
"a": "foo",
|
||||
"b": 1,
|
||||
"c": "${FOO}",
|
||||
"d": "abcd!@#$112"
|
||||
}`
|
||||
t.Setenv("FOO", "2")
|
||||
|
||||
tmpfile, err := createTempFile(t, ".json", text)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var val struct {
|
||||
A string `json:"a"`
|
||||
B int `json:"b"`
|
||||
C string `json:"c"`
|
||||
D string `json:"d"`
|
||||
}
|
||||
MustLoad(tmpfile, &val)
|
||||
assert.Equal(t, "foo", val.A)
|
||||
assert.Equal(t, 1, val.B)
|
||||
assert.Equal(t, "${FOO}", val.C)
|
||||
assert.Equal(t, "abcd!@#$112", val.D)
|
||||
}
|
||||
|
||||
func TestConfigJsonLargeIntegers(t *testing.T) {
|
||||
// Test that .json files preserve large integer precision (backward compatibility)
|
||||
text := `{
|
||||
"id": 1234567890123456789,
|
||||
"timestamp": 9223372036854775807
|
||||
}`
|
||||
|
||||
tmpfile, err := createTempFile(t, ".json", text)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var val struct {
|
||||
ID int64 `json:"id"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
MustLoad(tmpfile, &val)
|
||||
assert.Equal(t, int64(1234567890123456789), val.ID)
|
||||
assert.Equal(t, int64(9223372036854775807), val.Timestamp)
|
||||
}
|
||||
|
||||
func TestConfigJson5Env(t *testing.T) {
|
||||
text := `{
|
||||
// Comment with env variable
|
||||
a: "foo",
|
||||
b: 1,
|
||||
c: "${FOO}",
|
||||
d: "abcd!@#$a12 3",
|
||||
}`
|
||||
t.Setenv("FOO", "2")
|
||||
|
||||
tmpfile, err := createTempFile(t, ".json5", text)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var val struct {
|
||||
A string `json:"a"`
|
||||
B int `json:"b"`
|
||||
C string `json:"c"`
|
||||
D string `json:"d"`
|
||||
}
|
||||
MustLoad(tmpfile, &val, UseEnv())
|
||||
assert.Equal(t, "foo", val.A)
|
||||
assert.Equal(t, 1, val.B)
|
||||
assert.Equal(t, "2", val.C)
|
||||
assert.Equal(t, "abcd!@# 3", val.D)
|
||||
}
|
||||
|
||||
func TestLoadFromJson5Bytes(t *testing.T) {
|
||||
// Test JSON5 features: comments, trailing commas, single quotes, unquoted keys
|
||||
input := []byte(`{
|
||||
// This is a comment
|
||||
users: [
|
||||
{name: 'foo'}, // trailing comma
|
||||
{Name: "bar"},
|
||||
],
|
||||
}`)
|
||||
var val struct {
|
||||
Users []struct {
|
||||
Name string
|
||||
}
|
||||
}
|
||||
|
||||
assert.NoError(t, LoadFromJson5Bytes(input, &val))
|
||||
var expect []string
|
||||
for _, user := range val.Users {
|
||||
expect = append(expect, user.Name)
|
||||
}
|
||||
assert.EqualValues(t, []string{"foo", "bar"}, expect)
|
||||
}
|
||||
|
||||
func TestLoadFromJson5BytesError(t *testing.T) {
|
||||
// Invalid JSON5 syntax
|
||||
input := []byte(`{a: foo}`) // unquoted string value (invalid)
|
||||
var val struct {
|
||||
A string
|
||||
}
|
||||
|
||||
assert.Error(t, LoadFromJson5Bytes(input, &val))
|
||||
}
|
||||
|
||||
func TestConfigJson5LargeIntegersLimitation(t *testing.T) {
|
||||
// Document that JSON5 has precision limitations for large integers (>2^53)
|
||||
// due to JavaScript number semantics. Users should use .json for configs with large IDs.
|
||||
text := `{
|
||||
// JSON5 converts numbers to float64, which loses precision for large integers
|
||||
id: 1234567890123456789
|
||||
}`
|
||||
|
||||
tmpfile, err := createTempFile(t, ".json5", text)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var val struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// This will load; depending on the JSON5 implementation, large integers may lose precision.
|
||||
// This test documents that behavior without requiring loss of precision as an invariant.
|
||||
err = Load(tmpfile, &val)
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Logf("loaded JSON5 large integer id=%d (original 1234567890123456789)", val.ID)
|
||||
}
|
||||
|
||||
func TestConfigToml(t *testing.T) {
|
||||
text := `a = "foo"
|
||||
b = 1
|
||||
|
||||
@@ -25,29 +25,6 @@ func TestMd5Hex(t *testing.T) {
|
||||
assert.Equal(t, md5Digest, actual)
|
||||
}
|
||||
|
||||
func TestHash(t *testing.T) {
|
||||
result := Hash([]byte(text))
|
||||
assert.NotEqual(t, uint64(0), result)
|
||||
}
|
||||
|
||||
func TestHash_Deterministic(t *testing.T) {
|
||||
data := []byte("consistent-hash-test")
|
||||
first := Hash(data)
|
||||
second := Hash(data)
|
||||
assert.Equal(t, first, second)
|
||||
}
|
||||
|
||||
func TestHash_Empty(t *testing.T) {
|
||||
// Hash should not panic on empty input.
|
||||
result := Hash([]byte{})
|
||||
_ = result
|
||||
}
|
||||
|
||||
func TestMd5Hex_Empty(t *testing.T) {
|
||||
result := Md5Hex([]byte{})
|
||||
assert.Equal(t, 32, len(result))
|
||||
}
|
||||
|
||||
func BenchmarkHashFnv(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
h := fnv.New32()
|
||||
|
||||
@@ -247,7 +247,7 @@ func TestStructedLogDebugf(t *testing.T) {
|
||||
defer writer.Store(old)
|
||||
|
||||
doTestStructedLog(t, levelDebug, w, func(v ...any) {
|
||||
Debugf("%s", fmt.Sprint(v...))
|
||||
Debugf(fmt.Sprint(v...))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -559,7 +559,7 @@ func TestStructedLogSlowf(t *testing.T) {
|
||||
defer writer.Store(old)
|
||||
|
||||
doTestStructedLog(t, levelSlow, w, func(v ...any) {
|
||||
Slowf("%s", fmt.Sprint(v...))
|
||||
Slowf(fmt.Sprint(v...))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -625,7 +625,7 @@ func TestStructedLogStatf(t *testing.T) {
|
||||
defer writer.Store(old)
|
||||
|
||||
doTestStructedLog(t, levelStat, w, func(v ...any) {
|
||||
Statf("%s", fmt.Sprint(v...))
|
||||
Statf(fmt.Sprint(v...))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -645,7 +645,7 @@ func TestStructedLogSeveref(t *testing.T) {
|
||||
defer writer.Store(old)
|
||||
|
||||
doTestStructedLog(t, levelSevere, w, func(v ...any) {
|
||||
Severef("%s", fmt.Sprint(v...))
|
||||
Severef(fmt.Sprint(v...))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package mapping
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -13,6 +12,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/lang"
|
||||
"github.com/zeromicro/go-zero/core/stringx"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -278,7 +278,7 @@ func parseKeyAndOptions(tagName string, field reflect.StructField) (string, *fie
|
||||
cache, ok := optionsCache[value]
|
||||
cacheLock.RUnlock()
|
||||
if ok {
|
||||
return cmp.Or(cache.key, field.Name), cache.options, cache.err
|
||||
return stringx.TakeOne(cache.key, field.Name), cache.options, cache.err
|
||||
}
|
||||
|
||||
key, options, err := doParseKeyAndOptions(field, value)
|
||||
@@ -290,7 +290,7 @@ func parseKeyAndOptions(tagName string, field reflect.StructField) (string, *fie
|
||||
}
|
||||
cacheLock.Unlock()
|
||||
|
||||
return cmp.Or(key, field.Name), options, err
|
||||
return stringx.TakeOne(key, field.Name), options, err
|
||||
}
|
||||
|
||||
// support below notations:
|
||||
|
||||
@@ -29,10 +29,3 @@ func TestCalcDiffEntropy(t *testing.T) {
|
||||
}
|
||||
assert.True(t, CalcEntropy(m) < .99)
|
||||
}
|
||||
|
||||
func TestCalcEntropySingleItem(t *testing.T) {
|
||||
m := map[any]int{
|
||||
"only": 42,
|
||||
}
|
||||
assert.Equal(t, float64(1), CalcEntropy(m))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package mathx
|
||||
|
||||
// Numerical is a constraint that permits any numeric type.
|
||||
type Numerical interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 |
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
|
||||
|
||||
@@ -65,7 +65,6 @@ type (
|
||||
// RedisNode interface represents a redis node.
|
||||
RedisNode interface {
|
||||
red.Cmdable
|
||||
Do(ctx context.Context, args ...any) *red.Cmd
|
||||
}
|
||||
|
||||
// GeoLocation is used with GeoAdd to add geospatial location.
|
||||
@@ -406,25 +405,6 @@ func (s *Redis) DelCtx(ctx context.Context, keys ...string) (int, error) {
|
||||
return int(v), nil
|
||||
}
|
||||
|
||||
// Do executes a generic redis command with given arguments.
|
||||
func (s *Redis) Do(args ...any) (any, error) {
|
||||
return s.DoCtx(context.Background(), args...)
|
||||
}
|
||||
|
||||
// DoCtx executes a generic redis command with given arguments using the provided context.
|
||||
func (s *Redis) DoCtx(ctx context.Context, args ...any) (any, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, errors.New("missing redis command")
|
||||
}
|
||||
|
||||
conn, err := getRedis(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn.Do(ctx, args...).Result()
|
||||
}
|
||||
|
||||
// Eval is the implementation of redis eval command.
|
||||
func (s *Redis) Eval(script string, keys []string, args ...any) (any, error) {
|
||||
return s.EvalCtx(context.Background(), script, keys, args...)
|
||||
|
||||
@@ -275,36 +275,6 @@ func TestRedis_Eval(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRedis_Do(t *testing.T) {
|
||||
runOnRedis(t, func(client *Redis) {
|
||||
_, err := newRedis(client.Addr, badType()).Do("PING")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
pong, err := client.Do("PING")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "PONG", pong)
|
||||
|
||||
ok, err := client.Do("SET", "key1", "value1")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "OK", ok)
|
||||
|
||||
val, err := client.Do("GET", "key1")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "value1", val)
|
||||
|
||||
_, err = client.Do("GET", "not_exist")
|
||||
assert.Equal(t, Nil, err)
|
||||
|
||||
_, err = client.Do()
|
||||
assert.NotNil(t, err)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
_, err = client.DoCtx(ctx, "PING")
|
||||
assert.Equal(t, context.Canceled, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRedis_ScriptRun(t *testing.T) {
|
||||
runOnRedis(t, func(client *Redis) {
|
||||
sc := NewScript(`redis.call("EXISTS", KEYS[1])`)
|
||||
|
||||
@@ -3,7 +3,6 @@ package stringx
|
||||
import (
|
||||
"errors"
|
||||
"slices"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/lang"
|
||||
@@ -22,14 +21,20 @@ func Contains(list []string, str string) bool {
|
||||
return slices.Contains(list, str)
|
||||
}
|
||||
|
||||
// Filter filters chars from s with given remove function.
|
||||
func Filter(s string, remove func(r rune) bool) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
if remove(r) {
|
||||
return -1
|
||||
// Filter filters chars from s with given filter function.
|
||||
func Filter(s string, filter func(r rune) bool) string {
|
||||
var n int
|
||||
chars := []rune(s)
|
||||
for i, x := range chars {
|
||||
if n < i {
|
||||
chars[n] = x
|
||||
}
|
||||
return r
|
||||
}, s)
|
||||
if !filter(x) {
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
return string(chars[:n])
|
||||
}
|
||||
|
||||
// FirstN returns first n runes from s.
|
||||
@@ -136,7 +141,6 @@ func Substr(str string, start, stop int) (string, error) {
|
||||
}
|
||||
|
||||
// TakeOne returns valid string if not empty or later one.
|
||||
// Deprecated: use cmp.Or instead.
|
||||
func TakeOne(valid, or string) string {
|
||||
if len(valid) > 0 {
|
||||
return valid
|
||||
|
||||
@@ -29,40 +29,6 @@ func TestContainsString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasEmpty(t *testing.T) {
|
||||
cases := []struct {
|
||||
args []string
|
||||
expect bool
|
||||
}{
|
||||
{
|
||||
args: []string{"a", "b", "c"},
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
args: []string{"a", "", "c"},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
args: []string{""},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
args: []string{},
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
args: nil,
|
||||
expect: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, each := range cases {
|
||||
t.Run(path.Join(each.args...), func(t *testing.T) {
|
||||
assert.Equal(t, each.expect, HasEmpty(each.args...))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotEmpty(t *testing.T) {
|
||||
cases := []struct {
|
||||
args []string
|
||||
@@ -126,24 +92,6 @@ func TestFilter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFilter(b *testing.B) {
|
||||
b.Run("true", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Filter(`ab,cd,ef`, func(r rune) bool { return r == ',' })
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("false", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Filter(`ab,cd,ef`, func(r rune) bool { return r == '!' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestFirstN(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
133
go.mod
133
go.mod
@@ -1,84 +1,83 @@
|
||||
module github.com/zeromicro/go-zero
|
||||
|
||||
go 1.26.0
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
github.com/alicebob/miniredis/v2 v2.37.0
|
||||
github.com/alicebob/miniredis/v2 v2.36.1
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/fullstorydev/grpcurl v1.9.3
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/grafana/pyroscope-go v1.2.8
|
||||
github.com/jackc/pgx/v5 v5.8.0
|
||||
github.com/jhump/protoreflect v1.18.0
|
||||
github.com/modelcontextprotocol/go-sdk v1.4.0
|
||||
github.com/pelletier/go-toml/v2 v2.3.0
|
||||
github.com/grafana/pyroscope-go v1.2.7
|
||||
github.com/jackc/pgx/v5 v5.7.4
|
||||
github.com/jhump/protoreflect v1.17.0
|
||||
github.com/modelcontextprotocol/go-sdk v1.2.0
|
||||
github.com/pelletier/go-toml/v2 v2.2.4
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/redis/go-redis/v9 v9.18.0
|
||||
github.com/redis/go-redis/v9 v9.17.3
|
||||
github.com/spaolacci/murmur3 v1.1.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/titanous/json5 v1.0.0
|
||||
go.etcd.io/etcd/api/v3 v3.5.21
|
||||
go.etcd.io/etcd/client/v3 v3.5.21
|
||||
go.etcd.io/etcd/api/v3 v3.5.15
|
||||
go.etcd.io/etcd/client/v3 v3.5.15
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.opentelemetry.io/otel v1.40.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.40.0
|
||||
go.opentelemetry.io/otel/sdk v1.40.0
|
||||
go.opentelemetry.io/otel/trace v1.40.0
|
||||
go.opentelemetry.io/otel v1.24.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.24.0
|
||||
go.opentelemetry.io/otel/sdk v1.24.0
|
||||
go.opentelemetry.io/otel/trace v1.24.0
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
go.uber.org/goleak v1.3.0
|
||||
go.uber.org/mock v0.6.0
|
||||
golang.org/x/net v0.50.0
|
||||
golang.org/x/sys v0.41.0
|
||||
golang.org/x/time v0.14.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409
|
||||
google.golang.org/grpc v1.80.0
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/time v0.10.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/api v0.34.3
|
||||
k8s.io/apimachinery v0.36.0
|
||||
k8s.io/client-go v0.34.3
|
||||
k8s.io/utils v0.0.0-20260319190234-28399d86e0b5
|
||||
k8s.io/api v0.29.3
|
||||
k8s.io/apimachinery v0.29.4
|
||||
k8s.io/client-go v0.29.3
|
||||
k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/bufbuild/protocompile v0.14.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/envoyproxy/go-control-plane v0.12.0 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/jsonschema-go v0.4.2 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/jsonschema-go v0.3.0 // indirect
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jhump/protoreflect/v2 v2.0.0-beta.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
@@ -88,50 +87,40 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.3 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/segmentio/asm v1.1.3 // indirect
|
||||
github.com/segmentio/encoding v0.5.3 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.2.0 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/yuin/gopher-lua v1.1.1 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/term v0.40.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/crypto v0.41.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog/v2 v2.140.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
293
go.sum
293
go.sum
@@ -2,8 +2,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/alicebob/miniredis/v2 v2.37.0 h1:RheObYW32G1aiJIj81XVt78ZHJpHonHLHW7OLIshq68=
|
||||
github.com/alicebob/miniredis/v2 v2.37.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
|
||||
github.com/alicebob/miniredis/v2 v2.36.1 h1:Dvc5oAnNOr7BIfPn7tF269U8DvRW1dBG2D5n0WrfYMI=
|
||||
github.com/alicebob/miniredis/v2 v2.36.1/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -14,90 +14,90 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
|
||||
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
|
||||
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw=
|
||||
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
|
||||
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/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI=
|
||||
github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fullstorydev/grpcurl v1.9.3 h1:PC1Xi3w+JAvEE2Tg2Gf2RfVgPbf9+tbuQr1ZkyVU3jk=
|
||||
github.com/fullstorydev/grpcurl v1.9.3/go.mod h1:/b4Wxe8bG6ndAjlfSUjwseQReUDUvBJiFEB7UllOlUE=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/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/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
|
||||
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q=
|
||||
github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grafana/pyroscope-go v1.2.8 h1:UvCwIhlx9DeV7F6TW/z8q1Mi4PIm3vuUJ2ZlCEvmA4M=
|
||||
github.com/grafana/pyroscope-go v1.2.8/go.mod h1:SSi59eQ1/zmKoY/BKwa5rSFsJaq+242Bcrr4wPix1g8=
|
||||
github.com/grafana/pyroscope-go v1.2.7 h1:VWBBlqxjyR0Cwk2W6UrE8CdcdD80GOFNutj0Kb1T8ac=
|
||||
github.com/grafana/pyroscope-go v1.2.7/go.mod h1:o/bpSLiJYYP6HQtvcoVKiE9s5RiNgjYTj1DhiddP2Pc=
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og=
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=
|
||||
github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=
|
||||
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
|
||||
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jhump/protoreflect v1.18.0 h1:TOz0MSR/0JOZ5kECB/0ufGnC2jdsgZ123Rd/k4Z5/2w=
|
||||
github.com/jhump/protoreflect v1.18.0/go.mod h1:ezWcltJIVF4zYdIFM+D/sHV4Oh5LNU08ORzCGfwvTz8=
|
||||
github.com/jhump/protoreflect/v2 v2.0.0-beta.1 h1:Dw1rslK/VotaUGYsv53XVWITr+5RCPXfvvlGrM/+B6w=
|
||||
github.com/jhump/protoreflect/v2 v2.0.0-beta.1/go.mod h1:D9LBEowZyv8/iSu97FU2zmXG3JxVTmNw21mu63niFzU=
|
||||
github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94=
|
||||
github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
@@ -107,8 +107,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
@@ -127,31 +125,29 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/modelcontextprotocol/go-sdk v1.4.0 h1:u0kr8lbJc1oBcawK7Df+/ajNMpIDFE41OEPxdeTLOn8=
|
||||
github.com/modelcontextprotocol/go-sdk v1.4.0/go.mod h1:Nxc2n+n/GdCebUaqCOhTetptS17SXXNu9IfNTaLDi1E=
|
||||
github.com/modelcontextprotocol/go-sdk v1.2.0 h1:Y23co09300CEk8iZ/tMxIX1dVmKZkzoSBZOpJwUnc/s=
|
||||
github.com/modelcontextprotocol/go-sdk v1.2.0/go.mod h1:6fM3LCm3yV7pAs8isnKLn07oKtB0MP9LHd3DfAcKw10=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
|
||||
github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14=
|
||||
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
@@ -162,24 +158,16 @@ github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9Z
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs=
|
||||
github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0=
|
||||
github.com/redis/go-redis/v9 v9.17.3 h1:fN29NdNrE17KttK5Ndf20buqfDZwGNgoUr9qjl1DQx4=
|
||||
github.com/redis/go-redis/v9 v9.17.3/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/robertkrimen/otto v0.2.1 h1:FVP0PJ0AHIjC+N4pKCG9yCDz6LHNPCwi/GKID5pGGF0=
|
||||
github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//farB5FlRY=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc=
|
||||
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
|
||||
github.com/segmentio/encoding v0.5.3 h1:OjMgICtcSFuNvQCdwqMCv9Tg7lEOXGwm1J5RPQccx6w=
|
||||
github.com/segmentio/encoding v0.5.3/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -192,10 +180,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/titanous/json5 v1.0.0 h1:hJf8Su1d9NuI/ffpxgxQfxh/UiBFZX7bMPid0rIL/7s=
|
||||
github.com/titanous/json5 v1.0.0/go.mod h1:7JH1M8/LHKc6cyP5o5g3CSaRj+mBrIimTxzpvmckH8c=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||
@@ -211,42 +195,36 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
|
||||
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
go.etcd.io/etcd/api/v3 v3.5.21 h1:A6O2/JDb3tvHhiIz3xf9nJ7REHvtEFJJ3veW3FbCnS8=
|
||||
go.etcd.io/etcd/api/v3 v3.5.21/go.mod h1:c3aH5wcvXv/9dqIw2Y810LDXJfhSYdHQ0vxmP3CCHVY=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.21 h1:lPBu71Y7osQmzlflM9OfeIV2JlmpBjqBNlLtcoBqUTc=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.21/go.mod h1:BgqT/IXPjK9NkeSDjbzwsHySX3yIle2+ndz28nVsjUs=
|
||||
go.etcd.io/etcd/client/v3 v3.5.21 h1:T6b1Ow6fNjOLOtM0xSoKNQt1ASPCLWrF9XMHcH9pEyY=
|
||||
go.etcd.io/etcd/client/v3 v3.5.21/go.mod h1:mFYy67IOqmbRf/kRUvsHixzo3iG+1OF2W2+jVIQRAnU=
|
||||
go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk=
|
||||
go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
|
||||
go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
|
||||
go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.40.0 h1:zu+I4j+FdO6xIxBVPeuncQVbjxUM4LiMgv6GwGe9REE=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.40.0/go.mod h1:zS6cC4nFBYXbu18e7aLfMzubBjOiN7ZcROu477qtMf8=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -257,16 +235,14 @@ go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
@@ -276,16 +252,16 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -295,76 +271,69 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
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.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
|
||||
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4=
|
||||
k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk=
|
||||
k8s.io/apimachinery v0.36.0 h1:jZyPzhd5Z+3h9vJLt0z9XdzW9VzNzWAUw+P1xZ9PXtQ=
|
||||
k8s.io/apimachinery v0.36.0/go.mod h1:FklypaRJt6n5wUIwWXIP6GJlIpUizTgfo1T/As+Tyxc=
|
||||
k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A=
|
||||
k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM=
|
||||
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
|
||||
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
|
||||
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg=
|
||||
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0=
|
||||
k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 h1:kBawHLSnx/mYHmRnNUf9d4CpjREbeZuxoSGOX/J+aYM=
|
||||
k8s.io/utils v0.0.0-20260319190234-28399d86e0b5/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
|
||||
k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
|
||||
k8s.io/apimachinery v0.29.4 h1:RaFdJiDmuKs/8cm1M6Dh1Kvyh59YQFDcFuFTSmXes6Q=
|
||||
k8s.io/apimachinery v0.29.4/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y=
|
||||
k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
|
||||
k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0=
|
||||
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
|
||||
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
|
||||
k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 h1:OfgiEo21hGiwx1oJUU5MpEaeOEg6coWndBkZF/lkFuE=
|
||||
k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
@@ -3,63 +3,12 @@ package encoding
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/titanous/json5"
|
||||
"github.com/zeromicro/go-zero/core/lang"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Json5ToJson converts JSON5 data into its JSON representation.
|
||||
func Json5ToJson(data []byte) ([]byte, error) {
|
||||
var val any
|
||||
if err := json5.Unmarshal(data, &val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate that there are no unsupported values like Infinity or NaN
|
||||
if err := validateJSONCompatible(val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return encodeToJSON(val)
|
||||
}
|
||||
|
||||
// validateJSONCompatible checks if the value can be represented in standard JSON.
|
||||
// JSON5 allows Infinity and NaN, but standard JSON does not support these values.
|
||||
func validateJSONCompatible(val any) error {
|
||||
switch v := val.(type) {
|
||||
case float64:
|
||||
if math.IsInf(v, 0) {
|
||||
return fmt.Errorf("JSON5 value Infinity cannot be represented in standard JSON")
|
||||
}
|
||||
if math.IsNaN(v) {
|
||||
return fmt.Errorf("JSON5 value NaN cannot be represented in standard JSON")
|
||||
}
|
||||
case []any:
|
||||
for _, item := range v {
|
||||
if err := validateJSONCompatible(item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case map[string]any:
|
||||
for _, value := range v {
|
||||
if err := validateJSONCompatible(value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case map[any]any:
|
||||
for _, value := range v {
|
||||
if err := validateJSONCompatible(value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TomlToJson converts TOML data into its JSON representation.
|
||||
func TomlToJson(data []byte) ([]byte, error) {
|
||||
var val any
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -117,142 +116,3 @@ func TestYamlToJsonSlice(t *testing.T) {
|
||||
assert.Equal(t, `{"foo":["bar","baz"]}
|
||||
`, string(b))
|
||||
}
|
||||
|
||||
func TestJson5ToJson(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "standard json",
|
||||
input: `{"a":"foo","b":1,"c":"${FOO}","d":"abcd!@#$112"}`,
|
||||
expect: "{\"a\":\"foo\",\"b\":1,\"c\":\"${FOO}\",\"d\":\"abcd!@#$112\"}\n",
|
||||
},
|
||||
{
|
||||
name: "json5 with comments",
|
||||
input: `{/*comment*/"a":"foo","b":1}`,
|
||||
expect: "{\"a\":\"foo\",\"b\":1}\n",
|
||||
},
|
||||
{
|
||||
name: "json5 with trailing commas",
|
||||
input: `{"a":"foo","b":1,}`,
|
||||
expect: "{\"a\":\"foo\",\"b\":1}\n",
|
||||
},
|
||||
{
|
||||
name: "json5 with unquoted keys",
|
||||
input: `{a:"foo",b:1}`,
|
||||
expect: "{\"a\":\"foo\",\"b\":1}\n",
|
||||
},
|
||||
{
|
||||
name: "json5 with single quotes",
|
||||
input: `{"a":'foo',"b":1}`,
|
||||
expect: "{\"a\":\"foo\",\"b\":1}\n",
|
||||
},
|
||||
{
|
||||
name: "json5 with line comments",
|
||||
input: "{\n// This is a comment\n\"a\":\"foo\",\n\"b\":1\n}",
|
||||
expect: "{\"a\":\"foo\",\"b\":1}\n",
|
||||
},
|
||||
{
|
||||
name: "json5 all features combined",
|
||||
input: "{\n// comment\na: 'foo', // trailing comma\nb: 1,\n}",
|
||||
expect: "{\"a\":\"foo\",\"b\":1}\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
got, err := Json5ToJson([]byte(test.input))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.expect, string(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJson5ToJsonError(t *testing.T) {
|
||||
// Invalid JSON5: unquoted string value
|
||||
_, err := Json5ToJson([]byte("{a: foo}"))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestJson5ToJsonInfinity(t *testing.T) {
|
||||
// JSON5 allows Infinity but standard JSON does not
|
||||
_, err := Json5ToJson([]byte(`{value: Infinity}`))
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "Infinity")
|
||||
|
||||
// Negative infinity
|
||||
_, err = Json5ToJson([]byte(`{value: -Infinity}`))
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "Infinity")
|
||||
|
||||
// Infinity in array
|
||||
_, err = Json5ToJson([]byte(`{values: [1, Infinity, 3]}`))
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "Infinity")
|
||||
}
|
||||
|
||||
func TestJson5ToJsonNaN(t *testing.T) {
|
||||
// JSON5 allows NaN but standard JSON does not
|
||||
_, err := Json5ToJson([]byte(`{value: NaN}`))
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "NaN")
|
||||
|
||||
// NaN in nested structure
|
||||
_, err = Json5ToJson([]byte(`{nested: {value: NaN}}`))
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "NaN")
|
||||
}
|
||||
|
||||
func TestJson5ToJsonSlice(t *testing.T) {
|
||||
b, err := Json5ToJson([]byte(`{
|
||||
// comment
|
||||
foo: [
|
||||
'bar',
|
||||
"baz", // trailing comma
|
||||
],
|
||||
}`))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `{"foo":["bar","baz"]}
|
||||
`, string(b))
|
||||
}
|
||||
|
||||
func TestValidateJSONCompatible(t *testing.T) {
|
||||
// Test float64 types
|
||||
assert.NoError(t, validateJSONCompatible(float64(1.5)))
|
||||
assert.Error(t, validateJSONCompatible(math.Inf(1)))
|
||||
assert.Error(t, validateJSONCompatible(math.Inf(-1)))
|
||||
assert.Error(t, validateJSONCompatible(math.NaN()))
|
||||
|
||||
// Test arrays with invalid values
|
||||
assert.Error(t, validateJSONCompatible([]any{1, math.Inf(1), 3}))
|
||||
assert.Error(t, validateJSONCompatible([]any{1, math.NaN(), 3}))
|
||||
assert.NoError(t, validateJSONCompatible([]any{1, 2, 3}))
|
||||
|
||||
// Test map[string]any with invalid values
|
||||
assert.Error(t, validateJSONCompatible(map[string]any{"value": math.Inf(1)}))
|
||||
assert.Error(t, validateJSONCompatible(map[string]any{"value": math.NaN()}))
|
||||
assert.NoError(t, validateJSONCompatible(map[string]any{"value": 1.5}))
|
||||
|
||||
// Test map[any]any with invalid values
|
||||
assert.Error(t, validateJSONCompatible(map[any]any{"value": math.Inf(1)}))
|
||||
assert.Error(t, validateJSONCompatible(map[any]any{"value": math.NaN()}))
|
||||
assert.NoError(t, validateJSONCompatible(map[any]any{"value": 1.5}))
|
||||
|
||||
// Test nested structures
|
||||
assert.Error(t, validateJSONCompatible(map[string]any{
|
||||
"nested": map[string]any{"value": math.Inf(1)},
|
||||
}))
|
||||
assert.Error(t, validateJSONCompatible([]any{
|
||||
map[string]any{"value": math.NaN()},
|
||||
}))
|
||||
|
||||
// Test valid values of various types
|
||||
assert.NoError(t, validateJSONCompatible("string"))
|
||||
assert.NoError(t, validateJSONCompatible(42))
|
||||
assert.NoError(t, validateJSONCompatible(true))
|
||||
assert.NoError(t, validateJSONCompatible(nil))
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package mcp
|
||||
|
||||
import "net/http"
|
||||
|
||||
// RequestMetadataExtractor extracts request metadata for downstream handlers.
|
||||
type RequestMetadataExtractor func(*http.Request) RequestMetadata
|
||||
|
||||
// McpOption customizes MCP server construction.
|
||||
type McpOption interface {
|
||||
apply(*serverOptions)
|
||||
}
|
||||
|
||||
type mcpOptionFunc func(*serverOptions)
|
||||
|
||||
func (f mcpOptionFunc) apply(opts *serverOptions) {
|
||||
f(opts)
|
||||
}
|
||||
|
||||
type serverOptions struct {
|
||||
requestMetadataExtractor RequestMetadataExtractor
|
||||
}
|
||||
|
||||
func defaultServerOptions() serverOptions {
|
||||
return serverOptions{}
|
||||
}
|
||||
|
||||
// WithRequestMetadataExtractor installs an extractor that runs for each incoming
|
||||
// MCP HTTP request, and stores the extracted metadata into handler context.
|
||||
func WithRequestMetadataExtractor(extractor RequestMetadataExtractor) McpOption {
|
||||
return mcpOptionFunc(func(opts *serverOptions) {
|
||||
opts.requestMetadataExtractor = extractor
|
||||
})
|
||||
}
|
||||
@@ -15,7 +15,6 @@ This package provides a go-zero integration for the [Model Context Protocol (MCP
|
||||
- **CORS Support**: Configurable CORS settings for cross-origin requests
|
||||
- **Type-Safe Tool Handlers**: Generic tool handlers with automatic JSON schema generation
|
||||
- **Prompts and Resources**: Full support for MCP prompts and resources
|
||||
- **Request Metadata Bridge**: Optional request metadata extraction into handler context
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -221,35 +220,6 @@ mcp:
|
||||
messageEndpoint: /message
|
||||
```
|
||||
|
||||
## Request Metadata Bridge
|
||||
|
||||
For multi-tenant or request-context-aware tools, you can extract selected HTTP request metadata once at the transport boundary and read it from `context.Context` in handlers.
|
||||
|
||||
```go
|
||||
server := mcp.NewMcpServerWithOptions(c,
|
||||
mcp.WithRequestMetadataExtractor(mcp.DefaultRequestMetadataExtractor),
|
||||
)
|
||||
|
||||
handler := func(ctx context.Context, req *mcp.CallToolRequest, args SomeArgs) (*mcp.CallToolResult, any, error) {
|
||||
tenant, _ := mcp.HeaderFromContext(ctx, "X-Tenant-Id")
|
||||
traceID, _ := mcp.QueryFromContext(ctx, "trace")
|
||||
scope, _ := mcp.PathFromContext(ctx, "scope")
|
||||
|
||||
_ = tenant
|
||||
_ = traceID
|
||||
_ = scope
|
||||
|
||||
return &mcp.CallToolResult{}, nil, nil
|
||||
}
|
||||
```
|
||||
|
||||
Available helpers:
|
||||
|
||||
- `RequestMetadataFromContext(ctx)`
|
||||
- `HeaderFromContext(ctx, key)`
|
||||
- `QueryFromContext(ctx, key)`
|
||||
- `PathFromContext(ctx, key)`
|
||||
|
||||
## Configuration Options
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/pathvar"
|
||||
)
|
||||
|
||||
// RequestMetadata carries selected request-scoped values into MCP handlers.
|
||||
type RequestMetadata struct {
|
||||
Headers map[string][]string
|
||||
Query map[string][]string
|
||||
Path map[string]string
|
||||
}
|
||||
|
||||
type requestMetadataCtxKey struct{}
|
||||
|
||||
// RequestMetadataFromContext returns metadata extracted at the transport boundary.
|
||||
func RequestMetadataFromContext(ctx context.Context) (RequestMetadata, bool) {
|
||||
metadata, ok := requestMetadataFromContext(ctx)
|
||||
if !ok {
|
||||
return RequestMetadata{}, false
|
||||
}
|
||||
|
||||
return normalizeRequestMetadata(metadata), true
|
||||
}
|
||||
|
||||
// HeaderFromContext returns the first header value for key.
|
||||
func HeaderFromContext(ctx context.Context, key string) (string, bool) {
|
||||
metadata, ok := requestMetadataFromContext(ctx)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
vals := metadata.Headers[http.CanonicalHeaderKey(key)]
|
||||
if len(vals) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return vals[0], true
|
||||
}
|
||||
|
||||
// QueryFromContext returns the first query value for key.
|
||||
func QueryFromContext(ctx context.Context, key string) (string, bool) {
|
||||
metadata, ok := requestMetadataFromContext(ctx)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
vals := metadata.Query[key]
|
||||
if len(vals) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return vals[0], true
|
||||
}
|
||||
|
||||
// PathFromContext returns the path variable value for key.
|
||||
func PathFromContext(ctx context.Context, key string) (string, bool) {
|
||||
metadata, ok := requestMetadataFromContext(ctx)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
val, ok := metadata.Path[key]
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return val, true
|
||||
}
|
||||
|
||||
func requestMetadataFromContext(ctx context.Context) (RequestMetadata, bool) {
|
||||
metadata, ok := ctx.Value(requestMetadataCtxKey{}).(RequestMetadata)
|
||||
if !ok {
|
||||
return RequestMetadata{}, false
|
||||
}
|
||||
|
||||
return metadata, true
|
||||
}
|
||||
|
||||
// DefaultRequestMetadataExtractor extracts headers, query values, and path variables.
|
||||
func DefaultRequestMetadataExtractor(r *http.Request) RequestMetadata {
|
||||
metadata := RequestMetadata{
|
||||
Headers: make(map[string][]string, len(r.Header)),
|
||||
Query: make(map[string][]string),
|
||||
Path: clonePathVars(pathvar.Vars(r)),
|
||||
}
|
||||
|
||||
for key, vals := range r.Header {
|
||||
metadata.Headers[http.CanonicalHeaderKey(key)] = append([]string(nil), vals...)
|
||||
}
|
||||
|
||||
if r.URL != nil {
|
||||
for key, vals := range r.URL.Query() {
|
||||
metadata.Query[key] = append([]string(nil), vals...)
|
||||
}
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
||||
func normalizeRequestMetadata(metadata RequestMetadata) RequestMetadata {
|
||||
return RequestMetadata{
|
||||
Headers: cloneCanonicalHeaderValues(metadata.Headers),
|
||||
Query: cloneHeaderValues(metadata.Query),
|
||||
Path: clonePathVars(metadata.Path),
|
||||
}
|
||||
}
|
||||
|
||||
func cloneHeaderValues(values map[string][]string) map[string][]string {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
cloned := make(map[string][]string, len(values))
|
||||
for key, vals := range values {
|
||||
cloned[key] = append([]string(nil), vals...)
|
||||
}
|
||||
|
||||
return cloned
|
||||
}
|
||||
|
||||
func cloneCanonicalHeaderValues(values map[string][]string) map[string][]string {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
cloned := make(map[string][]string, len(values))
|
||||
for key, vals := range values {
|
||||
canonical := http.CanonicalHeaderKey(key)
|
||||
cloned[canonical] = append(cloned[canonical], vals...)
|
||||
}
|
||||
|
||||
return cloned
|
||||
}
|
||||
|
||||
func clonePathVars(values map[string]string) map[string]string {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
cloned := make(map[string]string, len(values))
|
||||
for key, val := range values {
|
||||
cloned[key] = val
|
||||
}
|
||||
|
||||
return cloned
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/rest/pathvar"
|
||||
)
|
||||
|
||||
func TestDefaultRequestMetadataExtractor(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/sse?tenant=t1&trace=abc", nil)
|
||||
req.Header.Add("X-Tenant-Id", "tenant-from-header")
|
||||
req = pathvar.WithVars(req, map[string]string{"tool": "sum"})
|
||||
|
||||
metadata := DefaultRequestMetadataExtractor(req)
|
||||
header, ok := metadata.Headers["X-Tenant-Id"]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, []string{"tenant-from-header"}, header)
|
||||
assert.Equal(t, []string{"t1"}, metadata.Query["tenant"])
|
||||
assert.Equal(t, "sum", metadata.Path["tool"])
|
||||
}
|
||||
|
||||
func TestRequestMetadataContextHelpers(t *testing.T) {
|
||||
ctx := context.WithValue(context.Background(), requestMetadataCtxKey{}, RequestMetadata{
|
||||
Headers: map[string][]string{"X-Trace-Id": {"trace-1"}},
|
||||
Query: map[string][]string{"tenant": {"foo"}},
|
||||
Path: map[string]string{"scope": "prod"},
|
||||
})
|
||||
|
||||
metadata, ok := RequestMetadataFromContext(ctx)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, []string{"trace-1"}, metadata.Headers["X-Trace-Id"])
|
||||
|
||||
header, ok := HeaderFromContext(ctx, "x-trace-id")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "trace-1", header)
|
||||
|
||||
query, ok := QueryFromContext(ctx, "tenant")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "foo", query)
|
||||
|
||||
path, ok := PathFromContext(ctx, "scope")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "prod", path)
|
||||
}
|
||||
|
||||
func TestRequestMetadataContextHelpersMissingKeys(t *testing.T) {
|
||||
ctx := context.WithValue(context.Background(), requestMetadataCtxKey{}, RequestMetadata{
|
||||
Headers: map[string][]string{"X-Trace-Id": {"trace-1"}},
|
||||
Query: map[string][]string{"tenant": {"foo"}},
|
||||
Path: map[string]string{"scope": "prod"},
|
||||
})
|
||||
|
||||
_, ok := HeaderFromContext(ctx, "x-missing")
|
||||
assert.False(t, ok)
|
||||
|
||||
_, ok = QueryFromContext(ctx, "missing")
|
||||
assert.False(t, ok)
|
||||
|
||||
_, ok = PathFromContext(ctx, "missing")
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestRequestMetadataFromContextNotFound(t *testing.T) {
|
||||
_, ok := RequestMetadataFromContext(context.Background())
|
||||
assert.False(t, ok)
|
||||
|
||||
_, ok = HeaderFromContext(context.Background(), "x-test")
|
||||
assert.False(t, ok)
|
||||
|
||||
_, ok = QueryFromContext(context.Background(), "tenant")
|
||||
assert.False(t, ok)
|
||||
|
||||
_, ok = PathFromContext(context.Background(), "tenant")
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestWrapRequestMetadata(t *testing.T) {
|
||||
s := &mcpServerImpl{
|
||||
options: serverOptions{
|
||||
requestMetadataExtractor: DefaultRequestMetadataExtractor,
|
||||
},
|
||||
}
|
||||
|
||||
called := false
|
||||
handler := s.wrapRequestMetadata(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
header, ok := HeaderFromContext(r.Context(), "x-tenant-id")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "tenant-1", header)
|
||||
|
||||
query, ok := QueryFromContext(r.Context(), "tenant")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "q-tenant", query)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/sse?tenant=q-tenant", nil)
|
||||
req.Header.Set("X-Tenant-Id", "tenant-1")
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.True(t, called)
|
||||
}
|
||||
|
||||
func TestWrapRequestMetadataNoExtractor(t *testing.T) {
|
||||
s := &mcpServerImpl{}
|
||||
|
||||
called := false
|
||||
handler := s.wrapRequestMetadata(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
_, ok := RequestMetadataFromContext(r.Context())
|
||||
assert.False(t, ok)
|
||||
}))
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/sse", nil))
|
||||
|
||||
assert.True(t, called)
|
||||
}
|
||||
|
||||
func TestWrapRequestMetadataCanonicalizesCustomHeaders(t *testing.T) {
|
||||
s := &mcpServerImpl{
|
||||
options: serverOptions{
|
||||
requestMetadataExtractor: func(*http.Request) RequestMetadata {
|
||||
return RequestMetadata{
|
||||
Headers: map[string][]string{
|
||||
"x-tenant-id": {"tenant-lower"},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
called := false
|
||||
handler := s.wrapRequestMetadata(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
header, ok := HeaderFromContext(r.Context(), "X-Tenant-Id")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "tenant-lower", header)
|
||||
}))
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/sse", nil))
|
||||
|
||||
assert.True(t, called)
|
||||
}
|
||||
|
||||
func TestRequestMetadataFromContextReturnsCopy(t *testing.T) {
|
||||
ctx := context.WithValue(context.Background(), requestMetadataCtxKey{}, RequestMetadata{
|
||||
Headers: map[string][]string{"X-Trace-Id": {"trace-1"}},
|
||||
})
|
||||
|
||||
metadata, ok := RequestMetadataFromContext(ctx)
|
||||
assert.True(t, ok)
|
||||
metadata.Headers["X-Trace-Id"][0] = "mutated"
|
||||
metadata.Headers["X-New"] = []string{"new"}
|
||||
|
||||
fresh, ok := RequestMetadataFromContext(ctx)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, []string{"trace-1"}, fresh.Headers["X-Trace-Id"])
|
||||
assert.Nil(t, fresh.Headers["X-New"])
|
||||
}
|
||||
|
||||
func TestRequestMetadataFromContextWithEmptyAndCanonicalizedHeaders(t *testing.T) {
|
||||
emptyCtx := context.WithValue(context.Background(), requestMetadataCtxKey{}, RequestMetadata{})
|
||||
empty, ok := RequestMetadataFromContext(emptyCtx)
|
||||
assert.True(t, ok)
|
||||
assert.Nil(t, empty.Headers)
|
||||
assert.Nil(t, empty.Query)
|
||||
assert.Nil(t, empty.Path)
|
||||
|
||||
ctx := context.WithValue(context.Background(), requestMetadataCtxKey{}, RequestMetadata{
|
||||
Headers: map[string][]string{
|
||||
"x-tenant-id": {"a"},
|
||||
"X-Tenant-Id": {"b"},
|
||||
},
|
||||
})
|
||||
|
||||
metadata, ok := RequestMetadataFromContext(ctx)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, []string{"a", "b"}, metadata.Headers["X-Tenant-Id"])
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
sdkmcp "github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
@@ -21,23 +20,10 @@ type mcpServerImpl struct {
|
||||
conf McpConf
|
||||
httpServer *rest.Server
|
||||
mcpServer *sdkmcp.Server
|
||||
options serverOptions
|
||||
}
|
||||
|
||||
// NewMcpServer creates a new MCP server using the official SDK
|
||||
func NewMcpServer(c McpConf) McpServer {
|
||||
return NewMcpServerWithOptions(c)
|
||||
}
|
||||
|
||||
// NewMcpServerWithOptions creates a new MCP server with optional customizations.
|
||||
func NewMcpServerWithOptions(c McpConf, opts ...McpOption) McpServer {
|
||||
serverOpts := defaultServerOptions()
|
||||
for _, opt := range opts {
|
||||
if opt != nil {
|
||||
opt.apply(&serverOpts)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the underlying rest HTTP server
|
||||
var httpServer *rest.Server
|
||||
if len(c.Mcp.Cors) == 0 {
|
||||
@@ -66,7 +52,6 @@ func NewMcpServerWithOptions(c McpConf, opts ...McpOption) McpServer {
|
||||
conf: c,
|
||||
httpServer: httpServer,
|
||||
mcpServer: mcpServer,
|
||||
options: serverOpts,
|
||||
}
|
||||
|
||||
// Choose transport based on configuration
|
||||
@@ -100,7 +85,7 @@ func (s *mcpServerImpl) setupSSETransport() {
|
||||
return s.mcpServer
|
||||
}, nil)
|
||||
|
||||
s.registerRoutes(s.wrapRequestMetadata(handler), s.conf.Mcp.SseEndpoint)
|
||||
s.registerRoutes(handler, s.conf.Mcp.SseEndpoint)
|
||||
}
|
||||
|
||||
// setupStreamableTransport configures the server to use Streamable HTTP transport (2025-03-26 spec)
|
||||
@@ -111,7 +96,7 @@ func (s *mcpServerImpl) setupStreamableTransport() {
|
||||
return s.mcpServer
|
||||
}, nil)
|
||||
|
||||
s.registerRoutes(s.wrapRequestMetadata(handler), s.conf.Mcp.MessageEndpoint)
|
||||
s.registerRoutes(handler, s.conf.Mcp.MessageEndpoint)
|
||||
}
|
||||
|
||||
func (s *mcpServerImpl) registerRoutes(handler http.Handler, endpoint string) {
|
||||
@@ -128,16 +113,3 @@ func (s *mcpServerImpl) registerRoutes(handler http.Handler, endpoint string) {
|
||||
Handler: handler.ServeHTTP,
|
||||
}, rest.WithTimeout(s.conf.Mcp.MessageTimeout))
|
||||
}
|
||||
|
||||
func (s *mcpServerImpl) wrapRequestMetadata(next http.Handler) http.Handler {
|
||||
extractor := s.options.requestMetadataExtractor
|
||||
if extractor == nil {
|
||||
return next
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
metadata := normalizeRequestMetadata(extractor(r))
|
||||
ctx := context.WithValue(r.Context(), requestMetadataCtxKey{}, metadata)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,14 +3,11 @@ package mcp
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdkmcp "github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/core/conf"
|
||||
)
|
||||
@@ -394,148 +391,3 @@ func TestAddToolWithCustomServer(t *testing.T) {
|
||||
return nil, nil, nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequestMetadataIntegrationSSEToolCall(t *testing.T) {
|
||||
port := getFreePort(t)
|
||||
|
||||
c := McpConf{}
|
||||
c.Host = "127.0.0.1"
|
||||
c.Port = port
|
||||
c.Mcp.Name = "metadata-integration-test"
|
||||
c.Mcp.UseStreamable = false
|
||||
c.Mcp.SseEndpoint = "/sse/:scope"
|
||||
c.Mcp.MessageTimeout = 2 * time.Second
|
||||
c.Mcp.SseTimeout = 2 * time.Second
|
||||
|
||||
server := NewMcpServerWithOptions(c, WithRequestMetadataExtractor(DefaultRequestMetadataExtractor))
|
||||
|
||||
tool := &Tool{
|
||||
Name: "inspect_metadata",
|
||||
Description: "Inspect metadata in handler context",
|
||||
}
|
||||
|
||||
type Args struct{}
|
||||
|
||||
AddTool(server, tool, func(ctx context.Context, req *CallToolRequest, args Args) (*CallToolResult, any, error) {
|
||||
header, ok := HeaderFromContext(ctx, "x-tenant-id")
|
||||
if !ok || header != "tenant-header" {
|
||||
return nil, nil, fmt.Errorf("unexpected header from context: %q", header)
|
||||
}
|
||||
|
||||
query, ok := QueryFromContext(ctx, "tenant")
|
||||
if !ok || query != "tenant-query" {
|
||||
return nil, nil, fmt.Errorf("unexpected query from context: %q", query)
|
||||
}
|
||||
|
||||
scope, ok := PathFromContext(ctx, "scope")
|
||||
if !ok || scope != "prod" {
|
||||
return nil, nil, fmt.Errorf("unexpected path from context: %q", scope)
|
||||
}
|
||||
|
||||
return &CallToolResult{
|
||||
Content: []Content{&TextContent{Text: "metadata-ok"}},
|
||||
}, nil, nil
|
||||
})
|
||||
|
||||
go server.Start()
|
||||
t.Cleanup(server.Stop)
|
||||
|
||||
baseURL := fmt.Sprintf("http://127.0.0.1:%d/sse/prod?tenant=tenant-query", port)
|
||||
waitForServerReady(t, baseURL, 2*time.Second)
|
||||
|
||||
client := sdkmcp.NewClient(&sdkmcp.Implementation{
|
||||
Name: "metadata-client",
|
||||
Version: "1.0.0",
|
||||
}, nil)
|
||||
|
||||
httpClient := &http.Client{
|
||||
Timeout: 2 * time.Second,
|
||||
Transport: metadataHeaderRoundTripper{
|
||||
next: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
|
||||
transport := &sdkmcp.SSEClientTransport{
|
||||
Endpoint: baseURL,
|
||||
HTTPClient: httpClient,
|
||||
}
|
||||
|
||||
session, err := client.Connect(context.Background(), transport, nil)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = session.Close()
|
||||
})
|
||||
|
||||
res, err := session.CallTool(context.Background(), &sdkmcp.CallToolParams{
|
||||
Name: "inspect_metadata",
|
||||
Arguments: map[string]any{},
|
||||
})
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.NotNil(t, res) {
|
||||
return
|
||||
}
|
||||
assert.False(t, res.IsError)
|
||||
}
|
||||
|
||||
type metadataHeaderRoundTripper struct {
|
||||
next http.RoundTripper
|
||||
}
|
||||
|
||||
func (r metadataHeaderRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
next := r.next
|
||||
if next == nil {
|
||||
next = http.DefaultTransport
|
||||
}
|
||||
|
||||
clone := req.Clone(req.Context())
|
||||
clone.Header.Set("X-Tenant-Id", "tenant-header")
|
||||
return next.RoundTrip(clone)
|
||||
}
|
||||
|
||||
func getFreePort(t *testing.T) int {
|
||||
t.Helper()
|
||||
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if !assert.NoError(t, err) {
|
||||
return 0
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
addr, ok := listener.Addr().(*net.TCPAddr)
|
||||
if !assert.True(t, ok) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return addr.Port
|
||||
}
|
||||
|
||||
func waitForServerReady(t *testing.T, endpoint string, timeout time.Duration) {
|
||||
t.Helper()
|
||||
|
||||
client := &http.Client{Timeout: 200 * time.Millisecond}
|
||||
deadline := time.Now().Add(timeout)
|
||||
for time.Now().Before(deadline) {
|
||||
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to build readiness request: %v", err)
|
||||
}
|
||||
req.Header.Set("Accept", "text/event-stream")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err == nil {
|
||||
_ = resp.Body.Close()
|
||||
if resp.StatusCode > 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
|
||||
t.Fatalf("server did not become ready for %s within %s", endpoint, timeout)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/zeromicro/go-zero/core/logc"
|
||||
@@ -99,10 +100,17 @@ func WithUnauthorizedCallback(callback UnauthorizedCallback) AuthorizeOption {
|
||||
|
||||
func detailAuthLog(r *http.Request, reason string) {
|
||||
// discard dump error, only for debug purpose
|
||||
details, _ := httputil.DumpRequest(r, true)
|
||||
// Skip dumping request body for multipart/form-data to avoid reading large files
|
||||
dumpBody := !isMultipartFormData(r)
|
||||
details, _ := httputil.DumpRequest(r, dumpBody)
|
||||
logc.Errorf(r.Context(), "authorize failed: %s\n=> %+v", reason, string(details))
|
||||
}
|
||||
|
||||
func isMultipartFormData(r *http.Request) bool {
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
return strings.Contains(contentType, "multipart/form-data")
|
||||
}
|
||||
|
||||
func unauthorized(w http.ResponseWriter, r *http.Request, err error, callback UnauthorizedCallback) {
|
||||
writer := response.NewHeaderOnceResponseWriter(w)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -90,6 +91,128 @@ func TestAuthHandler_NilError(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuthHandlerWithJSONBody(t *testing.T) {
|
||||
const key = "B63F477D-BBA3-4E52-96D3-C0034C27694A"
|
||||
|
||||
// Create a request with JSON body
|
||||
jsonBody := `{"username":"test","password":"secret"}`
|
||||
req := httptest.NewRequest(http.MethodPost, "http://localhost/login",
|
||||
strings.NewReader(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
// Missing authorization header to trigger the unauthorized path
|
||||
|
||||
handler := Authorize(key)(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
handler.ServeHTTP(resp, req)
|
||||
|
||||
// Should return unauthorized
|
||||
assert.Equal(t, http.StatusUnauthorized, resp.Code)
|
||||
}
|
||||
|
||||
func TestAuthHandlerWithMultipartFormData(t *testing.T) {
|
||||
const key = "B63F477D-BBA3-4E52-96D3-C0034C27694A"
|
||||
|
||||
// Create a multipart form-data request
|
||||
// We don't need actual body content since we're testing that
|
||||
// the body is NOT read when Content-Type is multipart/form-data
|
||||
req := httptest.NewRequest(http.MethodPost, "http://localhost/upload",
|
||||
http.NoBody)
|
||||
req.Header.Set("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundary")
|
||||
// Missing authorization header to trigger the unauthorized path
|
||||
|
||||
handler := Authorize(key)(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
handler.ServeHTTP(resp, req)
|
||||
|
||||
// Should return unauthorized
|
||||
assert.Equal(t, http.StatusUnauthorized, resp.Code)
|
||||
}
|
||||
|
||||
func TestAuthHandlerWithMultipartFormDataLargeFile(t *testing.T) {
|
||||
const key = "B63F477D-BBA3-4E52-96D3-C0034C27694A"
|
||||
|
||||
// Create a multipart form-data request with a simulated large file
|
||||
// This tests that the body is NOT consumed when Content-Type is multipart/form-data
|
||||
largeContent := make([]byte, 1024*1024) // 1MB of data
|
||||
for i := range largeContent {
|
||||
largeContent[i] = byte(i % 256)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "http://localhost/upload",
|
||||
http.NoBody)
|
||||
req.Header.Set("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundary")
|
||||
req.Header.Set("Content-Length", "1048576")
|
||||
// Missing authorization header to trigger the unauthorized path
|
||||
|
||||
handler := Authorize(key)(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
// This should complete quickly without reading the body
|
||||
start := time.Now()
|
||||
handler.ServeHTTP(resp, req)
|
||||
elapsed := time.Since(start)
|
||||
|
||||
// Should return unauthorized
|
||||
assert.Equal(t, http.StatusUnauthorized, resp.Code)
|
||||
// Should complete in less than 100ms (without reading 1MB of data)
|
||||
assert.Less(t, elapsed, 100*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestIsMultipartFormData(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
contentType string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "multipart/form-data",
|
||||
contentType: "multipart/form-data",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "multipart/form-data with boundary",
|
||||
contentType: "multipart/form-data; boundary=----WebKitFormBoundary",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "application/json",
|
||||
contentType: "application/json",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "application/x-www-form-urlencoded",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "empty content type",
|
||||
contentType: "",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "http://localhost", http.NoBody)
|
||||
req.Header.Set("Content-Type", tt.contentType)
|
||||
result := isMultipartFormData(req)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func buildToken(secretKey string, payloads map[string]any, seconds int64) (string, error) {
|
||||
now := time.Now().Unix()
|
||||
claims := make(jwt.MapClaims)
|
||||
|
||||
@@ -84,11 +84,8 @@ func buildRequest(ctx context.Context, method, url string, data any) (*http.Requ
|
||||
var reader io.Reader
|
||||
jsonVars, hasJsonBody := val[jsonKey]
|
||||
if hasJsonBody {
|
||||
switch method {
|
||||
case http.MethodGet:
|
||||
if method == http.MethodGet {
|
||||
return nil, ErrGetWithBody
|
||||
case http.MethodHead:
|
||||
return nil, ErrHeadWithBody
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
@@ -229,106 +229,3 @@ func TestDo_WithClientHttpTrace(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, enter)
|
||||
}
|
||||
|
||||
func TestBuildRequestWithBody(t *testing.T) {
|
||||
testBody := struct {
|
||||
Key string `json:"key"`
|
||||
Value int `json:"value"`
|
||||
}{
|
||||
Key: "foo",
|
||||
Value: 10,
|
||||
}
|
||||
|
||||
testcases := []struct {
|
||||
testName string
|
||||
method string
|
||||
url string
|
||||
body any
|
||||
wantedErr error
|
||||
}{
|
||||
{
|
||||
testName: "GET Request with Body",
|
||||
method: http.MethodGet,
|
||||
url: "/ping",
|
||||
body: testBody,
|
||||
wantedErr: ErrGetWithBody,
|
||||
},
|
||||
{
|
||||
testName: "GET Request without Body",
|
||||
method: http.MethodGet,
|
||||
url: "/ping",
|
||||
body: nil,
|
||||
wantedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "HEAD Request with Body",
|
||||
method: http.MethodHead,
|
||||
url: "/ping",
|
||||
body: testBody,
|
||||
wantedErr: ErrHeadWithBody,
|
||||
},
|
||||
{
|
||||
testName: "HEAD Request without Body",
|
||||
method: http.MethodHead,
|
||||
url: "/ping",
|
||||
body: nil,
|
||||
wantedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "POST Request with Body",
|
||||
method: http.MethodPost,
|
||||
url: "/ping",
|
||||
body: testBody,
|
||||
wantedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "PUT Request with Body",
|
||||
method: http.MethodPut,
|
||||
url: "/ping",
|
||||
body: testBody,
|
||||
wantedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "PATCH Request with Body",
|
||||
method: http.MethodPatch,
|
||||
url: "/ping",
|
||||
body: testBody,
|
||||
wantedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "DELETE Request with Body",
|
||||
method: http.MethodDelete,
|
||||
url: "/ping",
|
||||
body: testBody,
|
||||
wantedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "CONNECT Request with Body",
|
||||
method: http.MethodConnect,
|
||||
url: "/ping",
|
||||
body: testBody,
|
||||
wantedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "OPTIONS Request with Body",
|
||||
method: http.MethodOptions,
|
||||
url: "/ping",
|
||||
body: testBody,
|
||||
wantedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "TRACE Request with Body",
|
||||
method: http.MethodTrace,
|
||||
url: "/ping",
|
||||
body: testBody,
|
||||
wantedErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
_, err := buildRequest(context.Background(), tc.method, tc.url, tc.body)
|
||||
assert.Equal(t, tc.wantedErr, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,5 @@ const (
|
||||
colon = ':'
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrGetWithBody indicates that GET request with body.
|
||||
ErrGetWithBody = errors.New("HTTP GET should not have body")
|
||||
// ErrHeadWithBody indicates that HEAD request with body.
|
||||
ErrHeadWithBody = errors.New("HTTP HEAD should not have body")
|
||||
)
|
||||
// ErrGetWithBody indicates that GET request with body.
|
||||
var ErrGetWithBody = errors.New("HTTP GET should not have body")
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
syntax = "v1"
|
||||
|
||||
info (
|
||||
title: "Issue 5496 Reproduce"
|
||||
version: "v1"
|
||||
host: "localhost"
|
||||
)
|
||||
|
||||
// Reproduces issue #5496:
|
||||
// The `example` tag option in `path` and `form` tags does not appear in generated swagger JSON.
|
||||
type (
|
||||
Request {
|
||||
// path param with example — should appear as example in swagger
|
||||
Name string `path:"name,options=you|me,example=nihao"`
|
||||
// form param with example and range — should appear as example in swagger
|
||||
Age int `form:"age,optional,range=[1:200],example=18"`
|
||||
}
|
||||
Response {
|
||||
Name string `json:"name,example=nihao"`
|
||||
Age int `json:"age,example=18"`
|
||||
}
|
||||
)
|
||||
|
||||
@server (
|
||||
tags: "issue5496"
|
||||
summary: "Issue 5496 - example field missing for path/form params"
|
||||
)
|
||||
service issue5496 {
|
||||
@doc (
|
||||
description: "Demonstrate that example is missing in path/form swagger params"
|
||||
)
|
||||
@handler getUser
|
||||
get /user/:name (Request) returns (Response)
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
{
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "Issue 5496 Reproduce",
|
||||
"version": "v1"
|
||||
},
|
||||
"host": "localhost",
|
||||
"basePath": "/",
|
||||
"paths": {
|
||||
"/user/{name}": {
|
||||
"get": {
|
||||
"description": "Demonstrate that example is missing in path/form swagger params",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"issue5496"
|
||||
],
|
||||
"summary": "getUser",
|
||||
"operationId": "getUser",
|
||||
"parameters": [
|
||||
{
|
||||
"enum": [
|
||||
"you",
|
||||
"me"
|
||||
],
|
||||
"type": "string",
|
||||
"example": "nihao",
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"maximum": 200,
|
||||
"minimum": 1,
|
||||
"type": "integer",
|
||||
"example": 18,
|
||||
"name": "age",
|
||||
"in": "query",
|
||||
"allowEmptyValue": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"age": {
|
||||
"type": "integer",
|
||||
"example": 18
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "nihao"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-date": "2026-03-19 22:52:34",
|
||||
"x-description": "This is a goctl generated swagger file.",
|
||||
"x-github": "https://github.com/zeromicro/go-zero",
|
||||
"x-go-zero-doc": "https://go-zero.dev/",
|
||||
"x-goctl-version": "1.10.0"
|
||||
}
|
||||
@@ -72,7 +72,6 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para
|
||||
SimpleSchema: spec.SimpleSchema{
|
||||
Type: sampleTypeFromGoType(ctx, member.Type),
|
||||
Default: defValueFromOptions(ctx, headerTag.Options, member.Type),
|
||||
Example: exampleValueFromOptions(ctx, headerTag.Options, member.Type),
|
||||
Items: sampleItemsFromGoType(ctx, member.Type),
|
||||
},
|
||||
ParamProps: spec.ParamProps{
|
||||
@@ -97,7 +96,6 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para
|
||||
SimpleSchema: spec.SimpleSchema{
|
||||
Type: sampleTypeFromGoType(ctx, member.Type),
|
||||
Default: defValueFromOptions(ctx, pathParameterTag.Options, member.Type),
|
||||
Example: exampleValueFromOptions(ctx, pathParameterTag.Options, member.Type),
|
||||
Items: sampleItemsFromGoType(ctx, member.Type),
|
||||
},
|
||||
ParamProps: spec.ParamProps{
|
||||
@@ -123,7 +121,6 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para
|
||||
SimpleSchema: spec.SimpleSchema{
|
||||
Type: sampleTypeFromGoType(ctx, member.Type),
|
||||
Default: defValueFromOptions(ctx, formTag.Options, member.Type),
|
||||
Example: exampleValueFromOptions(ctx, formTag.Options, member.Type),
|
||||
Items: sampleItemsFromGoType(ctx, member.Type),
|
||||
},
|
||||
ParamProps: spec.ParamProps{
|
||||
@@ -146,7 +143,6 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para
|
||||
SimpleSchema: spec.SimpleSchema{
|
||||
Type: sampleTypeFromGoType(ctx, member.Type),
|
||||
Default: defValueFromOptions(ctx, formTag.Options, member.Type),
|
||||
Example: exampleValueFromOptions(ctx, formTag.Options, member.Type),
|
||||
Items: sampleItemsFromGoType(ctx, member.Type),
|
||||
},
|
||||
ParamProps: spec.ParamProps{
|
||||
|
||||
@@ -83,41 +83,6 @@ func TestParametersFromType_EdgeCases(t *testing.T) {
|
||||
assert.Empty(t, params)
|
||||
}
|
||||
|
||||
// TestParametersFromType_ExampleField reproduces issue #5496:
|
||||
// example= in path/form tags was not emitted in the generated swagger JSON.
|
||||
func TestParametersFromType_ExampleField(t *testing.T) {
|
||||
ctx := testingContext(t)
|
||||
|
||||
testStruct := apiSpec.DefineStruct{
|
||||
RawName: "Request",
|
||||
Members: []apiSpec.Member{
|
||||
{
|
||||
Name: "Name",
|
||||
Type: apiSpec.PrimitiveType{RawName: "string"},
|
||||
Tag: `path:"name,options=you|me,example=nihao"`,
|
||||
},
|
||||
{
|
||||
Name: "Age",
|
||||
Type: apiSpec.PrimitiveType{RawName: "int"},
|
||||
Tag: `form:"age,optional,range=[1:200],example=18"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
params := parametersFromType(ctx, http.MethodGet, testStruct)
|
||||
assert.Len(t, params, 2)
|
||||
|
||||
// path param should have example
|
||||
pathParam := params[0]
|
||||
assert.Equal(t, paramsInPath, pathParam.In)
|
||||
assert.Equal(t, "nihao", pathParam.SimpleSchema.Example, "path param example should be set")
|
||||
|
||||
// form/query param should have example
|
||||
queryParam := params[1]
|
||||
assert.Equal(t, paramsInQuery, queryParam.In)
|
||||
assert.EqualValues(t, int64(18), queryParam.SimpleSchema.Example, "form param example should be set")
|
||||
}
|
||||
|
||||
func createTestStruct(name string, hasJson bool) apiSpec.DefineStruct {
|
||||
tag := `form:"username"`
|
||||
if hasJson {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/zeromicro/go-zero/tools/goctl
|
||||
|
||||
go 1.24.0
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
@@ -16,109 +16,103 @@ require (
|
||||
github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1
|
||||
github.com/zeromicro/antlr v0.0.1
|
||||
github.com/zeromicro/ddl-parser v1.0.5
|
||||
github.com/zeromicro/go-zero v1.10.1
|
||||
golang.org/x/text v0.34.0
|
||||
google.golang.org/grpc v1.80.0
|
||||
github.com/zeromicro/go-zero v1.9.4
|
||||
golang.org/x/text v0.22.0
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/alicebob/miniredis/v2 v2.37.0 // indirect
|
||||
github.com/alicebob/miniredis/v2 v2.35.0 // indirect
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grafana/pyroscope-go v1.2.8 // indirect
|
||||
github.com/grafana/pyroscope-go v1.2.7 // indirect
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.8.0 // indirect
|
||||
github.com/jackc/pgx/v5 v5.7.4 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.3 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.18.0 // indirect
|
||||
github.com/prometheus/client_golang v1.21.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.17.2 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/titanous/json5 v1.0.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yuin/gopher-lua v1.1.1 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.21 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.21 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.40.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.15 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.15 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.50.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/term v0.40.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/oauth2 v0.24.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/time v0.10.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.34.3 // indirect
|
||||
k8s.io/apimachinery v0.34.3 // indirect
|
||||
k8s.io/client-go v0.34.3 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
||||
k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
k8s.io/api v0.29.3 // indirect
|
||||
k8s.io/apimachinery v0.29.4 // indirect
|
||||
k8s.io/client-go v0.29.3 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
@@ -2,8 +2,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/alicebob/miniredis/v2 v2.37.0 h1:RheObYW32G1aiJIj81XVt78ZHJpHonHLHW7OLIshq68=
|
||||
github.com/alicebob/miniredis/v2 v2.37.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
|
||||
github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
|
||||
github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec h1:EEyRvzmpEUZ+I8WmD5cw/vY8EqhambkOqy5iFr0908A=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
@@ -14,8 +14,8 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||
@@ -28,19 +28,18 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emicklei/proto v1.14.3 h1:zEhlzNkpP8kN6utonKMzlPfIvy82t5Kb9mufaJxSe1Q=
|
||||
github.com/emicklei/proto v1.14.3/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/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/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||
@@ -54,32 +53,34 @@ github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDq
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
|
||||
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
|
||||
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
|
||||
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
|
||||
github.com/grafana/pyroscope-go v1.2.8 h1:UvCwIhlx9DeV7F6TW/z8q1Mi4PIm3vuUJ2ZlCEvmA4M=
|
||||
github.com/grafana/pyroscope-go v1.2.8/go.mod h1:SSi59eQ1/zmKoY/BKwa5rSFsJaq+242Bcrr4wPix1g8=
|
||||
github.com/grafana/pyroscope-go v1.2.7 h1:VWBBlqxjyR0Cwk2W6UrE8CdcdD80GOFNutj0Kb1T8ac=
|
||||
github.com/grafana/pyroscope-go v1.2.7/go.mod h1:o/bpSLiJYYP6HQtvcoVKiE9s5RiNgjYTj1DhiddP2Pc=
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og=
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
|
||||
@@ -90,8 +91,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=
|
||||
github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=
|
||||
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
|
||||
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
@@ -101,10 +102,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -123,39 +122,36 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
|
||||
github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs=
|
||||
github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0=
|
||||
github.com/robertkrimen/otto v0.2.1 h1:FVP0PJ0AHIjC+N4pKCG9yCDz6LHNPCwi/GKID5pGGF0=
|
||||
github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//farB5FlRY=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
|
||||
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
|
||||
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
@@ -165,81 +161,78 @@ github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/titanous/json5 v1.0.0 h1:hJf8Su1d9NuI/ffpxgxQfxh/UiBFZX7bMPid0rIL/7s=
|
||||
github.com/titanous/json5 v1.0.0/go.mod h1:7JH1M8/LHKc6cyP5o5g3CSaRj+mBrIimTxzpvmckH8c=
|
||||
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/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
|
||||
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
github.com/zeromicro/antlr v0.0.1 h1:CQpIn/dc0pUjgGQ81y98s/NGOm2Hfru2NNio2I9mQgk=
|
||||
github.com/zeromicro/antlr v0.0.1/go.mod h1:nfpjEwFR6Q4xGDJMcZnCL9tEfQRgszMwu3rDz2Z+p5M=
|
||||
github.com/zeromicro/ddl-parser v1.0.5 h1:LaVqHdzMTjasua1yYpIYaksxKqRzFrEukj2Wi2EbWaQ=
|
||||
github.com/zeromicro/ddl-parser v1.0.5/go.mod h1:ISU/8NuPyEpl9pa17Py9TBPetMjtsiHrb9f5XGiYbo8=
|
||||
github.com/zeromicro/go-zero v1.10.1 h1:1nM3ilvYx97GUqyaNH2IQPtfNyK7tp5JvN63c7m6QKU=
|
||||
github.com/zeromicro/go-zero v1.10.1/go.mod h1:z41DXmO6gx/Se7Ow5UIwPxcUmpVj3ebhoNCcZ1gfp5k=
|
||||
go.etcd.io/etcd/api/v3 v3.5.21 h1:A6O2/JDb3tvHhiIz3xf9nJ7REHvtEFJJ3veW3FbCnS8=
|
||||
go.etcd.io/etcd/api/v3 v3.5.21/go.mod h1:c3aH5wcvXv/9dqIw2Y810LDXJfhSYdHQ0vxmP3CCHVY=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.21 h1:lPBu71Y7osQmzlflM9OfeIV2JlmpBjqBNlLtcoBqUTc=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.21/go.mod h1:BgqT/IXPjK9NkeSDjbzwsHySX3yIle2+ndz28nVsjUs=
|
||||
go.etcd.io/etcd/client/v3 v3.5.21 h1:T6b1Ow6fNjOLOtM0xSoKNQt1ASPCLWrF9XMHcH9pEyY=
|
||||
go.etcd.io/etcd/client/v3 v3.5.21/go.mod h1:mFYy67IOqmbRf/kRUvsHixzo3iG+1OF2W2+jVIQRAnU=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.40.0 h1:zu+I4j+FdO6xIxBVPeuncQVbjxUM4LiMgv6GwGe9REE=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.40.0/go.mod h1:zS6cC4nFBYXbu18e7aLfMzubBjOiN7ZcROu477qtMf8=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
github.com/zeromicro/go-zero v1.9.4 h1:aRLFoISqAYijABtkbliQC5SsI5TbizJpQvoHc9xup8k=
|
||||
github.com/zeromicro/go-zero v1.9.4/go.mod h1:a17JOTch25SWxBcUgJZYps60hygK3pIYdw7nGwlcS38=
|
||||
go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk=
|
||||
go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
|
||||
go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
|
||||
go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -248,83 +241,76 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
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.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d h1:JU0iKnSg02Gmb5ZdV8nYsKEKsP6o/FGVWTrw4i1DA9A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
|
||||
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4=
|
||||
k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk=
|
||||
k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE=
|
||||
k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
||||
k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A=
|
||||
k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||
k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 h1:kBawHLSnx/mYHmRnNUf9d4CpjREbeZuxoSGOX/J+aYM=
|
||||
k8s.io/utils v0.0.0-20260319190234-28399d86e0b5/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
|
||||
k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
|
||||
k8s.io/apimachinery v0.29.4 h1:RaFdJiDmuKs/8cm1M6Dh1Kvyh59YQFDcFuFTSmXes6Q=
|
||||
k8s.io/apimachinery v0.29.4/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y=
|
||||
k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
|
||||
k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0=
|
||||
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
|
||||
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
// BuildVersion is the version of goctl.
|
||||
const BuildVersion = "1.10.1"
|
||||
const BuildVersion = "1.9.2"
|
||||
|
||||
var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-beta": 2, "beta": 3, "released": 4, "": 5}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ func genFindOne(table Table, withCache, postgreSql bool) (string, string, error)
|
||||
|
||||
output, err := util.With("findOne").
|
||||
Parse(text).
|
||||
AddFunc("hasField", hasField(table)).
|
||||
Execute(map[string]any{
|
||||
"withCache": withCache,
|
||||
"upperStartCamelObject": camel,
|
||||
|
||||
@@ -22,7 +22,7 @@ func genFindOneByField(table Table, withCache, postgreSql bool) (*findOneCode, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := util.With("findOneByField").Parse(text).AddFunc("hasField", hasField(table))
|
||||
t := util.With("findOneByField").Parse(text)
|
||||
var list []string
|
||||
camelTableName := table.Name.ToCamel()
|
||||
for _, key := range table.UniqueCacheKey {
|
||||
@@ -54,7 +54,7 @@ func genFindOneByField(table Table, withCache, postgreSql bool) (*findOneCode, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t = util.With("findOneByFieldMethod").Parse(text).AddFunc("hasField", hasField(table))
|
||||
t = util.With("findOneByFieldMethod").Parse(text)
|
||||
var listMethod []string
|
||||
for _, key := range table.UniqueCacheKey {
|
||||
var inJoin, paramJoin Join
|
||||
@@ -88,7 +88,7 @@ func genFindOneByField(table Table, withCache, postgreSql bool) (*findOneCode, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := util.With("findOneByFieldExtraMethod").AddFunc("hasField", hasField(table)).Parse(text).Execute(map[string]any{
|
||||
out, err := util.With("findOneByFieldExtraMethod").Parse(text).Execute(map[string]any{
|
||||
"upperStartCamelObject": camelTableName,
|
||||
"primaryKeyLeft": table.PrimaryCacheKey.VarLeft,
|
||||
"lowerStartCamelObject": stringx.From(camelTableName).Untitle(),
|
||||
|
||||
@@ -360,7 +360,6 @@ func (g *defaultGenerator) genModelCustom(in parser.Table, withCache bool) (stri
|
||||
|
||||
t := util.With("model-custom").
|
||||
Parse(text).
|
||||
AddFunc("hasField", hasField(Table{Table: in})).
|
||||
GoFmt(true)
|
||||
output, err := t.Execute(map[string]any{
|
||||
"pkg": g.pkg,
|
||||
@@ -382,7 +381,6 @@ func (g *defaultGenerator) executeModel(table Table, code *code) (*bytes.Buffer,
|
||||
}
|
||||
t := util.With("model").
|
||||
Parse(text).
|
||||
AddFunc("hasField", hasField(table)).
|
||||
GoFmt(true)
|
||||
output, err := t.Execute(map[string]any{
|
||||
"pkg": g.pkg,
|
||||
|
||||
@@ -28,7 +28,7 @@ func genImports(table Table, withCache, timeImport bool) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buffer, err := util.With("import").Parse(text).AddFunc("hasField", hasField(table)).Execute(map[string]any{
|
||||
buffer, err := util.With("import").Parse(text).Execute(map[string]any{
|
||||
"time": timeImport,
|
||||
"containsPQ": table.ContainsPQ,
|
||||
"data": table,
|
||||
|
||||
@@ -94,16 +94,3 @@ func Update() error {
|
||||
|
||||
return pathx.InitTemplates(category, templates)
|
||||
}
|
||||
|
||||
// hasField returns a function that checks if a field exists in the table.
|
||||
// It uses a pre-built map for O(1) lookup performance.
|
||||
func hasField(table Table) func(string) bool {
|
||||
fieldSet := make(map[string]struct{}, len(table.Fields))
|
||||
for _, field := range table.Fields {
|
||||
fieldSet[field.NameOriginal] = struct{}{}
|
||||
}
|
||||
return func(f string) bool {
|
||||
_, ok := fieldSet[f]
|
||||
return ok
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/model/sql/parser"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/model/sql/template"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/stringx"
|
||||
)
|
||||
|
||||
func TestGenTemplates(t *testing.T) {
|
||||
@@ -93,151 +91,3 @@ func TestUpdate(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, template.New, string(data))
|
||||
}
|
||||
|
||||
func TestHasField(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
table Table
|
||||
fieldName string
|
||||
wantResult bool
|
||||
}{
|
||||
{
|
||||
name: "field exists",
|
||||
table: Table{
|
||||
Table: parser.Table{
|
||||
Fields: []*parser.Field{
|
||||
{NameOriginal: "id"},
|
||||
{NameOriginal: "name"},
|
||||
{NameOriginal: "created_at"},
|
||||
},
|
||||
},
|
||||
},
|
||||
fieldName: "name",
|
||||
wantResult: true,
|
||||
},
|
||||
{
|
||||
name: "field does not exist",
|
||||
table: Table{
|
||||
Table: parser.Table{
|
||||
Fields: []*parser.Field{
|
||||
{NameOriginal: "id"},
|
||||
{NameOriginal: "name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
fieldName: "email",
|
||||
wantResult: false,
|
||||
},
|
||||
{
|
||||
name: "empty table",
|
||||
table: Table{
|
||||
Table: parser.Table{
|
||||
Fields: []*parser.Field{},
|
||||
},
|
||||
},
|
||||
fieldName: "id",
|
||||
wantResult: false,
|
||||
},
|
||||
{
|
||||
name: "case sensitive match",
|
||||
table: Table{
|
||||
Table: parser.Table{
|
||||
Fields: []*parser.Field{
|
||||
{NameOriginal: "ID"},
|
||||
{NameOriginal: "Name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
fieldName: "id",
|
||||
wantResult: false,
|
||||
},
|
||||
{
|
||||
name: "exact match required",
|
||||
table: Table{
|
||||
Table: parser.Table{
|
||||
Fields: []*parser.Field{
|
||||
{NameOriginal: "user_name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
fieldName: "user_name",
|
||||
wantResult: true,
|
||||
},
|
||||
{
|
||||
name: "partial match should fail",
|
||||
table: Table{
|
||||
Table: parser.Table{
|
||||
Fields: []*parser.Field{
|
||||
{NameOriginal: "user_name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
fieldName: "user",
|
||||
wantResult: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fn := hasField(tt.table)
|
||||
result := fn(tt.fieldName)
|
||||
assert.Equal(t, tt.wantResult, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasFieldWithRealTable(t *testing.T) {
|
||||
// Create a realistic table structure
|
||||
table := Table{
|
||||
Table: parser.Table{
|
||||
Name: stringx.From("users"),
|
||||
Fields: []*parser.Field{
|
||||
{NameOriginal: "id", DataType: "int64"},
|
||||
{NameOriginal: "username", DataType: "string"},
|
||||
{NameOriginal: "email", DataType: "string"},
|
||||
{NameOriginal: "password", DataType: "string"},
|
||||
{NameOriginal: "created_at", DataType: "time.Time"},
|
||||
{NameOriginal: "updated_at", DataType: "time.Time"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
fn := hasField(table)
|
||||
|
||||
// Test all existing fields
|
||||
assert.True(t, fn("id"))
|
||||
assert.True(t, fn("username"))
|
||||
assert.True(t, fn("email"))
|
||||
assert.True(t, fn("password"))
|
||||
assert.True(t, fn("created_at"))
|
||||
assert.True(t, fn("updated_at"))
|
||||
|
||||
// Test non-existing fields
|
||||
assert.False(t, fn("deleted_at"))
|
||||
assert.False(t, fn("ID"))
|
||||
assert.False(t, fn("Username"))
|
||||
assert.False(t, fn(""))
|
||||
}
|
||||
|
||||
func TestHasFieldPerformance(t *testing.T) {
|
||||
// Create a table with many fields to test performance optimization
|
||||
var fields []*parser.Field
|
||||
for i := 0; i < 1000; i++ {
|
||||
fields = append(fields, &parser.Field{
|
||||
NameOriginal: "field_" + string(rune('0'+i%10)) + string(rune('a'+i%26)),
|
||||
})
|
||||
}
|
||||
|
||||
table := Table{
|
||||
Table: parser.Table{
|
||||
Fields: fields,
|
||||
},
|
||||
}
|
||||
|
||||
fn := hasField(table)
|
||||
|
||||
// Verify the function works correctly
|
||||
assert.True(t, fn(fields[0].NameOriginal))
|
||||
assert.True(t, fn(fields[999].NameOriginal))
|
||||
assert.False(t, fn("non_existent_field"))
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func genUpdate(table Table, withCache, postgreSql bool) (
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
output, err := util.With("update").Parse(text).AddFunc("hasField", hasField(table)).Execute(
|
||||
output, err := util.With("update").Parse(text).Execute(
|
||||
map[string]any{
|
||||
"withCache": withCache,
|
||||
"containsIndexCache": table.ContainsUniqueCacheKey,
|
||||
@@ -94,7 +94,7 @@ func genUpdate(table Table, withCache, postgreSql bool) (
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
updateMethodOutput, err := util.With("updateMethod").Parse(text).AddFunc("hasField", hasField(table)).Execute(
|
||||
updateMethodOutput, err := util.With("updateMethod").Parse(text).Execute(
|
||||
map[string]any{
|
||||
"upperStartCamelObject": camelTableName,
|
||||
"data": table,
|
||||
|
||||
@@ -197,7 +197,7 @@ func (p *printer) print(x reflect.Value) {
|
||||
|
||||
case reflect.Struct:
|
||||
if val, ok := x.Interface().(apitoken.Position); ok {
|
||||
p.printf("%s", val.String())
|
||||
p.printf(val.String())
|
||||
return
|
||||
}
|
||||
t := x.Type()
|
||||
|
||||
@@ -357,7 +357,7 @@ func (w *Writer) Write(opts ...Option) {
|
||||
|
||||
// WriteText writes the text.
|
||||
func (w *Writer) WriteText(text string) {
|
||||
_, _ = fmt.Fprint(w.tw, text)
|
||||
_, _ = fmt.Fprintf(w.tw, text)
|
||||
}
|
||||
|
||||
func (w *Writer) write(opt *option) {
|
||||
|
||||
@@ -284,7 +284,7 @@ func (api *API) parseImportedAPI(imports []ast.ImportStmt) ([]*API, error) {
|
||||
}
|
||||
// import cycle check
|
||||
if err := api.importManager.Push(impPath); err != nil {
|
||||
return nil, ast.SyntaxError(tok.Position, "%s", err.Error())
|
||||
return nil, ast.SyntaxError(tok.Position, err.Error())
|
||||
}
|
||||
|
||||
if _, ok := api.importSet[impPath]; ok {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -24,5 +24,5 @@ func (e *errorManager) error() error {
|
||||
if len(e.errors) == 0 {
|
||||
return nil
|
||||
}
|
||||
return errors.New(strings.Join(e.errors, "\n"))
|
||||
return fmt.Errorf(strings.Join(e.errors, "\n"))
|
||||
}
|
||||
|
||||
@@ -1676,7 +1676,7 @@ func (p *Parser) CheckErrors() error {
|
||||
errors = append(errors, e.Error())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s", strings.Join(errors, "\n"))
|
||||
return fmt.Errorf(strings.Join(errors, "\n"))
|
||||
}
|
||||
|
||||
func (p *Parser) appendStmt(stmt ...ast.Stmt) {
|
||||
|
||||
@@ -130,7 +130,7 @@ func TestParser_Parse_infoStmt(t *testing.T) {
|
||||
"author": `"type author here"`,
|
||||
"email": `"type email here"`,
|
||||
"version": `"type version here"`,
|
||||
"enable": `true`,
|
||||
"enable": `true`,
|
||||
"disable": `false`,
|
||||
}
|
||||
p := New("foo.api", infoTestAPI)
|
||||
@@ -762,11 +762,6 @@ func TestParser_Parse_service(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
Request: &ast.BodyStmt{
|
||||
LParen: ast.NewTokenNode(token.Token{Type: token.LPAREN, Text: "("}),
|
||||
RParen: ast.NewTokenNode(token.Token{Type: token.RPAREN, Text: ")"}),
|
||||
},
|
||||
Returns: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "returns"}),
|
||||
Response: &ast.BodyStmt{
|
||||
LParen: ast.NewTokenNode(token.Token{Type: token.LPAREN, Text: "("}),
|
||||
Body: &ast.BodyExpr{
|
||||
Value: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "Foo"}),
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
# 变更日志
|
||||
|
||||
## 未发布
|
||||
|
||||
### 新功能
|
||||
|
||||
#### 外部 Proto 导入支持(`--proto_path` / `-I`)
|
||||
|
||||
新增通过 `-I` / `--proto_path` 标志导入外部目录中的 proto 文件,支持完整的传递性依赖解析。
|
||||
|
||||
**涉及文件:**
|
||||
- `generator/gen.go` — `ZRpcContext` 新增 `ProtoPaths` 字段;新增 `resolveImportedProtos()` 在代码生成前填充 `ImportedProtos`。
|
||||
- `generator/genpb.go` — 新增 `buildProtocCmd()` 自动发现并追加传递性导入的 proto 文件到 `protoc` 命令;新增 `relativeToProtoPath()` 计算正确的相对路径。
|
||||
- `parser/import.go` — 新文件(主要新增)。实现 `ResolveImports()` 递归解析传递性导入,`ParseImportedProtos()` 提取导入 proto 的 `go_package` / `package` 元数据,`BuildProtoPackageMap()` 构建按 proto 包名的 O(1) 查找表。
|
||||
- `parser/proto.go` — `Proto` 结构体新增 `ImportedProtos []ImportedProto` 字段。
|
||||
- `cli/cli.go` — `RPCNew` 传递 `ProtoPaths` 到 `ZRpcContext`。
|
||||
- `cli/zrpc.go` — 将 `VarStringSliceProtoPath` 传递到 `ZRpcContext.ProtoPaths`。
|
||||
|
||||
**前后对比:**
|
||||
|
||||
| | 变更前 | 变更后 |
|
||||
|---|---|---|
|
||||
| 从外部目录导入 Proto | ❌ 不支持,所有类型必须在同一文件中定义 | ✅ 使用 `-I ./ext_protos` 添加搜索路径 |
|
||||
| 传递性导入(A → B → C) | ❌ 仅识别直接导入 | ✅ 递归解析所有传递性依赖 |
|
||||
| 导入 proto 的 `.pb.go` 生成 | ❌ 需手动为每个文件单独运行 protoc | ✅ 自动将导入的 proto 追加到 protoc 命令 |
|
||||
| Proto 搜索路径 | ❌ 仅源文件所在目录 | ✅ 支持多个 `-I` 路径,与 protoc 一致 |
|
||||
|
||||
**行为说明:**
|
||||
- 递归遍历 proto 文件中的所有 `import` 声明,跳过 `google/*` 知名类型。
|
||||
- 在每个 `-I` 目录中搜索被导入的文件,未找到的系统级 proto 静默跳过。
|
||||
- 将发现的 proto 文件追加到 `protoc` 命令,使其 `.pb.go` 文件与主 proto 一同生成。
|
||||
|
||||
---
|
||||
|
||||
#### 跨包类型解析
|
||||
|
||||
当导入的 proto 与主 proto 具有**不同**的 `go_package` 时,goctl 现在能够自动在 server、logic 和 client 代码中生成正确的 Go 导入路径和限定类型引用。
|
||||
|
||||
**涉及文件:**
|
||||
- `generator/typeref.go` — 新文件,核心类型解析引擎:
|
||||
- `resolveRPCTypeRef()` — 将 proto RPC 类型(简单类型、同包点号类型、跨包点号类型、Google WKT)解析为带正确导入路径的 Go 类型引用。
|
||||
- `resolveCallTypeRef()` — 客户端代码生成变体,支持类型别名。
|
||||
- `googleWKTTable` — 全部 16 种 Google 知名类型到 Go 等价类型的映射表。
|
||||
- `generator/genserver.go` — `genFunctions()` 调用 `resolveRPCTypeRef()` 解析请求/响应类型并收集额外导入路径。
|
||||
- `generator/genlogic.go` — `genLogicFunction()` 使用 `resolveRPCTypeRef()`;新增 `addLogicImports()` 按需添加主 pb 导入和跨包导入。
|
||||
- `generator/gencall.go` — `genFunction()` 和 `getInterfaceFuncs()` 使用 `resolveCallTypeRef()` 处理类型别名和额外导入;新增 `buildExtraImportLines()` 辅助函数。
|
||||
- `generator/call.tpl` — 新增 `{{.extraImports}}` 占位符用于跨包导入行。
|
||||
|
||||
**前后对比:**
|
||||
|
||||
| Proto 类型 | 变更前 | 变更后 |
|
||||
|---|---|---|
|
||||
| `GetReq`(同文件) | `pb.GetReq` | `pb.GetReq`(无变化) |
|
||||
| `ext.ExtReq`(相同 `go_package`) | ❌ 报错:"request type must defined in" | ✅ `pb.ExtReq` — 合并到主包 |
|
||||
| `common.TypesReq`(不同 `go_package`) | ❌ 报错:"request type must defined in" | ✅ `common.TypesReq` + 自动生成 `import "example.com/demo/pb/common"` |
|
||||
| `google.protobuf.Empty` | ❌ 报错:"request type must defined in" | ✅ `emptypb.Empty` + 自动生成导入 |
|
||||
|
||||
**行为说明:**
|
||||
- 简单类型(如 `GetReq`)解析为 `pb.GetReq`,无额外导入。
|
||||
- 同包点号类型(如 `ext.ExtReq`,其中 `ext` 与主 proto 有相同的 `go_package`)解析为 `pb.ExtReq`。
|
||||
- 跨包点号类型(如 `common.TypesReq`,其中 `common` 有不同的 `go_package`)解析为 `common.TypesReq`,并自动添加正确的 Go 导入路径。
|
||||
|
||||
---
|
||||
|
||||
#### Google 知名类型作为 RPC 参数
|
||||
|
||||
Google protobuf 知名类型现在可以直接用作 RPC 的请求/响应类型(而不仅仅是消息字段)。
|
||||
|
||||
**涉及文件:**
|
||||
- `generator/typeref.go` — `resolveGoogleWKT()` + `googleWKTTable` 处理所有标准类型。
|
||||
|
||||
**前后对比:**
|
||||
|
||||
| Proto 类型 | 变更前(作为 RPC 参数) | 变更后(作为 RPC 参数) |
|
||||
|---|---|---|
|
||||
| `google.protobuf.Empty` | ❌ 报错 | ✅ `emptypb.Empty` |
|
||||
| `google.protobuf.Timestamp` | ❌ 报错 | ✅ `timestamppb.Timestamp` |
|
||||
| `google.protobuf.Duration` | ❌ 报错 | ✅ `durationpb.Duration` |
|
||||
| `google.protobuf.Any` | ❌ 报错 | ✅ `anypb.Any` |
|
||||
| `google.protobuf.Struct` | ❌ 报错 | ✅ `structpb.Struct` |
|
||||
| `google.protobuf.FieldMask` | ❌ 报错 | ✅ `fieldmaskpb.FieldMask` |
|
||||
| `google.protobuf.*Value` | ❌ 报错 | ✅ `wrapperspb.*Value` |
|
||||
|
||||
> 注:这些类型此前已可用作**消息字段**。本次变更使其可直接用作 **RPC 请求/响应类型**。
|
||||
|
||||
**完整类型映射表:**
|
||||
|
||||
| Proto 类型 | Go 类型 |
|
||||
|---|---|
|
||||
| `google.protobuf.Empty` | `emptypb.Empty` |
|
||||
| `google.protobuf.Timestamp` | `timestamppb.Timestamp` |
|
||||
| `google.protobuf.Duration` | `durationpb.Duration` |
|
||||
| `google.protobuf.Any` | `anypb.Any` |
|
||||
| `google.protobuf.Struct` | `structpb.Struct` |
|
||||
| `google.protobuf.Value` | `structpb.Value` |
|
||||
| `google.protobuf.ListValue` | `structpb.ListValue` |
|
||||
| `google.protobuf.FieldMask` | `fieldmaskpb.FieldMask` |
|
||||
| `google.protobuf.*Value`(包装类型) | `wrapperspb.*Value` |
|
||||
|
||||
---
|
||||
|
||||
### 不兼容变更
|
||||
|
||||
#### RPC 定义中允许使用点号类型名
|
||||
|
||||
此前 goctl 会拒绝 RPC 请求/响应类型中包含点号的情况(如 `base.Req`),要求所有类型必须定义在同一个 proto 文件中。此限制已移除。
|
||||
|
||||
**前后对比:**
|
||||
|
||||
| Proto 定义 | 变更前 | 变更后 |
|
||||
|---|---|---|
|
||||
| `rpc Fetch(base.Req) returns (base.Reply)` | ❌ 解析错误:"request type must defined in xxx.proto" | ✅ 解析成功,`base.Req` 通过导入的 proto 解析 |
|
||||
| `rpc Ping(google.protobuf.Empty) returns (Reply)` | ❌ 解析错误:"request type must defined in xxx.proto" | ✅ 解析成功,解析为 `emptypb.Empty` |
|
||||
|
||||
**涉及文件:**
|
||||
- `parser/service.go` — 移除了拒绝点号类型名的验证循环(原错误信息为 `"request type must defined in"` / `"returns type must defined in"`)。
|
||||
- `parser/parser_test.go` — `TestDefaultProtoParseCaseInvalidRequestType` 和 `TestDefaultProtoParseCaseInvalidResponseType` 重命名并更新,验证点号类型现在可以正常解析。
|
||||
@@ -1,117 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
### New Features
|
||||
|
||||
#### External Proto Import Support (`--proto_path` / `-I`)
|
||||
|
||||
Added support for importing proto files from external directories via `-I` / `--proto_path` flags, with full transitive dependency resolution.
|
||||
|
||||
**Affected files:**
|
||||
- `generator/gen.go` — Added `ProtoPaths` field to `ZRpcContext`; added `resolveImportedProtos()` to populate `ImportedProtos` before code generation.
|
||||
- `generator/genpb.go` — Added `buildProtocCmd()` to automatically discover and append transitively imported proto files to the `protoc` command; added `relativeToProtoPath()` to compute correct relative paths for protoc output.
|
||||
- `parser/import.go` — New file (major addition). Implements `ResolveImports()` for recursive transitive import resolution, `ParseImportedProtos()` for extracting `go_package` / `package` metadata from imported protos, and `BuildProtoPackageMap()` for O(1) lookup by proto package name.
|
||||
- `parser/proto.go` — Added `ImportedProtos []ImportedProto` field to the `Proto` struct.
|
||||
- `cli/cli.go` — Passes `ProtoPaths` from `RPCNew` to `ZRpcContext`.
|
||||
- `cli/zrpc.go` — Passes `VarStringSliceProtoPath` to `ZRpcContext.ProtoPaths`.
|
||||
|
||||
**Before vs After:**
|
||||
|
||||
| | Before | After |
|
||||
|---|---|---|
|
||||
| Proto imports from external dirs | ❌ Not supported, all types must be in the same file | ✅ Use `-I ./ext_protos` to add search paths |
|
||||
| Transitive imports (A → B → C) | ❌ Only direct imports recognized | ✅ Recursively resolves all transitive dependencies |
|
||||
| Imported proto `.pb.go` generation | ❌ Manual, must run protoc separately for each file | ✅ Automatic, imported protos appended to protoc command |
|
||||
| Proto search paths | ❌ Only source file directory | ✅ Multiple `-I` paths, same as protoc |
|
||||
|
||||
**Behavior:**
|
||||
- Transitively walks all `import` declarations in proto files, skipping `google/*` well-known types.
|
||||
- Searches each `-I` directory for imported files, silently skipping system-level protos not found in user paths.
|
||||
- Appends discovered proto files to the `protoc` command so their `.pb.go` files are generated alongside the main proto.
|
||||
|
||||
---
|
||||
|
||||
#### Cross-Package Type Resolution
|
||||
|
||||
When an imported proto has a **different** `go_package` from the main proto, goctl now automatically generates the correct Go import paths and qualified type references in server, logic, and client code.
|
||||
|
||||
**Affected files:**
|
||||
- `generator/typeref.go` — New file. Core type resolution engine:
|
||||
- `resolveRPCTypeRef()` — Resolves proto RPC types (simple, same-package dotted, cross-package dotted, Google WKT) to Go type references with correct import paths.
|
||||
- `resolveCallTypeRef()` — Variant for client code generation with type alias support.
|
||||
- `googleWKTTable` — Mapping table for all 16 Google well-known types to their Go equivalents.
|
||||
- `generator/genserver.go` — `genFunctions()` now calls `resolveRPCTypeRef()` for request/response types and collects extra import paths.
|
||||
- `generator/genlogic.go` — `genLogicFunction()` uses `resolveRPCTypeRef()`; added `addLogicImports()` to conditionally include main pb import and cross-package imports.
|
||||
- `generator/gencall.go` — `genFunction()` and `getInterfaceFuncs()` use `resolveCallTypeRef()` for type aliases and extra imports; added `buildExtraImportLines()` helper.
|
||||
- `generator/call.tpl` — Added `{{.extraImports}}` placeholder for cross-package import lines.
|
||||
|
||||
**Before vs After:**
|
||||
|
||||
| Proto type | Before | After |
|
||||
|---|---|---|
|
||||
| `GetReq` (same file) | `pb.GetReq` | `pb.GetReq` (unchanged) |
|
||||
| `ext.ExtReq` (same `go_package`) | ❌ Error: "request type must defined in" | ✅ `pb.ExtReq` — merged into main package |
|
||||
| `common.TypesReq` (different `go_package`) | ❌ Error: "request type must defined in" | ✅ `common.TypesReq` + auto-generated `import "example.com/demo/pb/common"` |
|
||||
| `google.protobuf.Empty` | ❌ Error: "request type must defined in" | ✅ `emptypb.Empty` + auto-generated import |
|
||||
|
||||
**Behavior:**
|
||||
- Simple types (e.g., `GetReq`) resolve to `pb.GetReq` with no extra import.
|
||||
- Same-package dotted types (e.g., `ext.ExtReq` where `ext` has the same `go_package`) resolve to `pb.ExtReq`.
|
||||
- Cross-package dotted types (e.g., `common.TypesReq` where `common` has a different `go_package`) resolve to `common.TypesReq` with the correct Go import path added automatically.
|
||||
|
||||
---
|
||||
|
||||
#### Google Well-Known Types as RPC Parameters
|
||||
|
||||
Google protobuf well-known types can now be used directly as RPC request/response types (not just as message fields).
|
||||
|
||||
**Affected files:**
|
||||
- `generator/typeref.go` — `resolveGoogleWKT()` + `googleWKTTable` handles all standard types.
|
||||
|
||||
**Before vs After:**
|
||||
|
||||
| Proto Type | Before (as RPC param) | After (as RPC param) |
|
||||
|---|---|---|
|
||||
| `google.protobuf.Empty` | ❌ Error | ✅ `emptypb.Empty` |
|
||||
| `google.protobuf.Timestamp` | ❌ Error | ✅ `timestamppb.Timestamp` |
|
||||
| `google.protobuf.Duration` | ❌ Error | ✅ `durationpb.Duration` |
|
||||
| `google.protobuf.Any` | ❌ Error | ✅ `anypb.Any` |
|
||||
| `google.protobuf.Struct` | ❌ Error | ✅ `structpb.Struct` |
|
||||
| `google.protobuf.FieldMask` | ❌ Error | ✅ `fieldmaskpb.FieldMask` |
|
||||
| `google.protobuf.*Value` | ❌ Error | ✅ `wrapperspb.*Value` |
|
||||
|
||||
> Note: These types were already usable as **message fields** before. This change allows them as **RPC request/response types** directly.
|
||||
|
||||
**Supported types:**
|
||||
|
||||
| Proto Type | Go Type |
|
||||
|---|---|
|
||||
| `google.protobuf.Empty` | `emptypb.Empty` |
|
||||
| `google.protobuf.Timestamp` | `timestamppb.Timestamp` |
|
||||
| `google.protobuf.Duration` | `durationpb.Duration` |
|
||||
| `google.protobuf.Any` | `anypb.Any` |
|
||||
| `google.protobuf.Struct` | `structpb.Struct` |
|
||||
| `google.protobuf.Value` | `structpb.Value` |
|
||||
| `google.protobuf.ListValue` | `structpb.ListValue` |
|
||||
| `google.protobuf.FieldMask` | `fieldmaskpb.FieldMask` |
|
||||
| `google.protobuf.*Value` (wrappers) | `wrapperspb.*Value` |
|
||||
|
||||
---
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
#### Dotted Type Names Now Allowed in RPC Definitions
|
||||
|
||||
Previously, goctl rejected any RPC request/response type containing a dot (e.g., `base.Req`), requiring all types to be defined in the same proto file. This restriction has been removed.
|
||||
|
||||
**Before vs After:**
|
||||
|
||||
| Proto Definition | Before | After |
|
||||
|---|---|---|
|
||||
| `rpc Fetch(base.Req) returns (base.Reply)` | ❌ Parse error: "request type must defined in xxx.proto" | ✅ Parsed successfully, `base.Req` resolved via imported proto |
|
||||
| `rpc Ping(google.protobuf.Empty) returns (Reply)` | ❌ Parse error: "request type must defined in xxx.proto" | ✅ Parsed successfully, resolved to `emptypb.Empty` |
|
||||
|
||||
**Affected files:**
|
||||
- `parser/service.go` — Removed the validation loop that rejected dotted type names with `"request type must defined in"` / `"returns type must defined in"` errors.
|
||||
- `parser/parser_test.go` — `TestDefaultProtoParseCaseInvalidRequestType` and `TestDefaultProtoParseCaseInvalidResponseType` renamed and updated to verify that dotted types now parse successfully.
|
||||
@@ -1,315 +0,0 @@
|
||||
# goctl rpc — RPC 代码生成
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
goctl rpc 是 `goctl` 脚手架下的 RPC 服务代码生成模块,基于 `.proto` 文件生成完整的 zRPC 服务代码。你只需编写 proto 定义和业务逻辑,其余代码均由工具自动生成。
|
||||
|
||||
## 特性
|
||||
|
||||
- **贴近 protoc**:与 protoc 完全兼容,透传所有 protoc 参数
|
||||
- **外部 Proto 导入**:支持跨目录、跨包的 proto 导入,自动解析传递性依赖
|
||||
- **多服务模式**:单个 proto 文件中定义多个 service,按服务名自动分组
|
||||
- **流式支持**:支持服务端流、客户端流和双向流
|
||||
- **Google 标准类型**:自动识别 `google.protobuf.*` 类型并生成正确的 Go 导入
|
||||
- **客户端生成**:自动生成封装好的 RPC 客户端代码
|
||||
|
||||
## 前置条件
|
||||
|
||||
```bash
|
||||
# 安装 protoc 插件
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 方式一:一键创建服务
|
||||
|
||||
```bash
|
||||
goctl rpc new greeter
|
||||
```
|
||||
|
||||
生成完整的项目结构:
|
||||
|
||||
```
|
||||
greeter/
|
||||
├── etc/
|
||||
│ └── greeter.yaml
|
||||
├── greeter/
|
||||
│ ├── greeter.pb.go
|
||||
│ └── greeter_grpc.pb.go
|
||||
├── greeter.go
|
||||
├── greeter.proto
|
||||
├── greeterclient/
|
||||
│ └── greeter.go
|
||||
└── internal/
|
||||
├── config/
|
||||
│ └── config.go
|
||||
├── logic/
|
||||
│ └── pinglogic.go
|
||||
├── server/
|
||||
│ └── greeterserver.go
|
||||
└── svc/
|
||||
└── servicecontext.go
|
||||
```
|
||||
|
||||
### 方式二:基于 Proto 文件生成
|
||||
|
||||
1. 生成 proto 模板:
|
||||
|
||||
```bash
|
||||
goctl rpc template -o=user.proto
|
||||
```
|
||||
|
||||
2. 初始化输出目录并生成服务代码:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
goctl rpc protoc user.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 命令参考
|
||||
|
||||
### `goctl rpc protoc`
|
||||
|
||||
从 `.proto` 文件生成 zRPC 服务代码。
|
||||
|
||||
```bash
|
||||
goctl rpc protoc <proto_file> [flags]
|
||||
```
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 基础用法
|
||||
goctl rpc protoc user.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I .
|
||||
|
||||
# 多服务模式
|
||||
goctl rpc protoc multi.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I . -m
|
||||
|
||||
# 导入外部 proto
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I . -I ./shared_protos
|
||||
|
||||
# 使用 Google 标准类型
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I .
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
|
||||
| 参数 | 缩写 | 类型 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| `--zrpc_out` | | string | **必填** | zRPC 服务代码输出目录 |
|
||||
| `--go_out` | | string | **必填** | protoc Go 代码输出目录 |
|
||||
| `--go-grpc_out` | | string | **必填** | protoc gRPC 代码输出目录 |
|
||||
| `--go_opt` | | string | | protoc-gen-go 选项(如 `module=example.com/demo`) |
|
||||
| `--go-grpc_opt` | | string | | protoc-gen-go-grpc 选项(如 `module=example.com/demo`) |
|
||||
| `--proto_path` | `-I` | string[] | | proto 导入搜索目录(可多次指定) |
|
||||
| `--multiple` | `-m` | bool | `false` | 多服务模式 |
|
||||
| `--client` | `-c` | bool | `true` | 是否生成 RPC 客户端代码 |
|
||||
| `--style` | | string | `gozero` | 文件命名风格 |
|
||||
| `--module` | | string | | 自定义 Go module 名称 |
|
||||
| `--name-from-filename` | | bool | `false` | 使用文件名而非 package 名命名服务 |
|
||||
| `--verbose` | `-v` | bool | `false` | 显示详细日志 |
|
||||
| `--home` | | string | | goctl 模板目录 |
|
||||
| `--remote` | | string | | 远程模板 Git 仓库地址 |
|
||||
| `--branch` | | string | | 远程模板分支 |
|
||||
|
||||
### `goctl rpc new`
|
||||
|
||||
快速创建一个完整的 RPC 服务项目。
|
||||
|
||||
```bash
|
||||
goctl rpc new <service_name> [flags]
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
|
||||
| 参数 | 缩写 | 类型 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| `--style` | | string | `gozero` | 文件命名风格 |
|
||||
| `--client` | `-c` | bool | `true` | 是否生成 RPC 客户端代码 |
|
||||
| `--module` | | string | | 自定义 Go module 名称 |
|
||||
| `--verbose` | `-v` | bool | `false` | 显示详细日志 |
|
||||
| `--idea` | | bool | `false` | 生成 IDE 项目标记 |
|
||||
| `--name-from-filename` | | bool | `false` | 使用文件名而非 package 名命名服务 |
|
||||
| `--home` | | string | | goctl 模板目录 |
|
||||
| `--remote` | | string | | 远程模板 Git 仓库地址 |
|
||||
| `--branch` | | string | | 远程模板分支 |
|
||||
|
||||
### `goctl rpc template`
|
||||
|
||||
生成 proto 文件模板。
|
||||
|
||||
```bash
|
||||
goctl rpc template -o=<output_file> [flags]
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `-o` | string | 输出文件路径(必填) |
|
||||
| `--home` | string | goctl 模板目录 |
|
||||
| `--remote` | string | 远程模板 Git 仓库地址 |
|
||||
| `--branch` | string | 远程模板分支 |
|
||||
|
||||
---
|
||||
|
||||
## 功能详解
|
||||
|
||||
### 多服务模式(`--multiple`)
|
||||
|
||||
当 proto 文件包含多个 `service` 定义时,必须使用 `--multiple` 标志。
|
||||
|
||||
```protobuf
|
||||
service SearchService {
|
||||
rpc Search(SearchReq) returns (SearchReply);
|
||||
}
|
||||
|
||||
service NotifyService {
|
||||
rpc Notify(NotifyReq) returns (NotifyReply);
|
||||
}
|
||||
```
|
||||
|
||||
**启用 `--multiple` 后的目录变化:**
|
||||
|
||||
| 特性 | 默认模式 | `--multiple` 模式 |
|
||||
|------|---------|-------------------|
|
||||
| 服务数量 | 仅 1 个 | 1 个或多个 |
|
||||
| 客户端目录 | 以服务名命名 | 固定为 `client/` |
|
||||
| 代码组织 | 扁平结构 | 按服务名分组 |
|
||||
|
||||
**`--multiple=false`(默认)的目录结构:**
|
||||
|
||||
```
|
||||
output/
|
||||
├── greeterclient/
|
||||
│ └── greeter.go
|
||||
├── internal/
|
||||
│ ├── logic/
|
||||
│ │ └── sayhellologic.go
|
||||
│ └── server/
|
||||
│ └── greeterserver.go
|
||||
└── ...
|
||||
```
|
||||
|
||||
**`--multiple=true` 的目录结构:**
|
||||
|
||||
```
|
||||
output/
|
||||
├── client/
|
||||
│ ├── searchservice/
|
||||
│ │ └── searchservice.go
|
||||
│ └── notifyservice/
|
||||
│ └── notifyservice.go
|
||||
├── internal/
|
||||
│ ├── logic/
|
||||
│ │ ├── searchservice/
|
||||
│ │ │ └── searchlogic.go
|
||||
│ │ └── notifyservice/
|
||||
│ │ └── notifylogic.go
|
||||
│ └── server/
|
||||
│ ├── searchservice/
|
||||
│ │ └── searchserviceserver.go
|
||||
│ └── notifyservice/
|
||||
│ └── notifyserviceserver.go
|
||||
└── ...
|
||||
```
|
||||
|
||||
### 外部 Proto 导入(`--proto_path`)
|
||||
|
||||
通过 `-I` / `--proto_path` 指定额外的 proto 搜索目录,支持以下场景:
|
||||
|
||||
- **同目录导入**:`import "types.proto";`
|
||||
- **子目录导入**:`import "common/types.proto";`
|
||||
- **外部目录导入**:proto 文件位于项目外部
|
||||
- **传递性导入**:A 导入 B,B 导入 C,goctl 自动递归解析
|
||||
- **跨包导入**:不同 `go_package` 的 proto 文件,自动生成正确的 Go 导入
|
||||
|
||||
```bash
|
||||
# 从多个目录搜索 proto 文件
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I . -I ./shared_protos -I /path/to/external_protos
|
||||
```
|
||||
|
||||
### 服务命名
|
||||
|
||||
默认情况下,服务名称来自 proto 的 **package 名称**(例如 `package user;` → 服务名 `user`)。这使得多个 proto 文件可以共享同一个 package:
|
||||
|
||||
```
|
||||
protos/
|
||||
├── user_base.proto # package user;
|
||||
├── user_auth.proto # package user;
|
||||
└── user_profile.proto # package user;
|
||||
```
|
||||
|
||||
三个文件会生成到同一个 `user` 服务中。
|
||||
|
||||
如需使用 proto 文件名命名(旧版行为),请添加 `--name-from-filename` 标志。
|
||||
|
||||
### 流式 RPC
|
||||
|
||||
支持 gRPC 的三种流式模式:
|
||||
|
||||
```protobuf
|
||||
service StreamService {
|
||||
rpc ServerStream(Req) returns (stream Reply); // 服务端流
|
||||
rpc ClientStream(stream Req) returns (Reply); // 客户端流
|
||||
rpc BidiStream(stream Req) returns (stream Reply); // 双向流
|
||||
}
|
||||
```
|
||||
|
||||
### Google 标准类型
|
||||
|
||||
goctl 自动识别并正确处理 Google protobuf 标准类型:
|
||||
|
||||
| Proto 类型 | Go 类型 |
|
||||
|-----------|---------|
|
||||
| `google.protobuf.Empty` | `emptypb.Empty` |
|
||||
| `google.protobuf.Timestamp` | `timestamppb.Timestamp` |
|
||||
| `google.protobuf.Duration` | `durationpb.Duration` |
|
||||
| `google.protobuf.Any` | `anypb.Any` |
|
||||
| `google.protobuf.Struct` | `structpb.Struct` |
|
||||
| `google.protobuf.FieldMask` | `fieldmaskpb.FieldMask` |
|
||||
| `google.protobuf.*Value` | `wrapperspb.*Value` |
|
||||
|
||||
这些类型可直接用作 RPC 参数类型,goctl 会自动生成正确的导入。
|
||||
|
||||
---
|
||||
|
||||
## 完整示例
|
||||
|
||||
详见 [example/](example/) 目录,包含 10 个完整示例,覆盖所有生成场景。
|
||||
|
||||
| # | 示例 | 场景 |
|
||||
|---|------|------|
|
||||
| 01 | [基础服务](example/01-basic/) | 单服务,无导入 |
|
||||
| 02 | [同级导入](example/02-import-sibling/) | 导入同目录 proto |
|
||||
| 03 | [子目录导入](example/03-import-subdir/) | 导入子目录 proto |
|
||||
| 04 | [传递性导入](example/04-transitive-import/) | A → B → C 依赖链 |
|
||||
| 05 | [多服务](example/05-multiple-services/) | `--multiple` 模式 |
|
||||
| 06 | [标准类型](example/06-wellknown-types/) | 消息中使用 Timestamp 等 |
|
||||
| 07 | [外部 Proto(同包)](example/07-external-proto-same-pkg/) | 外部 proto,相同 go_package |
|
||||
| 08 | [外部 Proto(跨包)](example/08-external-proto-diff-pkg/) | 外部 proto,不同 go_package |
|
||||
| 09 | [标准类型作参数](example/09-google-types-as-rpc/) | Empty/Timestamp 作为 RPC 参数 |
|
||||
| 10 | [流式通信](example/10-streaming/) | 服务端/客户端/双向流 |
|
||||
@@ -1,315 +1,228 @@
|
||||
# goctl rpc — RPC Code Generation
|
||||
# Rpc Generation
|
||||
|
||||
English | [中文](README-cn.md)
|
||||
Goctl Rpc是`goctl`脚手架下的一个rpc服务代码生成模块,支持proto模板生成和rpc服务代码生成,通过此工具生成代码你只需要关注业务逻辑编写而不用去编写一些重复性的代码。这使得我们把精力重心放在业务上,从而加快了开发效率且降低了代码出错率。
|
||||
|
||||
goctl rpc is the RPC service code generation module of the `goctl` scaffold. It generates a complete zRPC service from `.proto` files. You only need to write the proto definition and business logic — all boilerplate code is generated automatically.
|
||||
## 特性
|
||||
|
||||
## Features
|
||||
* 简单易用
|
||||
* 快速提升开发效率
|
||||
* 出错率低
|
||||
* 贴近 protoc
|
||||
|
||||
- **protoc compatible**: Fully compatible with protoc, all protoc arguments are passed through
|
||||
- **External proto imports**: Cross-directory and cross-package proto imports with automatic transitive dependency resolution
|
||||
- **Multiple services**: Define multiple services in a single proto file, auto-grouped by service name
|
||||
- **Streaming support**: Server streaming, client streaming, and bidirectional streaming
|
||||
- **Google well-known types**: Automatic recognition of `google.protobuf.*` types with correct Go imports
|
||||
- **Client generation**: Auto-generated RPC client wrapper code
|
||||
|
||||
## Prerequisites
|
||||
## 快速开始
|
||||
|
||||
### 方式一:快速生成greet服务
|
||||
|
||||
通过命令 `goctl rpc new ${servieName}`生成
|
||||
|
||||
如生成greet rpc服务:
|
||||
|
||||
```Bash
|
||||
goctl rpc new greet
|
||||
```
|
||||
|
||||
执行后代码结构如下:
|
||||
|
||||
```text
|
||||
.
|
||||
└── greet
|
||||
├── etc
|
||||
│ └── greet.yaml
|
||||
├── greet
|
||||
│ ├── greet.go
|
||||
│ ├── greet.pb.go
|
||||
│ └── greet_grpc.pb.go
|
||||
├── greet.go
|
||||
├── greet.proto
|
||||
└── internal
|
||||
├── config
|
||||
│ └── config.go
|
||||
├── logic
|
||||
│ └── pinglogic.go
|
||||
├── server
|
||||
│ └── greetserver.go
|
||||
└── svc
|
||||
└── servicecontext.go
|
||||
```
|
||||
|
||||
### 方式二:通过指定proto生成rpc服务
|
||||
|
||||
* 生成proto模板
|
||||
|
||||
```Bash
|
||||
$ goctl rpc template -o=user.proto
|
||||
```
|
||||
|
||||
```proto
|
||||
syntax = "proto3";
|
||||
|
||||
package user;
|
||||
option go_package="./user";
|
||||
|
||||
message Request {
|
||||
string ping = 1;
|
||||
}
|
||||
|
||||
message Response {
|
||||
string pong = 1;
|
||||
}
|
||||
|
||||
service User {
|
||||
rpc Ping(Request) returns(Response);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
* 生成rpc服务代码
|
||||
|
||||
```bash
|
||||
# Install protoc plugins
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||
$ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=.
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Method 1: Create a Service Instantly
|
||||
## 用法
|
||||
|
||||
```bash
|
||||
goctl rpc new greeter
|
||||
### rpc 服务生成用法
|
||||
|
||||
```Bash
|
||||
$ goctl rpc protoc -h
|
||||
Generate grpc code
|
||||
|
||||
Usage:
|
||||
goctl rpc protoc [flags]
|
||||
|
||||
Examples:
|
||||
goctl rpc protoc xx.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.
|
||||
|
||||
Flags:
|
||||
--branch string The branch of the remote repo, it does work with --remote
|
||||
-h, --help help for protoc
|
||||
--home string The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority
|
||||
-m, --multiple Generated in multiple rpc service mode
|
||||
--name-from-filename Use proto filename instead of package name for service naming (legacy behavior)
|
||||
--remote string The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority
|
||||
The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure
|
||||
--style string The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] (default "gozero")
|
||||
-v, --verbose Enable log output
|
||||
--zrpc_out string The zrpc output directory
|
||||
```
|
||||
|
||||
Generates a complete project structure:
|
||||
### 参数说明
|
||||
|
||||
```
|
||||
greeter/
|
||||
├── etc/
|
||||
│ └── greeter.yaml
|
||||
├── greeter/
|
||||
│ ├── greeter.pb.go
|
||||
│ └── greeter_grpc.pb.go
|
||||
├── greeter.go
|
||||
├── greeter.proto
|
||||
├── greeterclient/
|
||||
│ └── greeter.go
|
||||
└── internal/
|
||||
├── config/
|
||||
│ └── config.go
|
||||
├── logic/
|
||||
│ └── pinglogic.go
|
||||
├── server/
|
||||
│ └── greeterserver.go
|
||||
└── svc/
|
||||
└── servicecontext.go
|
||||
```
|
||||
* --branch 指定远程仓库模板分支
|
||||
* --home 指定goctl模板根目录
|
||||
* -m, --multiple 指定生成多个rpc服务模式, 默认为 false, 如果为 false, 则只支持生成一个rpc service, 如果为 true, 则支持生成多个 rpc service,且多个 rpc service 会分组。
|
||||
* --name-from-filename 使用proto文件名而非package名称来命名服务(旧版行为)。默认使用package名称,这样可以支持多个proto文件共享同一个package。
|
||||
* --style 指定文件输出格式
|
||||
* -v, --verbose 显示日志
|
||||
* --zrpc_out 指定zrpc输出目录
|
||||
|
||||
### Method 2: Generate from a Proto File
|
||||
> ## --multiple
|
||||
> 是否开启多个 rpc service 生成,如果开启,则满足一下新特性
|
||||
> 1. 支持 1 到多个 rpc service
|
||||
> 2. 生成 rpc 服务会按照服务名称分组(尽管只有一个 rpc service)
|
||||
> 3. rpc client 的文件目录变更为固定名称 `client`
|
||||
>
|
||||
> 如果不开启,则和旧版本 rpc 生成逻辑一样(兼容)
|
||||
> 1. 有且只能有一个 rpc service
|
||||
|
||||
1. Generate a proto template:
|
||||
> ## Service Naming (Multi-Proto File Support)
|
||||
>
|
||||
> By default, the service name is derived from the **proto package name** (e.g., `package user;` → service name `user`).
|
||||
> This enables splitting a large proto file into multiple smaller files that share the same package name,
|
||||
> which is particularly useful for AI-assisted development where smaller files are easier to process.
|
||||
>
|
||||
> **Example: Multiple proto files with same package**
|
||||
> ```
|
||||
> protos/
|
||||
> ├── user_base.proto # package user;
|
||||
> ├── user_auth.proto # package user;
|
||||
> └── user_profile.proto # package user;
|
||||
> ```
|
||||
> All three files will generate into a single `user` service.
|
||||
>
|
||||
> **Legacy behavior (--name-from-filename)**
|
||||
>
|
||||
> If you need the old behavior where service name is derived from the proto filename,
|
||||
> use the `--name-from-filename` flag:
|
||||
> ```bash
|
||||
> goctl rpc protoc user.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=. --name-from-filename
|
||||
> ```
|
||||
|
||||
```bash
|
||||
goctl rpc template -o=user.proto
|
||||
```
|
||||
|
||||
2. Initialize the output directory and generate service code:
|
||||
## rpc 服务生成 example
|
||||
详情见 [example/rpc](https://github.com/zeromicro/go-zero/tree/master/tools/goctl/example)
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
goctl rpc protoc user.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Command Reference
|
||||
|
||||
### `goctl rpc protoc`
|
||||
|
||||
Generate zRPC service code from a `.proto` file.
|
||||
|
||||
```bash
|
||||
goctl rpc protoc <proto_file> [flags]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Basic usage
|
||||
goctl rpc protoc user.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I .
|
||||
|
||||
# Multiple services mode
|
||||
goctl rpc protoc multi.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I . -m
|
||||
|
||||
# Import external protos
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I . -I ./shared_protos
|
||||
|
||||
# Use Google well-known types
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I .
|
||||
```
|
||||
|
||||
**Flags:**
|
||||
|
||||
| Flag | Short | Type | Default | Description |
|
||||
|------|-------|------|---------|-------------|
|
||||
| `--zrpc_out` | | string | **required** | Output directory for zRPC service code |
|
||||
| `--go_out` | | string | **required** | Output directory for protoc Go code |
|
||||
| `--go-grpc_out` | | string | **required** | Output directory for protoc gRPC code |
|
||||
| `--go_opt` | | string | | Options for protoc-gen-go (e.g., `module=example.com/demo`) |
|
||||
| `--go-grpc_opt` | | string | | Options for protoc-gen-go-grpc (e.g., `module=example.com/demo`) |
|
||||
| `--proto_path` | `-I` | string[] | | Proto import search directories (repeatable) |
|
||||
| `--multiple` | `-m` | bool | `false` | Multiple services mode |
|
||||
| `--client` | `-c` | bool | `true` | Generate RPC client code |
|
||||
| `--style` | | string | `gozero` | File naming style |
|
||||
| `--module` | | string | | Custom Go module name |
|
||||
| `--name-from-filename` | | bool | `false` | Use filename instead of package name for service naming |
|
||||
| `--verbose` | `-v` | bool | `false` | Enable verbose logging |
|
||||
| `--home` | | string | | goctl template directory |
|
||||
| `--remote` | | string | | Remote template Git repository URL |
|
||||
| `--branch` | | string | | Remote template branch |
|
||||
|
||||
### `goctl rpc new`
|
||||
|
||||
Quickly create a complete RPC service project.
|
||||
|
||||
```bash
|
||||
goctl rpc new <service_name> [flags]
|
||||
```
|
||||
|
||||
**Flags:**
|
||||
|
||||
| Flag | Short | Type | Default | Description |
|
||||
|------|-------|------|---------|-------------|
|
||||
| `--style` | | string | `gozero` | File naming style |
|
||||
| `--client` | `-c` | bool | `true` | Generate RPC client code |
|
||||
| `--module` | | string | | Custom Go module name |
|
||||
| `--verbose` | `-v` | bool | `false` | Enable verbose logging |
|
||||
| `--idea` | | bool | `false` | Generate IDE project marker |
|
||||
| `--name-from-filename` | | bool | `false` | Use filename instead of package name for service naming |
|
||||
| `--home` | | string | | goctl template directory |
|
||||
| `--remote` | | string | | Remote template Git repository URL |
|
||||
| `--branch` | | string | | Remote template branch |
|
||||
|
||||
### `goctl rpc template`
|
||||
|
||||
Generate a proto file template.
|
||||
|
||||
```bash
|
||||
goctl rpc template -o=<output_file> [flags]
|
||||
```
|
||||
|
||||
**Flags:**
|
||||
|
||||
| Flag | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `-o` | string | Output file path (required) |
|
||||
| `--home` | string | goctl template directory |
|
||||
| `--remote` | string | Remote template Git repository URL |
|
||||
| `--branch` | string | Remote template branch |
|
||||
|
||||
---
|
||||
|
||||
## Feature Details
|
||||
|
||||
### Multiple Services Mode (`--multiple`)
|
||||
|
||||
When a proto file contains multiple `service` definitions, the `--multiple` flag is required.
|
||||
## --multiple 为 true 和 false 的目录区别
|
||||
源 proto 文件
|
||||
|
||||
```protobuf
|
||||
service SearchService {
|
||||
rpc Search(SearchReq) returns (SearchReply);
|
||||
syntax = "proto3";
|
||||
|
||||
package hello;
|
||||
|
||||
option go_package = "./hello";
|
||||
|
||||
message HelloReq {
|
||||
string in = 1;
|
||||
}
|
||||
|
||||
service NotifyService {
|
||||
rpc Notify(NotifyReq) returns (NotifyReply);
|
||||
message HelloResp {
|
||||
string msg = 1;
|
||||
}
|
||||
|
||||
service Greet {
|
||||
rpc SayHello(HelloReq) returns (HelloResp);
|
||||
}
|
||||
```
|
||||
|
||||
**Directory differences with `--multiple`:**
|
||||
### --multiple=true
|
||||
|
||||
| Feature | Default mode | `--multiple` mode |
|
||||
|---------|-------------|-------------------|
|
||||
| Services per proto | Exactly 1 | 1 or more |
|
||||
| Client directory | Named after service | Fixed `client/` directory |
|
||||
| Code organization | Flat structure | Grouped by service name |
|
||||
|
||||
**`--multiple=false` (default) directory structure:**
|
||||
|
||||
```
|
||||
output/
|
||||
├── greeterclient/
|
||||
│ └── greeter.go
|
||||
├── internal/
|
||||
│ ├── logic/
|
||||
│ │ └── sayhellologic.go
|
||||
│ └── server/
|
||||
│ └── greeterserver.go
|
||||
└── ...
|
||||
```text
|
||||
hello
|
||||
├── client // 区别1:rpc client 目录固定为 client 名称
|
||||
│ └── greet // 区别2:会按照 rpc service 名称分组
|
||||
│ └── greet.go
|
||||
├── etc
|
||||
│ └── hello.yaml
|
||||
├── hello.go
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ └── greet // 区别2:会按照 rpc service 名称分组
|
||||
│ │ └── sayhellologic.go
|
||||
│ ├── server
|
||||
│ │ └── greet // 区别2:会按照 rpc service 名称分组
|
||||
│ │ └── greetserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
└── pb
|
||||
└── hello
|
||||
├── hello.pb.go
|
||||
└── hello_grpc.pb.go
|
||||
```
|
||||
|
||||
**`--multiple=true` directory structure:**
|
||||
|
||||
```
|
||||
output/
|
||||
├── client/
|
||||
│ ├── searchservice/
|
||||
│ │ └── searchservice.go
|
||||
│ └── notifyservice/
|
||||
│ └── notifyservice.go
|
||||
├── internal/
|
||||
│ ├── logic/
|
||||
│ │ ├── searchservice/
|
||||
│ │ │ └── searchlogic.go
|
||||
│ │ └── notifyservice/
|
||||
│ │ └── notifylogic.go
|
||||
│ └── server/
|
||||
│ ├── searchservice/
|
||||
│ │ └── searchserviceserver.go
|
||||
│ └── notifyservice/
|
||||
│ └── notifyserviceserver.go
|
||||
└── ...
|
||||
```
|
||||
|
||||
### External Proto Imports (`--proto_path`)
|
||||
|
||||
Use `-I` / `--proto_path` to specify additional proto search directories. Supported scenarios:
|
||||
|
||||
- **Same-directory import**: `import "types.proto";`
|
||||
- **Subdirectory import**: `import "common/types.proto";`
|
||||
- **External directory import**: Proto files outside the project
|
||||
- **Transitive imports**: A imports B, B imports C — goctl resolves recursively
|
||||
- **Cross-package imports**: Different `go_package` values generate correct Go imports automatically
|
||||
|
||||
```bash
|
||||
# Search multiple directories for proto files
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I . -I ./shared_protos -I /path/to/external_protos
|
||||
```
|
||||
|
||||
### Service Naming
|
||||
|
||||
By default, the service name is derived from the proto **package name** (e.g., `package user;` → service name `user`). This allows multiple proto files to share the same package:
|
||||
|
||||
```
|
||||
protos/
|
||||
├── user_base.proto # package user;
|
||||
├── user_auth.proto # package user;
|
||||
└── user_profile.proto # package user;
|
||||
```
|
||||
|
||||
All three files generate into a single `user` service.
|
||||
|
||||
To use the proto filename for naming (legacy behavior), add the `--name-from-filename` flag.
|
||||
|
||||
### Streaming RPC
|
||||
|
||||
All three gRPC streaming patterns are supported:
|
||||
|
||||
```protobuf
|
||||
service StreamService {
|
||||
rpc ServerStream(Req) returns (stream Reply); // Server streaming
|
||||
rpc ClientStream(stream Req) returns (Reply); // Client streaming
|
||||
rpc BidiStream(stream Req) returns (stream Reply); // Bidirectional streaming
|
||||
}
|
||||
```
|
||||
|
||||
### Google Well-Known Types
|
||||
|
||||
goctl automatically recognizes and handles Google protobuf well-known types:
|
||||
|
||||
| Proto Type | Go Type |
|
||||
|-----------|---------|
|
||||
| `google.protobuf.Empty` | `emptypb.Empty` |
|
||||
| `google.protobuf.Timestamp` | `timestamppb.Timestamp` |
|
||||
| `google.protobuf.Duration` | `durationpb.Duration` |
|
||||
| `google.protobuf.Any` | `anypb.Any` |
|
||||
| `google.protobuf.Struct` | `structpb.Struct` |
|
||||
| `google.protobuf.FieldMask` | `fieldmaskpb.FieldMask` |
|
||||
| `google.protobuf.*Value` | `wrapperspb.*Value` |
|
||||
|
||||
These types can be used directly as RPC parameter types — goctl generates the correct imports automatically.
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
See the [example/](example/) directory for 10 complete examples covering all generation scenarios.
|
||||
|
||||
| # | Example | Scenario |
|
||||
|---|---------|----------|
|
||||
| 01 | [Basic service](example/01-basic/) | Single service, no imports |
|
||||
| 02 | [Sibling import](example/02-import-sibling/) | Import from same directory |
|
||||
| 03 | [Subdirectory import](example/03-import-subdir/) | Import from subdirectory |
|
||||
| 04 | [Transitive import](example/04-transitive-import/) | A → B → C dependency chain |
|
||||
| 05 | [Multiple services](example/05-multiple-services/) | `--multiple` mode |
|
||||
| 06 | [Well-known types](example/06-wellknown-types/) | Timestamp etc. in messages |
|
||||
| 07 | [External proto (same pkg)](example/07-external-proto-same-pkg/) | External proto, same go_package |
|
||||
| 08 | [External proto (diff pkg)](example/08-external-proto-diff-pkg/) | External proto, different go_package |
|
||||
| 09 | [Google types as params](example/09-google-types-as-rpc/) | Empty/Timestamp as RPC parameters |
|
||||
| 10 | [Streaming](example/10-streaming/) | Server/client/bidirectional streaming |
|
||||
### --multiple=false (旧版本目录,向后兼容)
|
||||
```text
|
||||
hello
|
||||
├── etc
|
||||
│ └── hello.yaml
|
||||
├── greet
|
||||
│ └── greet.go
|
||||
├── hello.go
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ └── sayhellologic.go
|
||||
│ ├── server
|
||||
│ │ └── greetserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
└── pb
|
||||
└── hello
|
||||
├── hello.pb.go
|
||||
└── hello_grpc.pb.go
|
||||
```
|
||||
@@ -98,7 +98,6 @@ func RPCNew(_ *cobra.Command, args []string) error {
|
||||
ctx.IsGenClient = VarBoolClient
|
||||
ctx.Module = VarStringModule
|
||||
ctx.NameFromFilename = VarBoolNameFromFilename
|
||||
ctx.ProtoPaths = []string{filepath.Dir(src)}
|
||||
|
||||
grpcOptList := VarStringSliceGoGRPCOpt
|
||||
if len(grpcOptList) > 0 {
|
||||
|
||||
@@ -105,7 +105,6 @@ func ZRPC(_ *cobra.Command, args []string) error {
|
||||
ctx.IsGenClient = VarBoolClient
|
||||
ctx.Module = VarStringModule
|
||||
ctx.NameFromFilename = VarBoolNameFromFilename
|
||||
ctx.ProtoPaths = VarStringSliceProtoPath
|
||||
g := generator.NewGenerator(style, verbose)
|
||||
return g.Generate(&ctx)
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
# 示例 01:基础 RPC 服务
|
||||
|
||||
这是使用 goctl 生成 RPC 服务的最简单示例。
|
||||
|
||||
## Proto 定义
|
||||
|
||||
一个 `greeter.proto` 文件,包含一个服务和一个 RPC 方法,无外部导入。
|
||||
|
||||
`go_package` 使用完整的模块路径:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/greeter";
|
||||
```
|
||||
|
||||
## 生成命令
|
||||
|
||||
### 方式一:使用 `goctl rpc new` 快速创建
|
||||
|
||||
```bash
|
||||
# 一条命令创建完整的 RPC 项目
|
||||
goctl rpc new greeter
|
||||
```
|
||||
|
||||
该命令会同时生成 proto 文件和服务代码:
|
||||
|
||||
```
|
||||
greeter/
|
||||
├── etc
|
||||
│ └── greeter.yaml
|
||||
├── greeter
|
||||
│ ├── greeter.pb.go
|
||||
│ └── greeter_grpc.pb.go
|
||||
├── greeter.go
|
||||
├── greeter.proto
|
||||
├── greeterclient
|
||||
│ └── greeter.go
|
||||
└── internal
|
||||
├── config
|
||||
│ └── config.go
|
||||
├── logic
|
||||
│ └── pinglogic.go
|
||||
├── server
|
||||
│ └── greeterserver.go
|
||||
└── svc
|
||||
└── servicecontext.go
|
||||
```
|
||||
|
||||
### 方式二:基于已有 Proto 文件生成
|
||||
|
||||
首先,在输出目录中初始化 `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
然后生成代码:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc greeter.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
生成的目录结构:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── greeter.yaml
|
||||
├── go.mod
|
||||
├── greeter
|
||||
│ ├── greeter.pb.go
|
||||
│ └── greeter_grpc.pb.go
|
||||
├── greeter.go
|
||||
├── greeterclient
|
||||
│ └── greeter.go
|
||||
└── internal
|
||||
├── config
|
||||
│ └── config.go
|
||||
├── logic
|
||||
│ └── sayhellologic.go
|
||||
├── server
|
||||
│ └── greeterserver.go
|
||||
└── svc
|
||||
└── servicecontext.go
|
||||
```
|
||||
|
||||
## 要点说明
|
||||
|
||||
- 这是最简单的场景:一个 proto 文件、一个服务、一个 RPC 方法。
|
||||
- `go_package` 使用完整的模块路径(`example.com/demo/greeter`),而非相对路径。
|
||||
- `--module` 告诉 goctl Go 模块名;`--go_opt=module=...` 和 `--go-grpc_opt=module=...` 告诉 protoc 从输出路径中去除模块前缀。
|
||||
- `--zrpc_out` 指定 goctl 生成的服务代码输出目录。
|
||||
- `--go_out` 和 `--go-grpc_out` 指定 protoc 生成代码的输出目录。
|
||||
- 编辑逻辑文件(`internal/logic/sayhellologic.go`)来实现业务逻辑。
|
||||
@@ -1,100 +0,0 @@
|
||||
# Example 01: Basic RPC Service
|
||||
|
||||
This is the simplest example of generating an RPC service with goctl.
|
||||
|
||||
## Proto Definition
|
||||
|
||||
A single `greeter.proto` file with one service and one RPC method, no external imports.
|
||||
|
||||
The `go_package` uses a full module path:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/greeter";
|
||||
```
|
||||
|
||||
## Generation Commands
|
||||
|
||||
### Method 1: Quick Start with `goctl rpc new`
|
||||
|
||||
```bash
|
||||
# Create a complete RPC project with one command
|
||||
goctl rpc new greeter
|
||||
```
|
||||
|
||||
This generates the proto file and service code together:
|
||||
|
||||
```
|
||||
greeter/
|
||||
├── etc
|
||||
│ └── greeter.yaml
|
||||
├── greeter
|
||||
│ ├── greeter.pb.go
|
||||
│ └── greeter_grpc.pb.go
|
||||
├── greeter.go
|
||||
├── greeter.proto
|
||||
├── greeterclient
|
||||
│ └── greeter.go
|
||||
└── internal
|
||||
├── config
|
||||
│ └── config.go
|
||||
├── logic
|
||||
│ └── pinglogic.go
|
||||
├── server
|
||||
│ └── greeterserver.go
|
||||
└── svc
|
||||
└── servicecontext.go
|
||||
```
|
||||
|
||||
### Method 2: Generate from an Existing Proto
|
||||
|
||||
First, initialize the output directory with a `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
Then generate the code:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc greeter.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
Generated directory structure:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── greeter.yaml
|
||||
├── go.mod
|
||||
├── greeter
|
||||
│ ├── greeter.pb.go
|
||||
│ └── greeter_grpc.pb.go
|
||||
├── greeter.go
|
||||
├── greeterclient
|
||||
│ └── greeter.go
|
||||
└── internal
|
||||
├── config
|
||||
│ └── config.go
|
||||
├── logic
|
||||
│ └── sayhellologic.go
|
||||
├── server
|
||||
│ └── greeterserver.go
|
||||
└── svc
|
||||
└── servicecontext.go
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- This is the simplest scenario: one proto file, one service, one RPC method.
|
||||
- The `go_package` uses a full module path (`example.com/demo/greeter`), not a relative path.
|
||||
- The `--module` flag tells goctl the Go module name; `--go_opt=module=...` and `--go-grpc_opt=module=...` tell protoc to strip the module prefix from output paths.
|
||||
- The `--zrpc_out` flag specifies where the goctl-generated service code goes.
|
||||
- The `--go_out` and `--go-grpc_out` flags specify where protoc-generated code goes.
|
||||
- Edit the logic file (`internal/logic/sayhellologic.go`) to implement your business logic.
|
||||
@@ -1,17 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package greeter;
|
||||
|
||||
option go_package = "example.com/demo/greeter";
|
||||
|
||||
message HelloReq {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message HelloReply {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
service Greeter {
|
||||
rpc SayHello(HelloReq) returns (HelloReply);
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
# 示例 02:导入同级 Proto 文件
|
||||
|
||||
本示例演示如何导入同一目录下的 proto 文件。
|
||||
|
||||
## Proto 定义
|
||||
|
||||
同一目录下的两个 proto 文件共享相同的 `go_package`:
|
||||
|
||||
- `types.proto` — 定义共享消息类型(`User`)。
|
||||
- `user.proto` — 定义 RPC 服务,导入 `types.proto`。
|
||||
|
||||
两个文件使用相同的 `go_package`,采用完整模块路径:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
`user.proto` 通过以下方式导入 `types.proto`:
|
||||
|
||||
```protobuf
|
||||
import "types.proto";
|
||||
```
|
||||
|
||||
## 生成命令
|
||||
|
||||
首先,在输出目录中初始化 `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
然后生成代码:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc user.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
生成的目录结构:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── usersvc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── createuserlogic.go
|
||||
│ │ └── getuserlogic.go
|
||||
│ ├── server
|
||||
│ │ └── userserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── pb
|
||||
│ ├── types.pb.go
|
||||
│ ├── user.pb.go
|
||||
│ └── user_grpc.pb.go
|
||||
├── userservice
|
||||
│ └── userservice.go
|
||||
└── usersvc.go
|
||||
```
|
||||
|
||||
## 要点说明
|
||||
|
||||
- 两个 proto 文件(`user.proto` 和 `types.proto`)共享相同的 `go_package = "example.com/demo/pb"`,编译到同一个 Go 包中。
|
||||
- `user.proto` 通过 `import "types.proto"` 导入 `types.proto`。
|
||||
- 多个 proto 文件共享相同的 `go_package` 时,它们会编译到同一个 Go 包中。
|
||||
- 只需将包含 `service` 定义的 proto 文件传递给 `goctl rpc protoc`。
|
||||
- 导入的 proto 文件会被 protoc 自动编译,并由 goctl 自动解析。
|
||||
@@ -1,77 +0,0 @@
|
||||
# Example 02: Importing a Sibling Proto File
|
||||
|
||||
This example demonstrates importing a proto file from the same directory.
|
||||
|
||||
## Proto Definition
|
||||
|
||||
Two proto files in the same directory share the same `go_package`:
|
||||
|
||||
- `types.proto` — Defines shared message types (`User`).
|
||||
- `user.proto` — Defines the RPC service, importing `types.proto`.
|
||||
|
||||
Both files use the same `go_package` with a full module path:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
`user.proto` imports `types.proto` via:
|
||||
|
||||
```protobuf
|
||||
import "types.proto";
|
||||
```
|
||||
|
||||
## Generation Commands
|
||||
|
||||
First, initialize the output directory with a `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
Then generate the code:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc user.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
Generated directory structure:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── usersvc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── createuserlogic.go
|
||||
│ │ └── getuserlogic.go
|
||||
│ ├── server
|
||||
│ │ └── userserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── pb
|
||||
│ ├── types.pb.go
|
||||
│ ├── user.pb.go
|
||||
│ └── user_grpc.pb.go
|
||||
├── userservice
|
||||
│ └── userservice.go
|
||||
└── usersvc.go
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- Two proto files (`user.proto` and `types.proto`) share the same `go_package = "example.com/demo/pb"`, compiled into a single Go package.
|
||||
- `user.proto` imports `types.proto` via `import "types.proto"`.
|
||||
- When multiple proto files share the same `go_package`, they compile into a single Go package.
|
||||
- Only the proto file containing `service` definitions needs to be passed to `goctl rpc protoc`.
|
||||
- The imported proto is automatically compiled by protoc and resolved by goctl.
|
||||
@@ -1,11 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package types;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
message User {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
int32 age = 3;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package usersvc;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
import "types.proto";
|
||||
|
||||
message GetUserReq {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetUserReply {
|
||||
types.User user = 1;
|
||||
}
|
||||
|
||||
message CreateUserReq {
|
||||
string name = 1;
|
||||
int32 age = 2;
|
||||
}
|
||||
|
||||
message CreateUserReply {
|
||||
types.User user = 1;
|
||||
}
|
||||
|
||||
service UserService {
|
||||
rpc GetUser(GetUserReq) returns (GetUserReply);
|
||||
rpc CreateUser(CreateUserReq) returns (CreateUserReply);
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
# 示例 03:导入子目录中的 Proto 文件
|
||||
|
||||
本示例演示如何导入子目录中的 proto 文件。
|
||||
|
||||
## Proto 定义
|
||||
|
||||
两个 proto 文件有**不同**的 `go_package` 值:
|
||||
|
||||
- `order.proto` — 定义 `OrderService`,导入 `common/types.proto`。
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
- `common/types.proto` — 定义可复用的分页和排序消息。
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb/common";
|
||||
```
|
||||
|
||||
`order.proto` 从子目录导入 `common/types.proto`:
|
||||
|
||||
```protobuf
|
||||
import "common/types.proto";
|
||||
```
|
||||
|
||||
注意两个文件的 `go_package` **不同**,因此会编译到不同的 Go 包中。
|
||||
|
||||
## 生成命令
|
||||
|
||||
首先,在输出目录中初始化 `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
然后生成代码:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc order.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
生成的目录结构:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── ordersvc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── getorderlogic.go
|
||||
│ │ └── listorderslogic.go
|
||||
│ ├── server
|
||||
│ │ └── orderserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── orderservice
|
||||
│ └── orderservice.go
|
||||
├── ordersvc.go
|
||||
└── pb
|
||||
├── common
|
||||
│ └── types.pb.go
|
||||
├── order.pb.go
|
||||
└── order_grpc.pb.go
|
||||
```
|
||||
|
||||
## 要点说明
|
||||
|
||||
- 两个 proto 文件有**不同**的 `go_package` 值,编译到不同的 Go 包中(`pb/` 和 `pb/common/`)。
|
||||
- `order.proto` 从子目录导入 `common/types.proto`。
|
||||
- 当导入的 proto 文件有不同的 `go_package` 时,goctl 会自动生成跨包导入。
|
||||
- `-I .` 告诉 protoc 从当前目录开始搜索,使其能够找到 `common/types.proto`。
|
||||
@@ -1,82 +0,0 @@
|
||||
# Example 03: Importing Proto from a Subdirectory
|
||||
|
||||
This example demonstrates importing a proto file from a subdirectory.
|
||||
|
||||
## Proto Definition
|
||||
|
||||
Two proto files with **different** `go_package` values:
|
||||
|
||||
- `order.proto` — Defines the `OrderService`, imports `common/types.proto`.
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
- `common/types.proto` — Defines reusable pagination and sorting messages.
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb/common";
|
||||
```
|
||||
|
||||
`order.proto` imports `common/types.proto` from a subdirectory:
|
||||
|
||||
```protobuf
|
||||
import "common/types.proto";
|
||||
```
|
||||
|
||||
Note that the two files have **different** `go_package` values, so they compile into separate Go packages.
|
||||
|
||||
## Generation Commands
|
||||
|
||||
First, initialize the output directory with a `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
Then generate the code:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc order.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
Generated directory structure:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── ordersvc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── getorderlogic.go
|
||||
│ │ └── listorderslogic.go
|
||||
│ ├── server
|
||||
│ │ └── orderserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── orderservice
|
||||
│ └── orderservice.go
|
||||
├── ordersvc.go
|
||||
└── pb
|
||||
├── common
|
||||
│ └── types.pb.go
|
||||
├── order.pb.go
|
||||
└── order_grpc.pb.go
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- Two proto files have **different** `go_package` values, so they compile into separate Go packages (`pb/` and `pb/common/`).
|
||||
- `order.proto` imports `common/types.proto` from a subdirectory.
|
||||
- When imported protos have a different `go_package`, goctl automatically generates cross-package imports.
|
||||
- The `-I .` flag tells protoc to search from the current directory, enabling it to find `common/types.proto`.
|
||||
@@ -1,15 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common;
|
||||
|
||||
option go_package = "example.com/demo/pb/common";
|
||||
|
||||
message PageInfo {
|
||||
int32 page = 1;
|
||||
int32 size = 2;
|
||||
}
|
||||
|
||||
message SortInfo {
|
||||
string field = 1;
|
||||
string order = 2;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package ordersvc;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
import "common/types.proto";
|
||||
|
||||
message OrderItem {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
double price = 3;
|
||||
}
|
||||
|
||||
message ListOrdersReq {
|
||||
common.PageInfo page = 1;
|
||||
common.SortInfo sort = 2;
|
||||
}
|
||||
|
||||
message ListOrdersReply {
|
||||
repeated OrderItem orders = 1;
|
||||
}
|
||||
|
||||
message GetOrderReq {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetOrderReply {
|
||||
OrderItem order = 1;
|
||||
}
|
||||
|
||||
service OrderService {
|
||||
rpc ListOrders(ListOrdersReq) returns (ListOrdersReply);
|
||||
rpc GetOrder(GetOrderReq) returns (GetOrderReply);
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
# 示例 04:传递性导入
|
||||
|
||||
本示例演示 proto 的传递性导入,即 A 导入 B,B 导入 C。
|
||||
|
||||
## Proto 定义
|
||||
|
||||
三个 proto 文件形成传递导入链,共享相同的 `go_package`:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
- `base.proto` — C 层:定义基础类型(`BaseResp`)。
|
||||
- `middleware.proto` — B 层:导入 `base.proto`,定义 `RequestMeta`。
|
||||
- `main.proto` — A 层:导入 `middleware.proto`,定义 `PingService`(入口文件)。
|
||||
|
||||
导入链:`main.proto` → `middleware.proto` → `base.proto`
|
||||
|
||||
## 生成命令
|
||||
|
||||
首先,在输出目录中初始化 `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
然后生成代码:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc main.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
生成的目录结构:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── pingsvc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ └── pinglogic.go
|
||||
│ ├── server
|
||||
│ │ └── pingserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── pb
|
||||
│ ├── base.pb.go
|
||||
│ ├── main.pb.go
|
||||
│ ├── main_grpc.pb.go
|
||||
│ └── middleware.pb.go
|
||||
├── pingservice
|
||||
│ └── pingservice.go
|
||||
└── pingsvc.go
|
||||
```
|
||||
|
||||
## 要点说明
|
||||
|
||||
- 三个 proto 文件(`base.proto` → `middleware.proto` → `main.proto`)形成传递导入链。
|
||||
- goctl 自动递归解析所有传递导入。
|
||||
- 三个文件共享相同的 `go_package = "example.com/demo/pb"`。
|
||||
- 只需指定入口 proto 文件,goctl 和 protoc 会自动处理其余部分。
|
||||
- 循环导入会被检测并报错(与 protoc 行为一致)。
|
||||
@@ -1,72 +0,0 @@
|
||||
# Example 04: Transitive Imports
|
||||
|
||||
This example demonstrates transitive proto imports, where A imports B and B imports C.
|
||||
|
||||
## Proto Definition
|
||||
|
||||
Three proto files form a transitive import chain, all sharing the same `go_package`:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
- `base.proto` — Layer C: defines base types (`BaseResp`).
|
||||
- `middleware.proto` — Layer B: imports `base.proto`, defines `RequestMeta`.
|
||||
- `main.proto` — Layer A: imports `middleware.proto`, defines the `PingService` (entry point).
|
||||
|
||||
Import chain: `main.proto` → `middleware.proto` → `base.proto`
|
||||
|
||||
## Generation Commands
|
||||
|
||||
First, initialize the output directory with a `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
Then generate the code:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc main.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
Generated directory structure:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── pingsvc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ └── pinglogic.go
|
||||
│ ├── server
|
||||
│ │ └── pingserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── pb
|
||||
│ ├── base.pb.go
|
||||
│ ├── main.pb.go
|
||||
│ ├── main_grpc.pb.go
|
||||
│ └── middleware.pb.go
|
||||
├── pingservice
|
||||
│ └── pingservice.go
|
||||
└── pingsvc.go
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- Three proto files (`base.proto` → `middleware.proto` → `main.proto`) form a transitive import chain.
|
||||
- goctl recursively resolves all transitive imports automatically.
|
||||
- All three files share the same `go_package = "example.com/demo/pb"`.
|
||||
- You only need to specify the entry proto file — goctl and protoc handle the rest.
|
||||
- Circular imports are detected and will cause an error (same as protoc behavior).
|
||||
@@ -1,10 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package base;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
message BaseResp {
|
||||
int32 code = 1;
|
||||
string msg = 2;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package pingsvc;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
import "middleware.proto";
|
||||
|
||||
message PingReq {
|
||||
middleware.RequestMeta meta = 1;
|
||||
}
|
||||
|
||||
message PingReply {
|
||||
string pong = 1;
|
||||
}
|
||||
|
||||
service PingService {
|
||||
rpc Ping(PingReq) returns (PingReply);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package middleware;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
import "base.proto";
|
||||
|
||||
message RequestMeta {
|
||||
string trace_id = 1;
|
||||
base.BaseResp base = 2;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
# 示例 05:多服务模式(`--multiple`)
|
||||
|
||||
本示例演示从一个 proto 文件生成多个 RPC 服务。
|
||||
|
||||
## Proto 定义
|
||||
|
||||
两个 proto 文件共享相同的 `go_package`:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
- `shared.proto` — 定义共享消息类型(`Meta`)。
|
||||
- `multi.proto` — 定义了**两个**服务:`SearchService` 和 `NotifyService`。
|
||||
|
||||
当 proto 文件包含多个 `service` 块时,必须使用 `-m`(或 `--multiple`)标志。
|
||||
|
||||
## 生成命令
|
||||
|
||||
首先,在输出目录中初始化 `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
然后使用 `-m` 标志生成代码:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc multi.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I . \
|
||||
-m
|
||||
```
|
||||
|
||||
生成的目录结构:
|
||||
|
||||
```
|
||||
output/
|
||||
├── client
|
||||
│ ├── notifyservice
|
||||
│ │ └── notifyservice.go
|
||||
│ └── searchservice
|
||||
│ └── searchservice.go
|
||||
├── etc
|
||||
│ └── multisvc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── notifyservice
|
||||
│ │ │ └── notifylogic.go
|
||||
│ │ └── searchservice
|
||||
│ │ └── searchlogic.go
|
||||
│ ├── server
|
||||
│ │ ├── notifyservice
|
||||
│ │ │ └── notifyserviceserver.go
|
||||
│ │ └── searchservice
|
||||
│ │ └── searchserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── multisvc.go
|
||||
└── pb
|
||||
├── multi.pb.go
|
||||
├── multi_grpc.pb.go
|
||||
└── shared.pb.go
|
||||
```
|
||||
|
||||
## 要点说明
|
||||
|
||||
- `-m`(或 `--multiple`)标志启用多服务模式。
|
||||
- 多服务模式下,`client/` 包含按服务名分组的子目录;`logic/` 和 `server/` 也按服务名分组。
|
||||
- 两个服务共享一个入口文件(`multisvc.go`)和配置。
|
||||
- 不使用 `--multiple` 时,goctl 只允许每个 proto 文件有一个 `service` 块。
|
||||
- 所有服务共享同一个 `config.go` 和 `servicecontext.go`。
|
||||
@@ -1,80 +0,0 @@
|
||||
# Example 05: Multiple Services (`--multiple`)
|
||||
|
||||
This example demonstrates generating multiple RPC services from a single proto file.
|
||||
|
||||
## Proto Definition
|
||||
|
||||
Two proto files share the same `go_package`:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
- `shared.proto` — Defines shared message types (`Meta`).
|
||||
- `multi.proto` — Defines **two** services: `SearchService` and `NotifyService`.
|
||||
|
||||
The `-m` (or `--multiple`) flag is required when a proto file contains more than one `service` block.
|
||||
|
||||
## Generation Commands
|
||||
|
||||
First, initialize the output directory with a `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
Then generate the code with the `-m` flag:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc multi.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I . \
|
||||
-m
|
||||
```
|
||||
|
||||
Generated directory structure:
|
||||
|
||||
```
|
||||
output/
|
||||
├── client
|
||||
│ ├── notifyservice
|
||||
│ │ └── notifyservice.go
|
||||
│ └── searchservice
|
||||
│ └── searchservice.go
|
||||
├── etc
|
||||
│ └── multisvc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── notifyservice
|
||||
│ │ │ └── notifylogic.go
|
||||
│ │ └── searchservice
|
||||
│ │ └── searchlogic.go
|
||||
│ ├── server
|
||||
│ │ ├── notifyservice
|
||||
│ │ │ └── notifyserviceserver.go
|
||||
│ │ └── searchservice
|
||||
│ │ └── searchserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── multisvc.go
|
||||
└── pb
|
||||
├── multi.pb.go
|
||||
├── multi_grpc.pb.go
|
||||
└── shared.pb.go
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- The `-m` (or `--multiple`) flag enables multiple-service mode.
|
||||
- In multiple mode, `client/` contains per-service subdirectories; `logic/` and `server/` are also grouped by service name.
|
||||
- Both services share a single entry point (`multisvc.go`) and config.
|
||||
- Without `--multiple`, goctl only allows one `service` block per proto file.
|
||||
- All services share the same `config.go` and `servicecontext.go`.
|
||||
@@ -1,33 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package multisvc;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
import "shared.proto";
|
||||
|
||||
message SearchReq {
|
||||
shared.Meta meta = 1;
|
||||
string keyword = 2;
|
||||
}
|
||||
|
||||
message SearchReply {
|
||||
repeated string items = 1;
|
||||
}
|
||||
|
||||
message NotifyReq {
|
||||
shared.Meta meta = 1;
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
message NotifyReply {
|
||||
bool ok = 1;
|
||||
}
|
||||
|
||||
service SearchService {
|
||||
rpc Search(SearchReq) returns (SearchReply);
|
||||
}
|
||||
|
||||
service NotifyService {
|
||||
rpc Notify(NotifyReq) returns (NotifyReply);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package shared;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
message Meta {
|
||||
string trace_id = 1;
|
||||
string version = 2;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
# 示例 06:知名类型
|
||||
|
||||
本示例演示如何使用 Google protobuf 知名类型(`Timestamp`、`Duration`、`Any`)作为消息字段。
|
||||
|
||||
## Proto 定义
|
||||
|
||||
`events.proto` 使用 `google.protobuf.Timestamp` 作为消息字段类型。
|
||||
|
||||
`go_package` 使用完整的模块路径:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
## 生成命令
|
||||
|
||||
首先,在输出目录中初始化 `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
然后生成代码:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc events.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
生成的目录结构:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── eventsvc.yaml
|
||||
├── eventservice
|
||||
│ └── eventservice.go
|
||||
├── eventsvc.go
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── createeventlogic.go
|
||||
│ │ └── listeventslogic.go
|
||||
│ ├── server
|
||||
│ │ └── eventserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
└── pb
|
||||
├── events.pb.go
|
||||
└── events_grpc.pb.go
|
||||
```
|
||||
|
||||
## 要点说明
|
||||
|
||||
- 使用 Google 知名类型(`google.protobuf.Timestamp`、`google.protobuf.Duration`、`google.protobuf.Any`)作为消息字段。
|
||||
- goctl 自动将知名类型映射到 Go 导入包(`timestamppb`、`durationpb`、`anypb` 等)。
|
||||
- 如果 protoc 已正确安装,知名类型无需额外的 `--proto_path`。
|
||||
@@ -1,65 +0,0 @@
|
||||
# Example 06: Well-Known Types
|
||||
|
||||
This example demonstrates using Google protobuf well-known types (`Timestamp`, `Duration`, `Any`) as message fields.
|
||||
|
||||
## Proto Definition
|
||||
|
||||
`events.proto` uses `google.protobuf.Timestamp` as a message field type.
|
||||
|
||||
The `go_package` uses a full module path:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
## Generation Commands
|
||||
|
||||
First, initialize the output directory with a `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
Then generate the code:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc events.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
Generated directory structure:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── eventsvc.yaml
|
||||
├── eventservice
|
||||
│ └── eventservice.go
|
||||
├── eventsvc.go
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── createeventlogic.go
|
||||
│ │ └── listeventslogic.go
|
||||
│ ├── server
|
||||
│ │ └── eventserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
└── pb
|
||||
├── events.pb.go
|
||||
└── events_grpc.pb.go
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- Uses Google well-known types (`google.protobuf.Timestamp`, `google.protobuf.Duration`, `google.protobuf.Any`) as message fields.
|
||||
- goctl automatically maps well-known types to Go imports (`timestamppb`, `durationpb`, `anypb`, etc.).
|
||||
- No extra `--proto_path` needed for well-known types if protoc is properly installed.
|
||||
@@ -1,35 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package eventsvc;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
message Event {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
google.protobuf.Timestamp created_at = 3;
|
||||
}
|
||||
|
||||
message CreateEventReq {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message CreateEventReply {
|
||||
Event event = 1;
|
||||
}
|
||||
|
||||
message ListEventsReq {
|
||||
int32 page = 1;
|
||||
int32 size = 2;
|
||||
}
|
||||
|
||||
message ListEventsReply {
|
||||
repeated Event events = 1;
|
||||
}
|
||||
|
||||
service EventService {
|
||||
rpc CreateEvent(CreateEventReq) returns (CreateEventReply);
|
||||
rpc ListEvents(ListEventsReq) returns (ListEventsReply);
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
# 示例 07:外部 Proto — 相同 `go_package`
|
||||
|
||||
本示例演示从外部目录导入 proto 文件,且两个文件共享**相同**的 `go_package`。
|
||||
|
||||
## Proto 定义
|
||||
|
||||
`service.proto` 和 `ext.proto` 使用相同的 `go_package`:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
源码布局:
|
||||
|
||||
```
|
||||
07-external-proto-same-pkg/
|
||||
├── ext_protos
|
||||
│ └── ext.proto # 外部 proto(go_package = "example.com/demo/pb")
|
||||
├── service.proto # 服务定义(go_package = "example.com/demo/pb")
|
||||
├── README.md
|
||||
└── README-cn.md
|
||||
```
|
||||
|
||||
- `ext.proto` 位于独立目录(`ext_protos/`),但与 `service.proto` 有相同的 `go_package`。
|
||||
- `service.proto` 导入 `ext.proto`,使用 `ext.ExtReq` / `ext.ExtReply` 作为 RPC 类型。
|
||||
|
||||
## 生成命令
|
||||
|
||||
首先,在输出目录中初始化 `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
然后生成代码(注意 `-I ./ext_protos`):
|
||||
|
||||
```bash
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I . -I ./ext_protos
|
||||
```
|
||||
|
||||
生成的目录结构:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── svc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ └── querylogic.go
|
||||
│ ├── server
|
||||
│ │ └── queryserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── pb
|
||||
│ ├── ext.pb.go
|
||||
│ ├── service.pb.go
|
||||
│ └── service_grpc.pb.go
|
||||
├── queryservice
|
||||
│ └── queryservice.go
|
||||
└── svc.go
|
||||
```
|
||||
|
||||
## 要点说明
|
||||
|
||||
- `ext.proto` 位于独立目录(`ext_protos/`),但与 `service.proto` 有相同的 `go_package`。
|
||||
- 使用 `-I ./ext_protos` 将外部目录添加到 proto 搜索路径。
|
||||
- 当外部 proto 有**相同**的 `go_package` 时,所有类型合并到一个 Go 包中——无需跨包导入。
|
||||
@@ -1,77 +0,0 @@
|
||||
# Example 07: External Proto — Same `go_package`
|
||||
|
||||
This example demonstrates importing proto files from an external directory where both files share the **same** `go_package`.
|
||||
|
||||
## Proto Definition
|
||||
|
||||
Both `service.proto` and `ext.proto` use the same `go_package`:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
Source layout:
|
||||
|
||||
```
|
||||
07-external-proto-same-pkg/
|
||||
├── ext_protos
|
||||
│ └── ext.proto # External proto (go_package = "example.com/demo/pb")
|
||||
├── service.proto # Service definition (go_package = "example.com/demo/pb")
|
||||
├── README.md
|
||||
└── README-cn.md
|
||||
```
|
||||
|
||||
- `ext.proto` lives in a separate directory (`ext_protos/`), but has the same `go_package` as `service.proto`.
|
||||
- `service.proto` imports `ext.proto` and uses `ext.ExtReq` / `ext.ExtReply` as RPC types.
|
||||
|
||||
## Generation Commands
|
||||
|
||||
First, initialize the output directory with a `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
Then generate the code (note `-I ./ext_protos`):
|
||||
|
||||
```bash
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I . -I ./ext_protos
|
||||
```
|
||||
|
||||
Generated directory structure:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── svc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ └── querylogic.go
|
||||
│ ├── server
|
||||
│ │ └── queryserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── pb
|
||||
│ ├── ext.pb.go
|
||||
│ ├── service.pb.go
|
||||
│ └── service_grpc.pb.go
|
||||
├── queryservice
|
||||
│ └── queryservice.go
|
||||
└── svc.go
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- `ext.proto` lives in a separate directory (`ext_protos/`), but has the same `go_package` as `service.proto`.
|
||||
- Use `-I ./ext_protos` to add the external directory to the proto search path.
|
||||
- When the external proto has the **same** `go_package`, all types merge into one Go package — no cross-package imports are needed.
|
||||
@@ -1,14 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package ext;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
message ExtReq {
|
||||
string key = 1;
|
||||
}
|
||||
|
||||
message ExtReply {
|
||||
string value = 1;
|
||||
int32 code = 2;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package svc;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
import "ext.proto";
|
||||
|
||||
service QueryService {
|
||||
rpc Query(ext.ExtReq) returns (ext.ExtReply);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
# 示例 08:外部 Proto — 不同 `go_package`
|
||||
|
||||
本示例演示从外部目录导入 proto 文件,且文件具有**不同**的 `go_package` 值,需要在生成的 Go 代码中进行跨包导入。
|
||||
|
||||
## Proto 定义
|
||||
|
||||
proto 文件使用不同的 `go_package` 值:
|
||||
|
||||
- `service.proto`:`go_package = "example.com/demo/pb"`
|
||||
- `ext_protos/common/types.proto`:`go_package = "example.com/demo/pb/common"`
|
||||
|
||||
源码布局:
|
||||
|
||||
```
|
||||
08-external-proto-diff-pkg/
|
||||
├── ext_protos
|
||||
│ └── common
|
||||
│ └── types.proto # 外部 proto(go_package = "example.com/demo/pb/common")
|
||||
├── service.proto # 服务定义(go_package = "example.com/demo/pb")
|
||||
├── README.md
|
||||
└── README-cn.md
|
||||
```
|
||||
|
||||
- `types.proto` 的 `go_package = "example.com/demo/pb/common"` — **不同**的 Go 包。
|
||||
- `service.proto` 直接使用 `common.ExtReq` / `common.ExtReply` 作为 RPC 参数类型。
|
||||
|
||||
## 生成命令
|
||||
|
||||
首先,在输出目录中初始化 `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
然后生成代码(注意 `-I ./ext_protos`):
|
||||
|
||||
```bash
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I . -I ./ext_protos
|
||||
```
|
||||
|
||||
生成的目录结构:
|
||||
|
||||
```
|
||||
output/
|
||||
├── dataservice
|
||||
│ └── dataservice.go
|
||||
├── etc
|
||||
│ └── svc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ └── fetchlogic.go
|
||||
│ ├── server
|
||||
│ │ └── dataserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── pb
|
||||
│ ├── common
|
||||
│ │ └── types.pb.go
|
||||
│ ├── service.pb.go
|
||||
│ └── service_grpc.pb.go
|
||||
└── svc.go
|
||||
```
|
||||
|
||||
## 要点说明
|
||||
|
||||
- 当外部 proto 有**不同**的 `go_package` 时,goctl 会自动生成跨包 Go 导入。
|
||||
- goctl 通过解析导入 proto 的 `go_package` 选项,将 proto 包名(如 `common`)映射到正确的 Go 导入路径。
|
||||
- `service.proto` 直接使用 `common.ExtReq` / `common.ExtReply` 作为 RPC 参数类型。
|
||||
@@ -1,78 +0,0 @@
|
||||
# Example 08: External Proto — Different `go_package`
|
||||
|
||||
This example demonstrates importing proto files from an external directory where the files have **different** `go_package` values, requiring cross-package imports in the generated Go code.
|
||||
|
||||
## Proto Definition
|
||||
|
||||
The proto files use different `go_package` values:
|
||||
|
||||
- `service.proto`: `go_package = "example.com/demo/pb"`
|
||||
- `ext_protos/common/types.proto`: `go_package = "example.com/demo/pb/common"`
|
||||
|
||||
Source layout:
|
||||
|
||||
```
|
||||
08-external-proto-diff-pkg/
|
||||
├── ext_protos
|
||||
│ └── common
|
||||
│ └── types.proto # External proto (go_package = "example.com/demo/pb/common")
|
||||
├── service.proto # Service definition (go_package = "example.com/demo/pb")
|
||||
├── README.md
|
||||
└── README-cn.md
|
||||
```
|
||||
|
||||
- `types.proto` has `go_package = "example.com/demo/pb/common"` — a **different** Go package.
|
||||
- `service.proto` uses `common.ExtReq` / `common.ExtReply` directly as RPC parameter types.
|
||||
|
||||
## Generation Commands
|
||||
|
||||
First, initialize the output directory with a `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
Then generate the code (note `-I ./ext_protos`):
|
||||
|
||||
```bash
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I . -I ./ext_protos
|
||||
```
|
||||
|
||||
Generated directory structure:
|
||||
|
||||
```
|
||||
output/
|
||||
├── dataservice
|
||||
│ └── dataservice.go
|
||||
├── etc
|
||||
│ └── svc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ └── fetchlogic.go
|
||||
│ ├── server
|
||||
│ │ └── dataserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── pb
|
||||
│ ├── common
|
||||
│ │ └── types.pb.go
|
||||
│ ├── service.pb.go
|
||||
│ └── service_grpc.pb.go
|
||||
└── svc.go
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- When the external proto has a **different** `go_package`, goctl generates cross-package Go imports automatically.
|
||||
- goctl resolves the proto package name (e.g., `common`) to the correct Go import path by parsing the imported proto's `go_package` option.
|
||||
- `service.proto` uses `common.ExtReq` / `common.ExtReply` directly as RPC parameter types.
|
||||
@@ -1,15 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common;
|
||||
|
||||
option go_package = "example.com/demo/pb/common";
|
||||
|
||||
message ExtReq {
|
||||
string key = 1;
|
||||
string source = 2;
|
||||
}
|
||||
|
||||
message ExtReply {
|
||||
string value = 1;
|
||||
int32 code = 2;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package svc;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
import "common/types.proto";
|
||||
|
||||
service DataService {
|
||||
rpc Fetch(common.ExtReq) returns (common.ExtReply);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
# 示例 09:Google 类型作为 RPC 参数
|
||||
|
||||
本示例演示将 Google protobuf 知名类型**直接**用作 RPC 请求或响应类型(而不仅仅是消息字段)。
|
||||
|
||||
## Proto 定义
|
||||
|
||||
`service.proto` 使用 `google.protobuf.Empty` 和 `google.protobuf.Timestamp` 直接作为 RPC 请求/响应类型。
|
||||
|
||||
`go_package` 使用完整的模块路径:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
## 生成命令
|
||||
|
||||
首先,在输出目录中初始化 `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
然后生成代码:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
生成的目录结构:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── healthsvc.yaml
|
||||
├── go.mod
|
||||
├── healthservice
|
||||
│ └── healthservice.go
|
||||
├── healthsvc.go
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── gettimelogic.go
|
||||
│ │ └── pinglogic.go
|
||||
│ ├── server
|
||||
│ │ └── healthserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
└── pb
|
||||
├── service.pb.go
|
||||
└── service_grpc.pb.go
|
||||
```
|
||||
|
||||
## 要点说明
|
||||
|
||||
- 使用 Google 知名类型(`google.protobuf.Empty`、`google.protobuf.Timestamp`)直接作为 RPC 请求/响应类型(不仅仅是消息字段)。
|
||||
- goctl 正确将其映射到 Go 类型(`emptypb.Empty`、`timestamppb.Timestamp`)并生成正确的导入。
|
||||
- 这与示例 06 不同,示例 06 中知名类型用作消息字段。
|
||||
@@ -1,65 +0,0 @@
|
||||
# Example 09: Google Types as RPC Parameters
|
||||
|
||||
This example demonstrates using Google protobuf well-known types **directly** as RPC request or response types (not just as message fields).
|
||||
|
||||
## Proto Definition
|
||||
|
||||
`service.proto` uses `google.protobuf.Empty` and `google.protobuf.Timestamp` directly as RPC request/response types.
|
||||
|
||||
The `go_package` uses a full module path:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
## Generation Commands
|
||||
|
||||
First, initialize the output directory with a `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
Then generate the code:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc service.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
Generated directory structure:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── healthsvc.yaml
|
||||
├── go.mod
|
||||
├── healthservice
|
||||
│ └── healthservice.go
|
||||
├── healthsvc.go
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── gettimelogic.go
|
||||
│ │ └── pinglogic.go
|
||||
│ ├── server
|
||||
│ │ └── healthserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
└── pb
|
||||
├── service.pb.go
|
||||
└── service_grpc.pb.go
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- Uses Google well-known types (`google.protobuf.Empty`, `google.protobuf.Timestamp`) directly as RPC request/response types (not just message fields).
|
||||
- goctl correctly maps these to Go types (`emptypb.Empty`, `timestamppb.Timestamp`) and generates proper imports.
|
||||
- This differs from Example 06 where well-known types are used as message fields.
|
||||
@@ -1,19 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package healthsvc;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
message HealthCheckReq {
|
||||
string service = 1;
|
||||
}
|
||||
|
||||
service HealthService {
|
||||
// Ping returns empty — useful for health checks with no response body.
|
||||
rpc Ping(HealthCheckReq) returns (google.protobuf.Empty);
|
||||
// GetTime returns a Timestamp directly as the response type.
|
||||
rpc GetTime(HealthCheckReq) returns (google.protobuf.Timestamp);
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
# 示例 10:流式 RPC
|
||||
|
||||
本示例演示 gRPC 的三种流式通信模式:服务端流、客户端流和双向流。
|
||||
|
||||
## Proto 定义
|
||||
|
||||
`stream.proto` 定义了三个 RPC 方法,演示每种流式模式。
|
||||
|
||||
`go_package` 使用完整的模块路径:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
## 生成命令
|
||||
|
||||
首先,在输出目录中初始化 `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
然后生成代码:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc stream.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
生成的目录结构:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── streamsvc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── bidistreamlogic.go
|
||||
│ │ ├── clientstreamlogic.go
|
||||
│ │ └── serverstreamlogic.go
|
||||
│ ├── server
|
||||
│ │ └── streamserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── pb
|
||||
│ ├── stream.pb.go
|
||||
│ └── stream_grpc.pb.go
|
||||
├── streamservice
|
||||
│ └── streamservice.go
|
||||
└── streamsvc.go
|
||||
```
|
||||
|
||||
## 要点说明
|
||||
|
||||
- 支持三种流式模式:服务端流(响应带 `stream`)、客户端流(请求带 `stream`)和双向流(两端都带 `stream`)。
|
||||
- goctl 为每个流式 RPC 方法生成独立的逻辑文件。
|
||||
- 流式客户端代码不会自动生成,需直接使用 gRPC 客户端。
|
||||
@@ -1,66 +0,0 @@
|
||||
# Example 10: Streaming RPC
|
||||
|
||||
This example demonstrates all three gRPC streaming patterns: server streaming, client streaming, and bidirectional streaming.
|
||||
|
||||
## Proto Definition
|
||||
|
||||
`stream.proto` defines three RPC methods demonstrating each streaming pattern.
|
||||
|
||||
The `go_package` uses a full module path:
|
||||
|
||||
```protobuf
|
||||
option go_package = "example.com/demo/pb";
|
||||
```
|
||||
|
||||
## Generation Commands
|
||||
|
||||
First, initialize the output directory with a `go.mod`:
|
||||
|
||||
```bash
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
```
|
||||
|
||||
Then generate the code:
|
||||
|
||||
```bash
|
||||
goctl rpc protoc stream.proto \
|
||||
--go_out=output \
|
||||
--go-grpc_out=output \
|
||||
--zrpc_out=output \
|
||||
--go_opt=module=example.com/demo \
|
||||
--go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo \
|
||||
-I .
|
||||
```
|
||||
|
||||
Generated directory structure:
|
||||
|
||||
```
|
||||
output/
|
||||
├── etc
|
||||
│ └── streamsvc.yaml
|
||||
├── go.mod
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ ├── bidistreamlogic.go
|
||||
│ │ ├── clientstreamlogic.go
|
||||
│ │ └── serverstreamlogic.go
|
||||
│ ├── server
|
||||
│ │ └── streamserviceserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
├── pb
|
||||
│ ├── stream.pb.go
|
||||
│ └── stream_grpc.pb.go
|
||||
├── streamservice
|
||||
│ └── streamservice.go
|
||||
└── streamsvc.go
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- Supports three streaming patterns: server streaming (`stream` on response), client streaming (`stream` on request), and bidirectional streaming (`stream` on both).
|
||||
- goctl generates separate logic files for each streaming RPC method.
|
||||
- Streaming client code is not auto-generated; use the gRPC client directly.
|
||||
@@ -1,24 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package streamsvc;
|
||||
|
||||
option go_package = "example.com/demo/pb";
|
||||
|
||||
message StreamReq {
|
||||
string input = 1;
|
||||
}
|
||||
|
||||
message StreamReply {
|
||||
string output = 1;
|
||||
}
|
||||
|
||||
service StreamService {
|
||||
// ServerStream: client sends one request, server returns a stream of responses.
|
||||
rpc ServerStream(StreamReq) returns (stream StreamReply);
|
||||
|
||||
// ClientStream: client sends a stream of requests, server returns one response.
|
||||
rpc ClientStream(stream StreamReq) returns (StreamReply);
|
||||
|
||||
// BidiStream: both client and server send streams of messages.
|
||||
rpc BidiStream(stream StreamReq) returns (stream StreamReply);
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
# RPC Examples
|
||||
|
||||
This directory contains complete examples for all `goctl rpc` code generation scenarios.
|
||||
|
||||
Each example includes:
|
||||
- `.proto` source files
|
||||
- `README.md` (English) and `README-cn.md` (中文) documentation
|
||||
|
||||
## Examples
|
||||
|
||||
| # | Directory | Scenario | Key Flags |
|
||||
|---|-----------|----------|-----------|
|
||||
| 01 | [01-basic](01-basic/) | Basic single service, no imports | — |
|
||||
| 02 | [02-import-sibling](02-import-sibling/) | Import sibling proto file | `--proto_path=.` |
|
||||
| 03 | [03-import-subdir](03-import-subdir/) | Import proto from subdirectory | `--proto_path=.` |
|
||||
| 04 | [04-transitive-import](04-transitive-import/) | Transitive imports (A → B → C) | `--proto_path=.` |
|
||||
| 05 | [05-multiple-services](05-multiple-services/) | Multiple services in one proto | `--multiple` |
|
||||
| 06 | [06-wellknown-types](06-wellknown-types/) | Google well-known types in messages | `--proto_path=$PROTOC_INCLUDE` |
|
||||
| 07 | [07-external-proto-same-pkg](07-external-proto-same-pkg/) | External proto, same `go_package` | `-I ./ext_protos` |
|
||||
| 08 | [08-external-proto-diff-pkg](08-external-proto-diff-pkg/) | External proto, different `go_package` | `-I ./ext_protos` |
|
||||
| 09 | [09-google-types-as-rpc](09-google-types-as-rpc/) | Google types as RPC parameters | `--proto_path=$PROTOC_INCLUDE` |
|
||||
| 10 | [10-streaming](10-streaming/) | Server/client/bidirectional streaming | — |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Go](https://go.dev/) 1.22+
|
||||
- [protoc](https://github.com/protocolbuffers/protobuf/releases) (Protocol Buffers compiler)
|
||||
- [protoc-gen-go](https://pkg.go.dev/google.golang.org/protobuf/cmd/protoc-gen-go) and [protoc-gen-go-grpc](https://pkg.go.dev/google.golang.org/grpc/cmd/protoc-gen-go-grpc)
|
||||
- [goctl](https://github.com/zeromicro/go-zero/tree/master/tools/goctl)
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Install protoc plugins
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||
|
||||
# Try the basic example
|
||||
cd 01-basic
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
goctl rpc protoc greeter.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# RPC 示例
|
||||
|
||||
本目录包含所有 `goctl rpc` 代码生成场景的完整示例。
|
||||
|
||||
每个示例包含:
|
||||
- `.proto` 源文件
|
||||
- `README.md`(英文)和 `README-cn.md`(中文)文档
|
||||
|
||||
## 示例列表
|
||||
|
||||
| # | 目录 | 场景 | 关键标志 |
|
||||
|---|------|------|---------|
|
||||
| 01 | [01-basic](01-basic/) | 基础单服务,无导入 | — |
|
||||
| 02 | [02-import-sibling](02-import-sibling/) | 导入同级 proto 文件 | `--proto_path=.` |
|
||||
| 03 | [03-import-subdir](03-import-subdir/) | 导入子目录中的 proto | `--proto_path=.` |
|
||||
| 04 | [04-transitive-import](04-transitive-import/) | 传递性导入(A → B → C) | `--proto_path=.` |
|
||||
| 05 | [05-multiple-services](05-multiple-services/) | 单 proto 多服务 | `--multiple` |
|
||||
| 06 | [06-wellknown-types](06-wellknown-types/) | 消息中使用 Google 标准类型 | `--proto_path=$PROTOC_INCLUDE` |
|
||||
| 07 | [07-external-proto-same-pkg](07-external-proto-same-pkg/) | 外部 proto,相同 `go_package` | `-I ./ext_protos` |
|
||||
| 08 | [08-external-proto-diff-pkg](08-external-proto-diff-pkg/) | 外部 proto,不同 `go_package` | `-I ./ext_protos` |
|
||||
| 09 | [09-google-types-as-rpc](09-google-types-as-rpc/) | Google 类型作为 RPC 参数 | `--proto_path=$PROTOC_INCLUDE` |
|
||||
| 10 | [10-streaming](10-streaming/) | 服务端/客户端/双向流 | — |
|
||||
|
||||
## 前置条件
|
||||
|
||||
- [Go](https://go.dev/) 1.22+
|
||||
- [protoc](https://github.com/protocolbuffers/protobuf/releases)(Protocol Buffers 编译器)
|
||||
- [protoc-gen-go](https://pkg.go.dev/google.golang.org/protobuf/cmd/protoc-gen-go) 和 [protoc-gen-go-grpc](https://pkg.go.dev/google.golang.org/grpc/cmd/protoc-gen-go-grpc)
|
||||
- [goctl](https://github.com/zeromicro/go-zero/tree/master/tools/goctl)
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 安装 protoc 插件
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||
|
||||
# 试试基础示例
|
||||
cd 01-basic
|
||||
mkdir -p output && cd output && go mod init example.com/demo && cd ..
|
||||
goctl rpc protoc greeter.proto \
|
||||
--go_out=output --go-grpc_out=output --zrpc_out=output \
|
||||
--go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
|
||||
--module=example.com/demo -I .
|
||||
```
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
{{.pbPackage}}
|
||||
{{if ne .pbPackage .protoGoPackage}}{{.protoGoPackage}}{{end}}
|
||||
{{.extraImports}}
|
||||
|
||||
"github.com/zeromicro/go-zero/zrpc"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/zeromicro/go-zero/tools/goctl/rpc/parser"
|
||||
@@ -36,9 +35,6 @@ type ZRpcContext struct {
|
||||
// NameFromFilename uses proto filename instead of package name for service naming.
|
||||
// Default is false (uses package name, which supports multi-proto files).
|
||||
NameFromFilename bool
|
||||
// ProtoPaths are the directories to search for imported proto files,
|
||||
// equivalent to protoc -I flags. When empty the directory of Src is used.
|
||||
ProtoPaths []string
|
||||
}
|
||||
|
||||
// Generate generates a rpc service, through the proto file,
|
||||
@@ -76,12 +72,6 @@ func (g *Generator) Generate(zctx *ZRpcContext) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Populate ImportedProtos so that generators can resolve dotted type references.
|
||||
proto.ImportedProtos, err = resolveImportedProtos(zctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dirCtx, err := mkdir(projectCtx, proto, g.cfg, zctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -130,27 +120,3 @@ func (g *Generator) Generate(zctx *ZRpcContext) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// resolveImportedProtos builds the full list of transitively imported proto
|
||||
// files for zctx and returns their parsed metadata. This mirrors the proto
|
||||
// path computation in genpb.go so both use the same search paths.
|
||||
func resolveImportedProtos(zctx *ZRpcContext) ([]parser.ImportedProto, error) {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
protoPaths := make([]string, 0, len(zctx.ProtoPaths)+1)
|
||||
srcDir := filepath.Dir(zctx.Src)
|
||||
if !filepath.IsAbs(srcDir) {
|
||||
srcDir = filepath.Join(pwd, srcDir)
|
||||
}
|
||||
protoPaths = append(protoPaths, srcDir)
|
||||
for _, p := range zctx.ProtoPaths {
|
||||
if !filepath.IsAbs(p) {
|
||||
p = filepath.Join(pwd, p)
|
||||
}
|
||||
protoPaths = append(protoPaths, p)
|
||||
}
|
||||
return parser.ParseImportedProtos(zctx.Src, protoPaths)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ func (g *Generator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config
|
||||
func (g *Generator) genCallGroup(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
|
||||
dir := ctx.GetCall()
|
||||
head := util.GetHead(proto.Name)
|
||||
pkgMap := parser.BuildProtoPackageMap(proto.ImportedProtos)
|
||||
for _, service := range proto.Service {
|
||||
childPkg, err := dir.GetChildPackage(service.Name)
|
||||
if err != nil {
|
||||
@@ -65,39 +64,28 @@ func (g *Generator) genCallGroup(ctx DirContext, proto parser.Proto, cfg *conf.C
|
||||
isCallPkgSameToGrpcPkg := childDir == ctx.GetProtoGo().Filename
|
||||
|
||||
serviceName := stringx.From(service.Name).ToCamel()
|
||||
|
||||
// Collect only the message types actually used by this service's RPCs,
|
||||
// so that each client file only aliases its own request/response types.
|
||||
usedTypes := collection.NewSet[string]()
|
||||
for _, rpc := range service.RPC {
|
||||
usedTypes.Add(parser.CamelCase(rpc.RequestType))
|
||||
usedTypes.Add(parser.CamelCase(rpc.ReturnsType))
|
||||
}
|
||||
|
||||
alias := collection.NewSet[string]()
|
||||
var hasSameNameBetweenMessageAndService bool
|
||||
for _, item := range proto.Message {
|
||||
msgName := getMessageName(*item.Message)
|
||||
camelMsgName := parser.CamelCase(msgName)
|
||||
if serviceName == msgName {
|
||||
hasSameNameBetweenMessageAndService = true
|
||||
}
|
||||
if !isCallPkgSameToPbPkg && usedTypes.Contains(camelMsgName) {
|
||||
alias.Add(fmt.Sprintf("%s = %s", camelMsgName,
|
||||
fmt.Sprintf("%s.%s", proto.PbPackage, camelMsgName)))
|
||||
if !isCallPkgSameToPbPkg {
|
||||
alias.Add(fmt.Sprintf("%s = %s", parser.CamelCase(msgName),
|
||||
fmt.Sprintf("%s.%s", proto.PbPackage, parser.CamelCase(msgName))))
|
||||
}
|
||||
}
|
||||
if hasSameNameBetweenMessageAndService {
|
||||
serviceName = stringx.From(service.Name + "_zrpc_client").ToCamel()
|
||||
}
|
||||
|
||||
extraImports := collection.NewSet[string]()
|
||||
functions, err := g.genFunction(proto.PbPackage, proto.GoPackage, serviceName, service, isCallPkgSameToGrpcPkg, pkgMap, alias, extraImports)
|
||||
functions, err := g.genFunction(proto.PbPackage, serviceName, service, isCallPkgSameToGrpcPkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iFunctions, err := g.getInterfaceFuncs(proto.PbPackage, proto.GoPackage, service, isCallPkgSameToGrpcPkg, pkgMap, extraImports)
|
||||
iFunctions, err := g.getInterfaceFuncs(proto.PbPackage, service, isCallPkgSameToGrpcPkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -114,7 +102,6 @@ func (g *Generator) genCallGroup(ctx DirContext, proto parser.Proto, cfg *conf.C
|
||||
protoGoPackage = ""
|
||||
}
|
||||
|
||||
extraImportLines := buildExtraImportLines(extraImports)
|
||||
aliasKeys := alias.Keys()
|
||||
sort.Strings(aliasKeys)
|
||||
if err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]any{
|
||||
@@ -124,7 +111,6 @@ func (g *Generator) genCallGroup(ctx DirContext, proto parser.Proto, cfg *conf.C
|
||||
"filePackage": childDir,
|
||||
"pbPackage": pbPackage,
|
||||
"protoGoPackage": protoGoPackage,
|
||||
"extraImports": extraImportLines,
|
||||
"serviceName": serviceName,
|
||||
"functions": strings.Join(functions, pathx.NL),
|
||||
"interface": strings.Join(iFunctions, pathx.NL),
|
||||
@@ -166,15 +152,13 @@ func (g *Generator) genCallInCompatibility(ctx DirContext, proto parser.Proto,
|
||||
serviceName = stringx.From(service.Name + "_zrpc_client").ToCamel()
|
||||
}
|
||||
|
||||
pkgMap := parser.BuildProtoPackageMap(proto.ImportedProtos)
|
||||
extraImports := collection.NewSet[string]()
|
||||
filename := filepath.Join(dir.Filename, fmt.Sprintf("%s.go", callFilename))
|
||||
functions, err := g.genFunction(proto.PbPackage, proto.GoPackage, serviceName, service, isCallPkgSameToGrpcPkg, pkgMap, alias, extraImports)
|
||||
functions, err := g.genFunction(proto.PbPackage, serviceName, service, isCallPkgSameToGrpcPkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iFunctions, err := g.getInterfaceFuncs(proto.PbPackage, proto.GoPackage, service, isCallPkgSameToGrpcPkg, pkgMap, extraImports)
|
||||
iFunctions, err := g.getInterfaceFuncs(proto.PbPackage, service, isCallPkgSameToGrpcPkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -190,7 +174,6 @@ func (g *Generator) genCallInCompatibility(ctx DirContext, proto parser.Proto,
|
||||
pbPackage = ""
|
||||
protoGoPackage = ""
|
||||
}
|
||||
extraImportLines := buildExtraImportLines(extraImports)
|
||||
aliasKeys := alias.Keys()
|
||||
sort.Strings(aliasKeys)
|
||||
return util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]any{
|
||||
@@ -200,7 +183,6 @@ func (g *Generator) genCallInCompatibility(ctx DirContext, proto parser.Proto,
|
||||
"filePackage": dir.Base,
|
||||
"pbPackage": pbPackage,
|
||||
"protoGoPackage": protoGoPackage,
|
||||
"extraImports": extraImportLines,
|
||||
"serviceName": serviceName,
|
||||
"functions": strings.Join(functions, pathx.NL),
|
||||
"interface": strings.Join(iFunctions, pathx.NL),
|
||||
@@ -229,9 +211,8 @@ func getMessageName(msg proto.Message) string {
|
||||
return strings.Join(list, "_")
|
||||
}
|
||||
|
||||
func (g *Generator) genFunction(goPackage, mainGoPackage, serviceName string, service parser.Service,
|
||||
isCallPkgSameToGrpcPkg bool, pkgMap map[string]parser.ImportedProto,
|
||||
alias, extraImports *collection.Set[string]) ([]string, error) {
|
||||
func (g *Generator) genFunction(goPackage string, serviceName string, service parser.Service,
|
||||
isCallPkgSameToGrpcPkg bool) ([]string, error) {
|
||||
functions := make([]string, 0)
|
||||
|
||||
for _, rpc := range service.RPC {
|
||||
@@ -247,29 +228,13 @@ func (g *Generator) genFunction(goPackage, mainGoPackage, serviceName string, se
|
||||
streamServer = fmt.Sprintf("%s_%s%s", parser.CamelCase(service.Name),
|
||||
parser.CamelCase(rpc.Name), "Client")
|
||||
}
|
||||
|
||||
reqName, reqAlias, reqImport := resolveCallTypeRef(rpc.RequestType, goPackage, mainGoPackage, pkgMap)
|
||||
respName, respAlias, respImport := resolveCallTypeRef(rpc.ReturnsType, goPackage, mainGoPackage, pkgMap)
|
||||
if reqAlias != "" {
|
||||
alias.Add(reqAlias)
|
||||
}
|
||||
if respAlias != "" {
|
||||
alias.Add(respAlias)
|
||||
}
|
||||
if reqImport != "" {
|
||||
extraImports.Add(reqImport)
|
||||
}
|
||||
if respImport != "" {
|
||||
extraImports.Add(respImport)
|
||||
}
|
||||
|
||||
buffer, err := util.With("sharedFn").Parse(text).Execute(map[string]any{
|
||||
"serviceName": serviceName,
|
||||
"rpcServiceName": parser.CamelCase(service.Name),
|
||||
"method": parser.CamelCase(rpc.Name),
|
||||
"package": goPackage,
|
||||
"pbRequest": reqName,
|
||||
"pbResponse": respName,
|
||||
"pbRequest": parser.CamelCase(rpc.RequestType),
|
||||
"pbResponse": parser.CamelCase(rpc.ReturnsType),
|
||||
"hasComment": len(comment) > 0,
|
||||
"comment": comment,
|
||||
"hasReq": !rpc.StreamsRequest,
|
||||
@@ -287,9 +252,8 @@ func (g *Generator) genFunction(goPackage, mainGoPackage, serviceName string, se
|
||||
return functions, nil
|
||||
}
|
||||
|
||||
func (g *Generator) getInterfaceFuncs(goPackage, mainGoPackage string, service parser.Service,
|
||||
isCallPkgSameToGrpcPkg bool, pkgMap map[string]parser.ImportedProto,
|
||||
extraImports *collection.Set[string]) ([]string, error) {
|
||||
func (g *Generator) getInterfaceFuncs(goPackage string, service parser.Service,
|
||||
isCallPkgSameToGrpcPkg bool) ([]string, error) {
|
||||
functions := make([]string, 0)
|
||||
|
||||
for _, rpc := range service.RPC {
|
||||
@@ -306,25 +270,15 @@ func (g *Generator) getInterfaceFuncs(goPackage, mainGoPackage string, service p
|
||||
streamServer = fmt.Sprintf("%s_%s%s", parser.CamelCase(service.Name),
|
||||
parser.CamelCase(rpc.Name), "Client")
|
||||
}
|
||||
|
||||
reqName, _, reqImport := resolveCallTypeRef(rpc.RequestType, goPackage, mainGoPackage, pkgMap)
|
||||
respName, _, respImport := resolveCallTypeRef(rpc.ReturnsType, goPackage, mainGoPackage, pkgMap)
|
||||
if reqImport != "" {
|
||||
extraImports.Add(reqImport)
|
||||
}
|
||||
if respImport != "" {
|
||||
extraImports.Add(respImport)
|
||||
}
|
||||
|
||||
buffer, err := util.With("interfaceFn").Parse(text).Execute(
|
||||
map[string]any{
|
||||
"hasComment": len(comment) > 0,
|
||||
"comment": comment,
|
||||
"method": parser.CamelCase(rpc.Name),
|
||||
"hasReq": !rpc.StreamsRequest,
|
||||
"pbRequest": reqName,
|
||||
"pbRequest": parser.CamelCase(rpc.RequestType),
|
||||
"notStream": !rpc.StreamsRequest && !rpc.StreamsReturns,
|
||||
"pbResponse": respName,
|
||||
"pbResponse": parser.CamelCase(rpc.ReturnsType),
|
||||
"streamBody": streamServer,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -336,18 +290,3 @@ func (g *Generator) getInterfaceFuncs(goPackage, mainGoPackage string, service p
|
||||
|
||||
return functions, nil
|
||||
}
|
||||
|
||||
// buildExtraImportLines converts a set of import paths into quoted import lines
|
||||
// for use in the call.tpl {{.extraImports}} placeholder.
|
||||
func buildExtraImportLines(extraImports *collection.Set[string]) string {
|
||||
if extraImports.Count() == 0 {
|
||||
return ""
|
||||
}
|
||||
keys := extraImports.Keys()
|
||||
sort.Strings(keys)
|
||||
lines := make([]string, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
lines = append(lines, fmt.Sprintf(`"%s"`, k))
|
||||
}
|
||||
return strings.Join(lines, "\n\t")
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user