Compare commits
59 Commits
fe9482fb15
...
c41763d08a
| Author | SHA1 | Date | |
|---|---|---|---|
| c41763d08a | |||
|
|
0449450c64 | ||
|
|
9f9a12ea57 | ||
|
|
cc2a7e97f9 | ||
|
|
09d7af76af | ||
|
|
c233a66601 | ||
|
|
94fa12560c | ||
|
|
7d90f906f5 | ||
|
|
f372b98d96 | ||
|
|
459d3025c5 | ||
|
|
e9e55125a9 | ||
|
|
159ecb7386 | ||
|
|
69bb746a1d | ||
|
|
d184f96b13 | ||
|
|
c7dacb0146 | ||
|
|
2207477b60 | ||
|
|
105ab590ff | ||
|
|
2f4c58ed73 | ||
|
|
1631aa02ad | ||
|
|
4df10eef5d | ||
|
|
3d552ea7a8 | ||
|
|
74b87ac9fd | ||
|
|
ba1d6e3664 | ||
|
|
2096cd5749 | ||
|
|
2eb2fa26f6 | ||
|
|
bc4187ca90 | ||
|
|
b7be25b98b | ||
|
|
dd01695d45 | ||
|
|
25821bdee6 | ||
|
|
b624b966f0 | ||
|
|
df96262235 | ||
|
|
2629636f64 | ||
|
|
708ad207d7 | ||
|
|
b53ba76a99 | ||
|
|
be7f93924a | ||
|
|
45be48a4ee | ||
|
|
e08ba2fee8 | ||
|
|
a5d2b971a1 | ||
|
|
9763c8b143 | ||
|
|
4e3f1776dc | ||
|
|
e38036cea2 | ||
|
|
8e97c5819f | ||
|
|
0ee44c7064 | ||
|
|
a1bacd3fc8 | ||
|
|
c98d5fdaf4 | ||
|
|
2ee43b41b8 | ||
|
|
8367af3416 | ||
|
|
03b6e377d7 | ||
|
|
ec41880476 | ||
|
|
5263805b3b | ||
|
|
a7363f0c21 | ||
|
|
52e5d85221 | ||
|
|
88aab8f635 | ||
|
|
1f63cbe9c6 | ||
|
|
0dfaf135dd | ||
|
|
914bcdcf2b | ||
|
|
e38cb0118d | ||
|
|
cb8161c799 | ||
|
|
c4dac2095f |
@@ -1 +1,7 @@
|
||||
**/.git
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
goctl
|
||||
Makefile
|
||||
readme.md
|
||||
readme-cn.md
|
||||
|
||||
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
@@ -5,6 +5,14 @@
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "docker" # Update image tags in Dockerfile
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "github-actions" # Update GitHub Actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "gomod" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
|
||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -64,4 +64,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
10
.github/workflows/go.yml
vendored
10
.github/workflows/go.yml
vendored
@@ -15,9 +15,9 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: '1.19'
|
||||
check-latest: true
|
||||
cache: true
|
||||
id: go
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
|
||||
test-win:
|
||||
name: Windows
|
||||
@@ -50,10 +50,10 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
# use 1.19 to guarantee Go 1.19 compatibility
|
||||
go-version: 1.19
|
||||
go-version: '1.19'
|
||||
check-latest: true
|
||||
cache: true
|
||||
|
||||
|
||||
2
.github/workflows/issues.yml
vendored
2
.github/workflows/issues.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v8
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-issue-stale: 365
|
||||
days-before-issue-close: 90
|
||||
|
||||
16
SECURITY.md
Normal file
16
SECURITY.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We publish releases monthly.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| >= 1.4.4 | :white_check_mark: |
|
||||
| < 1.4.4 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
https://github.com/zeromicro/go-zero/security/advisories
|
||||
|
||||
Accepted vulnerabilities are expected to be fixed within a month.
|
||||
@@ -130,7 +130,7 @@ func (r *redisBitSet) check(ctx context.Context, offsets []uint) (bool, error) {
|
||||
}
|
||||
|
||||
resp, err := r.store.ScriptRunCtx(ctx, testScript, []string{r.key}, args)
|
||||
if err == redis.Nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
return false, err
|
||||
@@ -162,7 +162,7 @@ func (r *redisBitSet) set(ctx context.Context, offsets []uint) error {
|
||||
}
|
||||
|
||||
_, err = r.store.ScriptRunCtx(ctx, setScript, []string{r.key}, args)
|
||||
if err == redis.Nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ func GetBreaker(name string) Breaker {
|
||||
// NoBreakerFor disables the circuit breaker for the given name.
|
||||
func NoBreakerFor(name string) {
|
||||
lock.Lock()
|
||||
breakers[name] = newNopBreaker()
|
||||
breakers[name] = NopBreaker()
|
||||
lock.Unlock()
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ const nopBreakerName = "nopBreaker"
|
||||
|
||||
type nopBreaker struct{}
|
||||
|
||||
func newNopBreaker() Breaker {
|
||||
// NopBreaker returns a breaker that never trigger breaker circuit.
|
||||
func NopBreaker() Breaker {
|
||||
return nopBreaker{}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func TestNopBreaker(t *testing.T) {
|
||||
b := newNopBreaker()
|
||||
b := NopBreaker()
|
||||
assert.Equal(t, nopBreakerName, b.Name())
|
||||
p, err := b.Allow()
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -23,7 +23,7 @@ var (
|
||||
zero = big.NewInt(0)
|
||||
)
|
||||
|
||||
// DhKey defines the Diffie Hellman key.
|
||||
// DhKey defines the Diffie-Hellman key.
|
||||
type DhKey struct {
|
||||
PriKey *big.Int
|
||||
PubKey *big.Int
|
||||
@@ -46,7 +46,7 @@ func ComputeKey(pubKey, priKey *big.Int) (*big.Int, error) {
|
||||
return new(big.Int).Exp(pubKey, priKey, p), nil
|
||||
}
|
||||
|
||||
// GenerateKey returns a Diffie Hellman key.
|
||||
// GenerateKey returns a Diffie-Hellman key.
|
||||
func GenerateKey() (*DhKey, error) {
|
||||
var err error
|
||||
var x *big.Int
|
||||
|
||||
@@ -128,8 +128,8 @@ func (c *Cache) Take(key string, fetch func() (any, error)) (any, error) {
|
||||
|
||||
var fresh bool
|
||||
val, err := c.barrier.Do(key, func() (any, error) {
|
||||
// because O(1) on map search in memory, and fetch is an IO query
|
||||
// so we do double check, cache might be taken by another call
|
||||
// because O(1) on map search in memory, and fetch is an IO query,
|
||||
// so we do double-check, cache might be taken by another call
|
||||
if val, ok := c.doGet(key); ok {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ func addOrMergeFields(info *fieldInfo, key string, child *fieldInfo, fullName st
|
||||
return newConflictKeyError(fullName)
|
||||
}
|
||||
|
||||
if err := mergeFields(prev, key, child.children, fullName); err != nil {
|
||||
if err := mergeFields(prev, child.children, fullName); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@@ -281,7 +281,7 @@ func getTagName(field reflect.StructField) string {
|
||||
return field.Name
|
||||
}
|
||||
|
||||
func mergeFields(prev *fieldInfo, key string, children map[string]*fieldInfo, fullName string) error {
|
||||
func mergeFields(prev *fieldInfo, children map[string]*fieldInfo, fullName string) error {
|
||||
if len(prev.children) == 0 || len(children) == 0 {
|
||||
return newConflictKeyError(fullName)
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ func (c *cluster) load(cli EtcdClient, key string) int64 {
|
||||
break
|
||||
}
|
||||
|
||||
logx.Error(err)
|
||||
logx.Errorf("%s, key is %s", err.Error(), key)
|
||||
time.Sleep(coolDownInterval)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package errorx
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type (
|
||||
// A BatchError is an error that can hold multiple errors.
|
||||
BatchError struct {
|
||||
errs errorArray
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
errorArray []error
|
||||
@@ -13,6 +17,9 @@ type (
|
||||
|
||||
// Add adds errs to be, nil errors are ignored.
|
||||
func (be *BatchError) Add(errs ...error) {
|
||||
be.lock.Lock()
|
||||
defer be.lock.Unlock()
|
||||
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
be.errs = append(be.errs, err)
|
||||
@@ -22,6 +29,9 @@ func (be *BatchError) Add(errs ...error) {
|
||||
|
||||
// Err returns an error that represents all errors.
|
||||
func (be *BatchError) Err() error {
|
||||
be.lock.Lock()
|
||||
defer be.lock.Unlock()
|
||||
|
||||
switch len(be.errs) {
|
||||
case 0:
|
||||
return nil
|
||||
@@ -34,6 +44,9 @@ func (be *BatchError) Err() error {
|
||||
|
||||
// NotNil checks if any error inside.
|
||||
func (be *BatchError) NotNil() bool {
|
||||
be.lock.Lock()
|
||||
defer be.lock.Unlock()
|
||||
|
||||
return len(be.errs) > 0
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package errorx
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -33,7 +34,7 @@ func TestBatchErrorNilFromFunc(t *testing.T) {
|
||||
func TestBatchErrorOneError(t *testing.T) {
|
||||
var batch BatchError
|
||||
batch.Add(errors.New(err1))
|
||||
assert.NotNil(t, batch)
|
||||
assert.NotNil(t, batch.Err())
|
||||
assert.Equal(t, err1, batch.Err().Error())
|
||||
assert.True(t, batch.NotNil())
|
||||
}
|
||||
@@ -42,7 +43,26 @@ func TestBatchErrorWithErrors(t *testing.T) {
|
||||
var batch BatchError
|
||||
batch.Add(errors.New(err1))
|
||||
batch.Add(errors.New(err2))
|
||||
assert.NotNil(t, batch)
|
||||
assert.NotNil(t, batch.Err())
|
||||
assert.Equal(t, fmt.Sprintf("%s\n%s", err1, err2), batch.Err().Error())
|
||||
assert.True(t, batch.NotNil())
|
||||
}
|
||||
|
||||
func TestBatchErrorConcurrentAdd(t *testing.T) {
|
||||
const count = 10000
|
||||
var batch BatchError
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(count)
|
||||
for i := 0; i < count; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
batch.Add(errors.New(err1))
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
assert.NotNil(t, batch.Err())
|
||||
assert.Equal(t, count, len(batch.errs))
|
||||
assert.True(t, batch.NotNil())
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import "gopkg.in/cheggaaa/pb.v1"
|
||||
type (
|
||||
// A Scanner is used to read lines.
|
||||
Scanner interface {
|
||||
// Scan checks if has remaining to read.
|
||||
// Scan checks if it has remaining to read.
|
||||
Scan() bool
|
||||
// Text returns next line.
|
||||
Text() string
|
||||
|
||||
@@ -2,6 +2,7 @@ package fx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/errorx"
|
||||
@@ -14,9 +15,10 @@ type (
|
||||
RetryOption func(*retryOptions)
|
||||
|
||||
retryOptions struct {
|
||||
times int
|
||||
interval time.Duration
|
||||
timeout time.Duration
|
||||
times int
|
||||
interval time.Duration
|
||||
timeout time.Duration
|
||||
ignoreErrors []error
|
||||
}
|
||||
)
|
||||
|
||||
@@ -62,6 +64,11 @@ func retry(ctx context.Context, fn func(errChan chan error, retryCount int), opt
|
||||
select {
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
for _, ignoreErr := range options.ignoreErrors {
|
||||
if errors.Is(err, ignoreErr) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
berr.Add(err)
|
||||
} else {
|
||||
return nil
|
||||
@@ -84,19 +91,28 @@ func retry(ctx context.Context, fn func(errChan chan error, retryCount int), opt
|
||||
return berr.Err()
|
||||
}
|
||||
|
||||
// WithRetry customize a DoWithRetry call with given retry times.
|
||||
func WithRetry(times int) RetryOption {
|
||||
// WithIgnoreErrors Ignore the specified errors
|
||||
func WithIgnoreErrors(ignoreErrors []error) RetryOption {
|
||||
return func(options *retryOptions) {
|
||||
options.times = times
|
||||
options.ignoreErrors = ignoreErrors
|
||||
}
|
||||
}
|
||||
|
||||
// WithInterval customizes a DoWithRetry call with given interval.
|
||||
func WithInterval(interval time.Duration) RetryOption {
|
||||
return func(options *retryOptions) {
|
||||
options.interval = interval
|
||||
}
|
||||
}
|
||||
|
||||
// WithRetry customizes a DoWithRetry call with given retry times.
|
||||
func WithRetry(times int) RetryOption {
|
||||
return func(options *retryOptions) {
|
||||
options.times = times
|
||||
}
|
||||
}
|
||||
|
||||
// WithTimeout customizes a DoWithRetry call with given timeout.
|
||||
func WithTimeout(timeout time.Duration) RetryOption {
|
||||
return func(options *retryOptions) {
|
||||
options.timeout = timeout
|
||||
|
||||
@@ -97,6 +97,24 @@ func TestRetryWithInterval(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestRetryWithWithIgnoreErrors(t *testing.T) {
|
||||
ignoreErr1 := errors.New("ignore error1")
|
||||
ignoreErr2 := errors.New("ignore error2")
|
||||
ignoreErrs := []error{ignoreErr1, ignoreErr2}
|
||||
|
||||
assert.Nil(t, DoWithRetry(func() error {
|
||||
return ignoreErr1
|
||||
}, WithIgnoreErrors(ignoreErrs)))
|
||||
|
||||
assert.Nil(t, DoWithRetry(func() error {
|
||||
return ignoreErr2
|
||||
}, WithIgnoreErrors(ignoreErrs)))
|
||||
|
||||
assert.NotNil(t, DoWithRetry(func() error {
|
||||
return errors.New("any")
|
||||
}))
|
||||
}
|
||||
|
||||
func TestRetryCtx(t *testing.T) {
|
||||
t.Run("with timeout", func(t *testing.T) {
|
||||
assert.NotNil(t, DoWithRetryCtx(context.Background(), func(ctx context.Context, retryCount int) error {
|
||||
|
||||
@@ -352,7 +352,7 @@ func (s Stream) Parallel(fn ParallelFunc, opts ...Option) {
|
||||
}, opts...).Done()
|
||||
}
|
||||
|
||||
// Reduce is an utility method to let the caller deal with the underlying channel.
|
||||
// Reduce is a utility method to let the caller deal with the underlying channel.
|
||||
func (s Stream) Reduce(fn ReduceFunc) (any, error) {
|
||||
return fn(s.source)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package iox
|
||||
|
||||
import "os"
|
||||
|
||||
// RedirectInOut redirects stdin to r, stdout to w, and callers need to call restore afterwards.
|
||||
// RedirectInOut redirects stdin to r, stdout to w, and callers need to call restore afterward.
|
||||
func RedirectInOut() (restore func(), err error) {
|
||||
var r, w *os.File
|
||||
r, w, err = os.Pipe()
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
const bufSize = 32 * 1024
|
||||
|
||||
// CountLines returns the number of lines in file.
|
||||
// CountLines returns the number of lines in the file.
|
||||
func CountLines(file string) (int, error) {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,11 +2,12 @@ package iox
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A TextLineScanner is a scanner that can scan lines from given reader.
|
||||
// A TextLineScanner is a scanner that can scan lines from the given reader.
|
||||
type TextLineScanner struct {
|
||||
reader *bufio.Reader
|
||||
hasNext bool
|
||||
@@ -14,7 +15,7 @@ type TextLineScanner struct {
|
||||
err error
|
||||
}
|
||||
|
||||
// NewTextLineScanner returns a TextLineScanner with given reader.
|
||||
// NewTextLineScanner returns a TextLineScanner with the given reader.
|
||||
func NewTextLineScanner(reader io.Reader) *TextLineScanner {
|
||||
return &TextLineScanner{
|
||||
reader: bufio.NewReader(reader),
|
||||
@@ -30,7 +31,7 @@ func (scanner *TextLineScanner) Scan() bool {
|
||||
|
||||
line, err := scanner.reader.ReadString('\n')
|
||||
scanner.line = strings.TrimRight(line, "\n")
|
||||
if err == io.EOF {
|
||||
if errors.Is(err, io.EOF) {
|
||||
scanner.hasNext = false
|
||||
return true
|
||||
} else if err != nil {
|
||||
|
||||
@@ -125,7 +125,7 @@ func (lim *TokenLimiter) reserveN(ctx context.Context, now time.Time, n int) boo
|
||||
})
|
||||
// redis allowed == false
|
||||
// Lua boolean false -> r Nil bulk reply
|
||||
if err == redis.Nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return false
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
|
||||
|
||||
@@ -138,10 +138,10 @@ func (as *adaptiveShedder) Allow() (Promise, error) {
|
||||
func (as *adaptiveShedder) addFlying(delta int64) {
|
||||
flying := atomic.AddInt64(&as.flying, delta)
|
||||
// update avgFlying when the request is finished.
|
||||
// this strategy makes avgFlying have a little bit lag against flying, and smoother.
|
||||
// this strategy makes avgFlying have a little bit of lag against flying, and smoother.
|
||||
// when the flying requests increase rapidly, avgFlying increase slower, accept more requests.
|
||||
// when the flying requests drop rapidly, avgFlying drop slower, accept fewer requests.
|
||||
// it makes the service to serve as more requests as possible.
|
||||
// it makes the service to serve as many requests as possible.
|
||||
if delta < 0 {
|
||||
as.avgFlyingLock.Lock()
|
||||
as.avgFlying = as.avgFlying*flyingBeta + float64(flying)*(1-flyingBeta)
|
||||
@@ -200,7 +200,7 @@ func (as *adaptiveShedder) minRt() float64 {
|
||||
func (as *adaptiveShedder) overloadFactor() float64 {
|
||||
// as.cpuThreshold must be less than cpuMax
|
||||
factor := (cpuMax - float64(stat.CpuUsage())) / (cpuMax - float64(as.cpuThreshold))
|
||||
// at least accept 10% of acceptable requests even cpu is highly overloaded.
|
||||
// at least accept 10% of acceptable requests, even cpu is highly overloaded.
|
||||
return mathx.Between(factor, overloadFactorLowerBound, 1)
|
||||
}
|
||||
|
||||
@@ -250,14 +250,14 @@ func (as *adaptiveShedder) systemOverloaded() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// WithBuckets customizes the Shedder with given number of buckets.
|
||||
// WithBuckets customizes the Shedder with the given number of buckets.
|
||||
func WithBuckets(buckets int) ShedderOption {
|
||||
return func(opts *shedderOptions) {
|
||||
opts.buckets = buckets
|
||||
}
|
||||
}
|
||||
|
||||
// WithCpuThreshold customizes the Shedder with given cpu threshold.
|
||||
// WithCpuThreshold customizes the Shedder with the given cpu threshold.
|
||||
func WithCpuThreshold(threshold int64) ShedderOption {
|
||||
return func(opts *shedderOptions) {
|
||||
opts.cpuThreshold = threshold
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/zeromicro/go-zero/core/syncx"
|
||||
)
|
||||
|
||||
// A ShedderGroup is a manager to manage key based shedders.
|
||||
// A ShedderGroup is a manager to manage key-based shedders.
|
||||
type ShedderGroup struct {
|
||||
options []ShedderOption
|
||||
manager *syncx.ResourceManager
|
||||
|
||||
@@ -42,7 +42,7 @@ func Debugv(ctx context.Context, v interface{}) {
|
||||
getLogger(ctx).Debugv(v)
|
||||
}
|
||||
|
||||
// Debugw writes msg along with fields into access log.
|
||||
// Debugw writes msg along with fields into the access log.
|
||||
func Debugw(ctx context.Context, msg string, fields ...LogField) {
|
||||
getLogger(ctx).Debugw(msg, fields...)
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func Errorv(ctx context.Context, v any) {
|
||||
getLogger(ctx).Errorv(v)
|
||||
}
|
||||
|
||||
// Errorw writes msg along with fields into error log.
|
||||
// Errorw writes msg along with fields into the error log.
|
||||
func Errorw(ctx context.Context, msg string, fields ...LogField) {
|
||||
getLogger(ctx).Errorw(msg, fields...)
|
||||
}
|
||||
@@ -88,7 +88,7 @@ func Infov(ctx context.Context, v any) {
|
||||
getLogger(ctx).Infov(v)
|
||||
}
|
||||
|
||||
// Infow writes msg along with fields into access log.
|
||||
// Infow writes msg along with fields into the access log.
|
||||
func Infow(ctx context.Context, msg string, fields ...LogField) {
|
||||
getLogger(ctx).Infow(msg, fields...)
|
||||
}
|
||||
@@ -108,10 +108,11 @@ func SetLevel(level uint32) {
|
||||
logx.SetLevel(level)
|
||||
}
|
||||
|
||||
// SetUp sets up the logx. If already set up, just return nil.
|
||||
// we allow SetUp to be called multiple times, because for example
|
||||
// SetUp sets up the logx.
|
||||
// If already set up, return nil.
|
||||
// We allow SetUp to be called multiple times, because, for example,
|
||||
// we need to allow different service frameworks to initialize logx respectively.
|
||||
// the same logic for SetUp
|
||||
// The same logic for SetUp
|
||||
func SetUp(c LogConf) error {
|
||||
return logx.SetUp(c)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package logx
|
||||
|
||||
// A LessLogger is a logger that control to log once during the given duration.
|
||||
// A LessLogger is a logger that controls to log once during the given duration.
|
||||
type LessLogger struct {
|
||||
*limitedExecutor
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func Debugv(v any) {
|
||||
}
|
||||
}
|
||||
|
||||
// Debugw writes msg along with fields into access log.
|
||||
// Debugw writes msg along with fields into the access log.
|
||||
func Debugw(msg string, fields ...LogField) {
|
||||
if shallLog(DebugLevel) {
|
||||
writeDebug(msg, fields...)
|
||||
@@ -142,7 +142,7 @@ func Errorv(v any) {
|
||||
}
|
||||
}
|
||||
|
||||
// Errorw writes msg along with fields into error log.
|
||||
// Errorw writes msg along with fields into the error log.
|
||||
func Errorw(msg string, fields ...LogField) {
|
||||
if shallLog(ErrorLevel) {
|
||||
writeError(msg, fields...)
|
||||
@@ -208,7 +208,7 @@ func Infov(v any) {
|
||||
}
|
||||
}
|
||||
|
||||
// Infow writes msg along with fields into access log.
|
||||
// Infow writes msg along with fields into the access log.
|
||||
func Infow(msg string, fields ...LogField) {
|
||||
if shallLog(InfoLevel) {
|
||||
writeInfo(msg, fields...)
|
||||
@@ -254,11 +254,12 @@ func SetWriter(w Writer) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetUp sets up the logx. If already set up, just return nil.
|
||||
// we allow SetUp to be called multiple times, because for example
|
||||
// SetUp sets up the logx.
|
||||
// If already set up, return nil.
|
||||
// We allow SetUp to be called multiple times, because, for example,
|
||||
// we need to allow different service frameworks to initialize logx respectively.
|
||||
func SetUp(c LogConf) (err error) {
|
||||
// Just ignore the subsequent SetUp calls.
|
||||
// Ignore the later SetUp calls.
|
||||
// Because multiple services in one process might call SetUp respectively.
|
||||
// Need to wait for the first caller to complete the execution.
|
||||
setupOnce.Do(func() {
|
||||
@@ -480,7 +481,7 @@ func writeDebug(val any, fields ...LogField) {
|
||||
getWriter().Debug(val, addCaller(fields...)...)
|
||||
}
|
||||
|
||||
// writeError writes v into error log.
|
||||
// writeError writes v into the error log.
|
||||
// Not checking shallLog here is for performance consideration.
|
||||
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
|
||||
// The caller should check shallLog before calling this function.
|
||||
@@ -520,7 +521,7 @@ func writeStack(msg string) {
|
||||
getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
|
||||
}
|
||||
|
||||
// writeStat writes v into stat log.
|
||||
// writeStat writes v into the stat log.
|
||||
// Not checking shallLog here is for performance consideration.
|
||||
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
|
||||
// The caller should check shallLog before calling this function.
|
||||
|
||||
@@ -570,7 +570,7 @@ func TestErrorfWithWrappedError(t *testing.T) {
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
Errorf("hello %w", errors.New(message))
|
||||
Errorf("hello %s", errors.New(message))
|
||||
assert.True(t, strings.Contains(w.String(), "hello there"))
|
||||
}
|
||||
|
||||
|
||||
@@ -319,7 +319,7 @@ func (l *RotateLogger) maybeCompressFile(file string) {
|
||||
}()
|
||||
|
||||
if _, err := os.Stat(file); err != nil {
|
||||
// file not exists or other error, ignore compression
|
||||
// file doesn't exist or another error, ignore compression
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const (
|
||||
)
|
||||
|
||||
// Marshal marshals the given val and returns the map that contains the fields.
|
||||
// optional=another is not implemented, and it's hard to implement and not common used.
|
||||
// optional=another is not implemented, and it's hard to implement and not commonly used.
|
||||
func Marshal(val any) (map[string]map[string]any, error) {
|
||||
ret := make(map[string]map[string]any)
|
||||
tp := reflect.TypeOf(val)
|
||||
|
||||
@@ -39,7 +39,7 @@ var (
|
||||
)
|
||||
|
||||
type (
|
||||
// Unmarshaler is used to unmarshal with given tag key.
|
||||
// Unmarshaler is used to unmarshal with the given tag key.
|
||||
Unmarshaler struct {
|
||||
key string
|
||||
opts unmarshalOptions
|
||||
@@ -69,7 +69,7 @@ func NewUnmarshaler(key string, opts ...UnmarshalOption) *Unmarshaler {
|
||||
return &unmarshaler
|
||||
}
|
||||
|
||||
// UnmarshalKey unmarshals m into v with tag key.
|
||||
// UnmarshalKey unmarshals m into v with the tag key.
|
||||
func UnmarshalKey(m map[string]any, v any) error {
|
||||
return keyUnmarshaler.Unmarshal(m, v)
|
||||
}
|
||||
@@ -223,11 +223,11 @@ func (u *Unmarshaler) fillSliceFromString(fieldType reflect.Type, value reflect.
|
||||
switch v := mapValue.(type) {
|
||||
case fmt.Stringer:
|
||||
if err := jsonx.UnmarshalFromString(v.String(), &slice); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("fullName: `%s`, error: `%w`", fullName, err)
|
||||
}
|
||||
case string:
|
||||
if err := jsonx.UnmarshalFromString(v, &slice); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("fullName: `%s`, error: `%w`", fullName, err)
|
||||
}
|
||||
default:
|
||||
return errUnsupportedType
|
||||
@@ -428,6 +428,10 @@ func (u *Unmarshaler) parseOptionsWithContext(field reflect.StructField, m Value
|
||||
}
|
||||
}
|
||||
|
||||
if u.opts.fillDefault {
|
||||
return key, &options.fieldOptionsWithContext, nil
|
||||
}
|
||||
|
||||
optsWithContext, err := options.toOptionsWithContext(key, m, fullName)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
@@ -625,7 +629,7 @@ func (u *Unmarshaler) processFieldPrimitiveWithJSONNumber(fieldType reflect.Type
|
||||
return err
|
||||
}
|
||||
|
||||
// if value is a pointer, we need to check overflow with the pointer's value.
|
||||
// if the value is a pointer, we need to check overflow with the pointer's value.
|
||||
derefedValue := value
|
||||
for derefedValue.Type().Kind() == reflect.Ptr {
|
||||
derefedValue = derefedValue.Elem()
|
||||
|
||||
@@ -5411,6 +5411,15 @@ func TestFillDefaultUnmarshal(t *testing.T) {
|
||||
assert.Equal(t, "c", st.C)
|
||||
})
|
||||
|
||||
t.Run("optional !", func(t *testing.T) {
|
||||
var st struct {
|
||||
A string `json:",optional"`
|
||||
B string `json:",optional=!A"`
|
||||
}
|
||||
err := fillDefaultUnmarshal.Unmarshal(map[string]any{}, &st)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("has value", func(t *testing.T) {
|
||||
type St struct {
|
||||
A string `json:",default=a"`
|
||||
@@ -5857,7 +5866,7 @@ type mockValuerWithParent struct {
|
||||
ok bool
|
||||
}
|
||||
|
||||
func (m mockValuerWithParent) Value(key string) (any, bool) {
|
||||
func (m mockValuerWithParent) Value(_ string) (any, bool) {
|
||||
return m.value, m.ok
|
||||
}
|
||||
|
||||
|
||||
@@ -416,7 +416,7 @@ func parseOption(fieldOpts *fieldOptions, fieldName, option string) error {
|
||||
}
|
||||
|
||||
// parseOptions parses the given options in tag.
|
||||
// for example: `json:"name,options=foo|bar"` or `json:"name,options=[foo,bar]"`
|
||||
// for example, `json:"name,options=foo|bar"` or `json:"name,options=[foo,bar]"`
|
||||
func parseOptions(val string) []string {
|
||||
if len(val) == 0 {
|
||||
return nil
|
||||
|
||||
@@ -26,9 +26,9 @@ type (
|
||||
parent valuerWithParent
|
||||
}
|
||||
|
||||
// mapValuer is a type for map to meet the Valuer interface.
|
||||
// mapValuer is a type for the map to meet the Valuer interface.
|
||||
mapValuer map[string]any
|
||||
// simpleValuer is a type to get value from current node.
|
||||
// simpleValuer is a type to get value from the current node.
|
||||
simpleValuer node
|
||||
// recursiveValuer is a type to get the value recursively from current and parent nodes.
|
||||
recursiveValuer node
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func FuzzMapReduce(f *testing.F) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
rand.NewSource(time.Now().UnixNano())
|
||||
|
||||
f.Add(int64(10), runtime.NumCPU())
|
||||
f.Fuzz(func(t *testing.T, n int64, workers int) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
// If Fuzz stuck, we don't know why, because it only returns hung or unexpected,
|
||||
// so we need to simulate the fuzz test in test mode.
|
||||
func TestMapReduceRandom(t *testing.T) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
rand.NewSource(time.Now().UnixNano())
|
||||
|
||||
const (
|
||||
times = 10000
|
||||
|
||||
@@ -36,6 +36,6 @@ type fakeCreator struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (fc fakeCreator) Create(name string) (file *os.File, err error) {
|
||||
func (fc fakeCreator) Create(_ string) (file *os.File, err error) {
|
||||
return fc.file, fc.err
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func (q *Queue) AddListener(listener Listener) {
|
||||
q.listeners = append(q.listeners, listener)
|
||||
}
|
||||
|
||||
// Broadcast broadcasts message to all event channels.
|
||||
// Broadcast broadcasts the message to all event channels.
|
||||
func (q *Queue) Broadcast(message any) {
|
||||
go func() {
|
||||
q.eventLock.Lock()
|
||||
@@ -202,7 +202,7 @@ func (q *Queue) produce() {
|
||||
}
|
||||
|
||||
func (q *Queue) produceOne(producer Producer) (string, bool) {
|
||||
// avoid panic quit the producer, just log it and continue
|
||||
// avoid panic quit the producer, log it and continue
|
||||
defer rescue.Recover()
|
||||
|
||||
return producer.Produce()
|
||||
|
||||
@@ -67,7 +67,7 @@ func (p *mockedPusher) Name() string {
|
||||
return p.name
|
||||
}
|
||||
|
||||
func (p *mockedPusher) Push(s string) error {
|
||||
func (p *mockedPusher) Push(_ string) error {
|
||||
if proba.TrueOnProba(failProba) {
|
||||
return errors.New("dummy")
|
||||
}
|
||||
|
||||
@@ -23,42 +23,17 @@ var (
|
||||
preTotal uint64
|
||||
limit float64
|
||||
cores uint64
|
||||
noCgroup bool
|
||||
initOnce sync.Once
|
||||
)
|
||||
|
||||
// if /proc not present, ignore the cpu calculation, like wsl linux
|
||||
func initialize() {
|
||||
cpus, err := effectiveCpus()
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
cores = uint64(cpus)
|
||||
limit = float64(cpus)
|
||||
quota, err := cpuQuota()
|
||||
if err == nil && quota > 0 {
|
||||
if quota < limit {
|
||||
limit = quota
|
||||
}
|
||||
}
|
||||
|
||||
preSystem, err = systemCpuUsage()
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
preTotal, err = cpuUsage()
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// RefreshCpu refreshes cpu usage and returns.
|
||||
func RefreshCpu() uint64 {
|
||||
initOnce.Do(initialize)
|
||||
initializeOnce()
|
||||
|
||||
if noCgroup {
|
||||
return 0
|
||||
}
|
||||
|
||||
total, err := cpuUsage()
|
||||
if err != nil {
|
||||
@@ -112,6 +87,47 @@ func effectiveCpus() (int, error) {
|
||||
return cg.effectiveCpus()
|
||||
}
|
||||
|
||||
// if /proc not present, ignore the cpu calculation, like wsl linux
|
||||
func initialize() error {
|
||||
cpus, err := effectiveCpus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cores = uint64(cpus)
|
||||
limit = float64(cpus)
|
||||
quota, err := cpuQuota()
|
||||
if err == nil && quota > 0 {
|
||||
if quota < limit {
|
||||
limit = quota
|
||||
}
|
||||
}
|
||||
|
||||
preSystem, err = systemCpuUsage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preTotal, err = cpuUsage()
|
||||
return err
|
||||
}
|
||||
|
||||
func initializeOnce() {
|
||||
initOnce.Do(func() {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
noCgroup = true
|
||||
logx.Error(p)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := initialize(); err != nil {
|
||||
noCgroup = true
|
||||
logx.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func systemCpuUsage() (uint64, error) {
|
||||
lines, err := iox.ReadTextLines(statFile, iox.WithoutBlank())
|
||||
if err != nil {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package stat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/core/logx/logtest"
|
||||
)
|
||||
|
||||
func TestMetrics(t *testing.T) {
|
||||
@@ -30,6 +32,34 @@ func TestMetrics(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTopDurationWithEmpty(t *testing.T) {
|
||||
assert.Equal(t, float32(0), getTopDuration(nil))
|
||||
assert.Equal(t, float32(0), getTopDuration([]Task{}))
|
||||
}
|
||||
|
||||
func TestLogAndReport(t *testing.T) {
|
||||
buf := logtest.NewCollector(t)
|
||||
old := logEnabled.True()
|
||||
logEnabled.Set(true)
|
||||
t.Cleanup(func() {
|
||||
logEnabled.Set(old)
|
||||
})
|
||||
|
||||
log(&StatReport{})
|
||||
assert.NotEmpty(t, buf.String())
|
||||
|
||||
writerLock.Lock()
|
||||
writer := reportWriter
|
||||
writerLock.Unlock()
|
||||
buf = logtest.NewCollector(t)
|
||||
t.Cleanup(func() {
|
||||
SetReportWriter(writer)
|
||||
})
|
||||
SetReportWriter(&badWriter{})
|
||||
writeReport(&StatReport{})
|
||||
assert.NotEmpty(t, buf.String())
|
||||
}
|
||||
|
||||
type mockedWriter struct {
|
||||
report *StatReport
|
||||
}
|
||||
@@ -38,3 +68,9 @@ func (m *mockedWriter) Write(report *StatReport) error {
|
||||
m.report = report
|
||||
return nil
|
||||
}
|
||||
|
||||
type badWriter struct{}
|
||||
|
||||
func (b *badWriter) Write(_ *StatReport) error {
|
||||
return errors.New("bad")
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package stat
|
||||
|
||||
import "time"
|
||||
|
||||
// A Task is a task that is reported to Metrics.
|
||||
// A Task is a task reported to Metrics.
|
||||
type Task struct {
|
||||
Drop bool
|
||||
Duration time.Duration
|
||||
|
||||
2
core/stores/cache/cacheopt.go
vendored
2
core/stores/cache/cacheopt.go
vendored
@@ -8,7 +8,7 @@ const (
|
||||
)
|
||||
|
||||
type (
|
||||
// An Options is used to store the cache options.
|
||||
// Options is used to store the cache options.
|
||||
Options struct {
|
||||
Expiry time.Duration
|
||||
NotFoundExpiry time.Duration
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package mon
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/syncx"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/bsoncodec"
|
||||
mopt "go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
@@ -16,10 +19,17 @@ var (
|
||||
)
|
||||
|
||||
type (
|
||||
options = mopt.ClientOptions
|
||||
|
||||
// Option defines the method to customize a mongo model.
|
||||
Option func(opts *options)
|
||||
|
||||
// TypeCodec is a struct that stores specific type Encoder/Decoder.
|
||||
TypeCodec struct {
|
||||
ValueType reflect.Type
|
||||
Encoder bsoncodec.ValueEncoder
|
||||
Decoder bsoncodec.ValueDecoder
|
||||
}
|
||||
|
||||
options = mopt.ClientOptions
|
||||
)
|
||||
|
||||
// DisableLog disables logging of mongo commands, includes info and slow logs.
|
||||
@@ -38,15 +48,27 @@ func SetSlowThreshold(threshold time.Duration) {
|
||||
slowThreshold.Set(threshold)
|
||||
}
|
||||
|
||||
func defaultTimeoutOption() Option {
|
||||
return func(opts *options) {
|
||||
opts.SetTimeout(defaultTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
// WithTimeout set the mon client operation timeout.
|
||||
func WithTimeout(timeout time.Duration) Option {
|
||||
return func(opts *options) {
|
||||
opts.SetTimeout(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
// WithTypeCodec registers TypeCodecs to convert custom types.
|
||||
func WithTypeCodec(typeCodecs ...TypeCodec) Option {
|
||||
return func(opts *options) {
|
||||
registry := bson.NewRegistry()
|
||||
for _, v := range typeCodecs {
|
||||
registry.RegisterTypeEncoder(v.ValueType, v.Encoder)
|
||||
registry.RegisterTypeDecoder(v.ValueType, v.Decoder)
|
||||
}
|
||||
opts.SetRegistry(registry)
|
||||
}
|
||||
}
|
||||
|
||||
func defaultTimeoutOption() Option {
|
||||
return func(opts *options) {
|
||||
opts.SetTimeout(defaultTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package mon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.mongodb.org/mongo-driver/bson/bsoncodec"
|
||||
"go.mongodb.org/mongo-driver/bson/bsonrw"
|
||||
mopt "go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
@@ -51,3 +55,56 @@ func TestDisableInfoLog(t *testing.T) {
|
||||
assert.False(t, logMon.True())
|
||||
assert.True(t, logSlowMon.True())
|
||||
}
|
||||
|
||||
func TestWithRegistryForTimestampRegisterType(t *testing.T) {
|
||||
opts := mopt.Client()
|
||||
|
||||
// mongoDateTimeEncoder allow user convert time.Time to primitive.DateTime.
|
||||
var mongoDateTimeEncoder bsoncodec.ValueEncoderFunc = func(ect bsoncodec.EncodeContext, w bsonrw.ValueWriter, value reflect.Value) error {
|
||||
// Use reflect, determine if it can be converted to time.Time.
|
||||
dec, ok := value.Interface().(time.Time)
|
||||
if !ok {
|
||||
return fmt.Errorf("value %v to encode is not of type time.Time", value)
|
||||
}
|
||||
return w.WriteDateTime(dec.Unix())
|
||||
}
|
||||
|
||||
// mongoDateTimeEncoder allow user convert primitive.DateTime to time.Time.
|
||||
var mongoDateTimeDecoder bsoncodec.ValueDecoderFunc = func(ect bsoncodec.DecodeContext, r bsonrw.ValueReader, value reflect.Value) error {
|
||||
primTime, err := r.ReadDateTime()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading primitive.DateTime from ValueReader: %v", err)
|
||||
}
|
||||
value.Set(reflect.ValueOf(time.Unix(primTime, 0)))
|
||||
return nil
|
||||
}
|
||||
|
||||
codecs := []TypeCodec{
|
||||
{
|
||||
ValueType: reflect.TypeOf(time.Time{}),
|
||||
Encoder: mongoDateTimeEncoder,
|
||||
Decoder: mongoDateTimeDecoder,
|
||||
},
|
||||
}
|
||||
WithTypeCodec(codecs...)(opts)
|
||||
|
||||
for _, v := range codecs {
|
||||
// Validate Encoder
|
||||
enc, err := opts.Registry.LookupEncoder(v.ValueType)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if assert.ObjectsAreEqual(v.Encoder, enc) {
|
||||
t.Errorf("Encoder got from Registry: %v, but want: %v", enc, v.Encoder)
|
||||
}
|
||||
|
||||
// Validate Decoder
|
||||
dec, err := opts.Registry.LookupDecoder(v.ValueType)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if assert.ObjectsAreEqual(v.Decoder, dec) {
|
||||
t.Errorf("Decoder got from Registry: %v, but want: %v", dec, v.Decoder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
core/stores/redis/breakerhook.go
Normal file
41
core/stores/redis/breakerhook.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
red "github.com/redis/go-redis/v9"
|
||||
"github.com/zeromicro/go-zero/core/breaker"
|
||||
"github.com/zeromicro/go-zero/core/lang"
|
||||
)
|
||||
|
||||
var ignoreCmds = map[string]lang.PlaceholderType{
|
||||
"blpop": {},
|
||||
}
|
||||
|
||||
type breakerHook struct {
|
||||
brk breaker.Breaker
|
||||
}
|
||||
|
||||
func (h breakerHook) DialHook(next red.DialHook) red.DialHook {
|
||||
return next
|
||||
}
|
||||
|
||||
func (h breakerHook) ProcessHook(next red.ProcessHook) red.ProcessHook {
|
||||
return func(ctx context.Context, cmd red.Cmder) error {
|
||||
if _, ok := ignoreCmds[cmd.Name()]; ok {
|
||||
return next(ctx, cmd)
|
||||
}
|
||||
|
||||
return h.brk.DoWithAcceptable(func() error {
|
||||
return next(ctx, cmd)
|
||||
}, acceptable)
|
||||
}
|
||||
}
|
||||
|
||||
func (h breakerHook) ProcessPipelineHook(next red.ProcessPipelineHook) red.ProcessPipelineHook {
|
||||
return func(ctx context.Context, cmds []red.Cmder) error {
|
||||
return h.brk.DoWithAcceptable(func() error {
|
||||
return next(ctx, cmds)
|
||||
}, acceptable)
|
||||
}
|
||||
}
|
||||
135
core/stores/redis/breakerhook_test.go
Normal file
135
core/stores/redis/breakerhook_test.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/core/breaker"
|
||||
)
|
||||
|
||||
func TestBreakerHook_ProcessHook(t *testing.T) {
|
||||
t.Run("breakerHookOpen", func(t *testing.T) {
|
||||
s := miniredis.RunT(t)
|
||||
|
||||
rds := MustNewRedis(RedisConf{
|
||||
Host: s.Addr(),
|
||||
Type: NodeType,
|
||||
})
|
||||
|
||||
someError := errors.New("ERR some error")
|
||||
s.SetError(someError.Error())
|
||||
|
||||
var err error
|
||||
for i := 0; i < 1000; i++ {
|
||||
_, err = rds.Get("key")
|
||||
if err != nil && err.Error() != someError.Error() {
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Equal(t, breaker.ErrServiceUnavailable, err)
|
||||
})
|
||||
|
||||
t.Run("breakerHookClose", func(t *testing.T) {
|
||||
s := miniredis.RunT(t)
|
||||
|
||||
rds := MustNewRedis(RedisConf{
|
||||
Host: s.Addr(),
|
||||
Type: NodeType,
|
||||
})
|
||||
|
||||
var err error
|
||||
for i := 0; i < 1000; i++ {
|
||||
_, err = rds.Get("key")
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.NotEqual(t, breaker.ErrServiceUnavailable, err)
|
||||
})
|
||||
|
||||
t.Run("breakerHook_ignoreCmd", func(t *testing.T) {
|
||||
s := miniredis.RunT(t)
|
||||
|
||||
rds := MustNewRedis(RedisConf{
|
||||
Host: s.Addr(),
|
||||
Type: NodeType,
|
||||
})
|
||||
|
||||
someError := errors.New("ERR some error")
|
||||
s.SetError(someError.Error())
|
||||
|
||||
var err error
|
||||
|
||||
node, err := getRedis(rds)
|
||||
assert.NoError(t, err)
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
_, err = rds.Blpop(node, "key")
|
||||
if err != nil && err.Error() != someError.Error() {
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Equal(t, someError.Error(), err.Error())
|
||||
})
|
||||
}
|
||||
|
||||
func TestBreakerHook_ProcessPipelineHook(t *testing.T) {
|
||||
t.Run("breakerPipelineHookOpen", func(t *testing.T) {
|
||||
s := miniredis.RunT(t)
|
||||
|
||||
rds := MustNewRedis(RedisConf{
|
||||
Host: s.Addr(),
|
||||
Type: NodeType,
|
||||
})
|
||||
|
||||
someError := errors.New("ERR some error")
|
||||
s.SetError(someError.Error())
|
||||
|
||||
var err error
|
||||
for i := 0; i < 1000; i++ {
|
||||
err = rds.Pipelined(
|
||||
func(pipe Pipeliner) error {
|
||||
pipe.Incr(context.Background(), "pipelined_counter")
|
||||
pipe.Expire(context.Background(), "pipelined_counter", time.Hour)
|
||||
pipe.ZAdd(context.Background(), "zadd", Z{Score: 12, Member: "zadd"})
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil && err.Error() != someError.Error() {
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Equal(t, breaker.ErrServiceUnavailable, err)
|
||||
})
|
||||
|
||||
t.Run("breakerPipelineHookClose", func(t *testing.T) {
|
||||
s := miniredis.RunT(t)
|
||||
|
||||
rds := MustNewRedis(RedisConf{
|
||||
Host: s.Addr(),
|
||||
Type: NodeType,
|
||||
})
|
||||
|
||||
var err error
|
||||
for i := 0; i < 1000; i++ {
|
||||
err = rds.Pipelined(
|
||||
func(pipe Pipeliner) error {
|
||||
pipe.Incr(context.Background(), "pipelined_counter")
|
||||
pipe.Expire(context.Background(), "pipelined_counter", time.Hour)
|
||||
pipe.ZAdd(context.Background(), "zadd", Z{Score: 12, Member: "zadd"})
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.NotEqual(t, breaker.ErrServiceUnavailable, err)
|
||||
})
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func (rc RedisConf) NewRedis() *Redis {
|
||||
opts = append(opts, WithTLS())
|
||||
}
|
||||
|
||||
return New(rc.Host, opts...)
|
||||
return newRedis(rc.Host, opts...)
|
||||
}
|
||||
|
||||
// Validate validates the RedisConf.
|
||||
|
||||
@@ -23,17 +23,18 @@ import (
|
||||
const spanName = "redis"
|
||||
|
||||
var (
|
||||
durationHook = hook{}
|
||||
defaultDurationHook = durationHook{}
|
||||
redisCmdsAttributeKey = attribute.Key("redis.cmds")
|
||||
)
|
||||
|
||||
type hook struct{}
|
||||
type durationHook struct {
|
||||
}
|
||||
|
||||
func (h hook) DialHook(next red.DialHook) red.DialHook {
|
||||
func (h durationHook) DialHook(next red.DialHook) red.DialHook {
|
||||
return next
|
||||
}
|
||||
|
||||
func (h hook) ProcessHook(next red.ProcessHook) red.ProcessHook {
|
||||
func (h durationHook) ProcessHook(next red.ProcessHook) red.ProcessHook {
|
||||
return func(ctx context.Context, cmd red.Cmder) error {
|
||||
start := timex.Now()
|
||||
ctx, endSpan := h.startSpan(ctx, cmd)
|
||||
@@ -57,7 +58,7 @@ func (h hook) ProcessHook(next red.ProcessHook) red.ProcessHook {
|
||||
}
|
||||
}
|
||||
|
||||
func (h hook) ProcessPipelineHook(next red.ProcessPipelineHook) red.ProcessPipelineHook {
|
||||
func (h durationHook) ProcessPipelineHook(next red.ProcessPipelineHook) red.ProcessPipelineHook {
|
||||
return func(ctx context.Context, cmds []red.Cmder) error {
|
||||
if len(cmds) == 0 {
|
||||
return next(ctx, cmds)
|
||||
@@ -83,6 +84,33 @@ func (h hook) ProcessPipelineHook(next red.ProcessPipelineHook) red.ProcessPipel
|
||||
}
|
||||
}
|
||||
|
||||
func (h durationHook) startSpan(ctx context.Context, cmds ...red.Cmder) (context.Context, func(err error)) {
|
||||
tracer := trace.TracerFromContext(ctx)
|
||||
|
||||
ctx, span := tracer.Start(ctx,
|
||||
spanName,
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
|
||||
cmdStrs := make([]string, 0, len(cmds))
|
||||
for _, cmd := range cmds {
|
||||
cmdStrs = append(cmdStrs, cmd.Name())
|
||||
}
|
||||
span.SetAttributes(redisCmdsAttributeKey.StringSlice(cmdStrs))
|
||||
|
||||
return ctx, func(err error) {
|
||||
defer span.End()
|
||||
|
||||
if err == nil || errors.Is(err, red.Nil) {
|
||||
span.SetStatus(codes.Ok, "")
|
||||
return
|
||||
}
|
||||
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
span.RecordError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func formatError(err error) string {
|
||||
if err == nil || errors.Is(err, red.Nil) {
|
||||
return ""
|
||||
@@ -95,7 +123,7 @@ func formatError(err error) string {
|
||||
}
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
case errors.Is(err, io.EOF):
|
||||
return "eof"
|
||||
case errors.Is(err, context.DeadlineExceeded):
|
||||
return "context deadline"
|
||||
@@ -123,30 +151,3 @@ func logDuration(ctx context.Context, cmds []red.Cmder, duration time.Duration)
|
||||
}
|
||||
logx.WithContext(ctx).WithDuration(duration).Slowf("[REDIS] slowcall on executing: %s", buf.String())
|
||||
}
|
||||
|
||||
func (h hook) startSpan(ctx context.Context, cmds ...red.Cmder) (context.Context, func(err error)) {
|
||||
tracer := trace.TracerFromContext(ctx)
|
||||
|
||||
ctx, span := tracer.Start(ctx,
|
||||
spanName,
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
|
||||
cmdStrs := make([]string, 0, len(cmds))
|
||||
for _, cmd := range cmds {
|
||||
cmdStrs = append(cmdStrs, cmd.Name())
|
||||
}
|
||||
span.SetAttributes(redisCmdsAttributeKey.StringSlice(cmdStrs))
|
||||
|
||||
return ctx, func(err error) {
|
||||
defer span.End()
|
||||
|
||||
if err == nil || errors.Is(err, red.Nil) {
|
||||
span.SetStatus(codes.Ok, "")
|
||||
return
|
||||
}
|
||||
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
span.RecordError(err)
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ func TestHookProcessCase1(t *testing.T) {
|
||||
tracetest.NewInMemoryExporter(t)
|
||||
w := logtest.NewCollector(t)
|
||||
|
||||
err := durationHook.ProcessHook(func(ctx context.Context, cmd red.Cmder) error {
|
||||
err := defaultDurationHook.ProcessHook(func(ctx context.Context, cmd red.Cmder) error {
|
||||
assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name())
|
||||
return nil
|
||||
})(context.Background(), red.NewCmd(context.Background()))
|
||||
@@ -36,7 +36,7 @@ func TestHookProcessCase2(t *testing.T) {
|
||||
tracetest.NewInMemoryExporter(t)
|
||||
w := logtest.NewCollector(t)
|
||||
|
||||
err := durationHook.ProcessHook(func(ctx context.Context, cmd red.Cmder) error {
|
||||
err := defaultDurationHook.ProcessHook(func(ctx context.Context, cmd red.Cmder) error {
|
||||
assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name())
|
||||
time.Sleep(slowThreshold.Load() + time.Millisecond)
|
||||
return nil
|
||||
@@ -54,12 +54,12 @@ func TestHookProcessPipelineCase1(t *testing.T) {
|
||||
tracetest.NewInMemoryExporter(t)
|
||||
w := logtest.NewCollector(t)
|
||||
|
||||
err := durationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error {
|
||||
err := defaultDurationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error {
|
||||
return nil
|
||||
})(context.Background(), nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = durationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error {
|
||||
err = defaultDurationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error {
|
||||
assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name())
|
||||
return nil
|
||||
})(context.Background(), []red.Cmder{
|
||||
@@ -74,7 +74,7 @@ func TestHookProcessPipelineCase2(t *testing.T) {
|
||||
tracetest.NewInMemoryExporter(t)
|
||||
w := logtest.NewCollector(t)
|
||||
|
||||
err := durationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error {
|
||||
err := defaultDurationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error {
|
||||
assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name())
|
||||
time.Sleep(slowThreshold.Load() + time.Millisecond)
|
||||
return nil
|
||||
@@ -91,7 +91,7 @@ func TestHookProcessPipelineCase2(t *testing.T) {
|
||||
func TestHookProcessPipelineCase3(t *testing.T) {
|
||||
te := tracetest.NewInMemoryExporter(t)
|
||||
|
||||
err := durationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error {
|
||||
err := defaultDurationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error {
|
||||
assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name())
|
||||
return assert.AnError
|
||||
})(context.Background(), []red.Cmder{
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -37,8 +37,11 @@ func getClient(r *Redis) (*red.Client, error) {
|
||||
MinIdleConns: idleConns,
|
||||
TLSConfig: tlsConfig,
|
||||
})
|
||||
store.AddHook(durationHook)
|
||||
for _, hook := range r.hooks {
|
||||
|
||||
hooks := append([]red.Hook{defaultDurationHook, breakerHook{
|
||||
brk: r.brk,
|
||||
}}, r.hooks...)
|
||||
for _, hook := range hooks {
|
||||
store.AddHook(hook)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,11 @@ func getCluster(r *Redis) (*red.ClusterClient, error) {
|
||||
MinIdleConns: idleConns,
|
||||
TLSConfig: tlsConfig,
|
||||
})
|
||||
store.AddHook(durationHook)
|
||||
for _, hook := range r.hooks {
|
||||
|
||||
hooks := append([]red.Hook{defaultDurationHook, breakerHook{
|
||||
brk: r.brk,
|
||||
}}, r.hooks...)
|
||||
for _, hook := range hooks {
|
||||
store.AddHook(hook)
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestGetCluster(t *testing.T) {
|
||||
Addr: r.Addr(),
|
||||
Type: ClusterType,
|
||||
tls: true,
|
||||
hooks: []red.Hook{durationHook},
|
||||
hooks: []red.Hook{defaultDurationHook},
|
||||
})
|
||||
if assert.NoError(t, err) {
|
||||
assert.NotNil(t, c)
|
||||
|
||||
@@ -2,6 +2,7 @@ package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
@@ -41,7 +42,7 @@ type RedisLock struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
rand.NewSource(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// NewRedisLock returns a RedisLock.
|
||||
@@ -64,7 +65,7 @@ func (rl *RedisLock) AcquireCtx(ctx context.Context) (bool, error) {
|
||||
resp, err := rl.store.ScriptRunCtx(ctx, lockScript, []string{rl.key}, []string{
|
||||
rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance),
|
||||
})
|
||||
if err == red.Nil {
|
||||
if errors.Is(err, red.Nil) {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
logx.Errorf("Error on acquiring lock for %s, %s", rl.key, err.Error())
|
||||
|
||||
@@ -13,7 +13,7 @@ const (
|
||||
|
||||
// NewMysql returns a mysql connection.
|
||||
func NewMysql(datasource string, opts ...SqlOption) SqlConn {
|
||||
opts = append(opts, withMysqlAcceptable())
|
||||
opts = append([]SqlOption{withMysqlAcceptable()}, opts...)
|
||||
return NewSqlConn(mysqlDriverName, datasource, opts...)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package sqlx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
@@ -38,7 +37,6 @@ func TestBreakerOnNotHandlingDuplicateEntry(t *testing.T) {
|
||||
func TestMysqlAcceptable(t *testing.T) {
|
||||
conn := NewMysql("nomysql").(*commonSqlConn)
|
||||
withMysqlAcceptable()(conn)
|
||||
assert.EqualValues(t, reflect.ValueOf(mysqlAcceptable).Pointer(), reflect.ValueOf(conn.accept).Pointer())
|
||||
assert.True(t, mysqlAcceptable(nil))
|
||||
assert.False(t, mysqlAcceptable(errors.New("any")))
|
||||
assert.False(t, mysqlAcceptable(new(mysql.MySQLError)))
|
||||
|
||||
@@ -42,21 +42,6 @@ type (
|
||||
// SqlOption defines the method to customize a sql connection.
|
||||
SqlOption func(*commonSqlConn)
|
||||
|
||||
// StmtSession interface represents a session that can be used to execute statements.
|
||||
StmtSession interface {
|
||||
Close() error
|
||||
Exec(args ...any) (sql.Result, error)
|
||||
ExecCtx(ctx context.Context, args ...any) (sql.Result, error)
|
||||
QueryRow(v any, args ...any) error
|
||||
QueryRowCtx(ctx context.Context, v any, args ...any) error
|
||||
QueryRowPartial(v any, args ...any) error
|
||||
QueryRowPartialCtx(ctx context.Context, v any, args ...any) error
|
||||
QueryRows(v any, args ...any) error
|
||||
QueryRowsCtx(ctx context.Context, v any, args ...any) error
|
||||
QueryRowsPartial(v any, args ...any) error
|
||||
QueryRowsPartialCtx(ctx context.Context, v any, args ...any) error
|
||||
}
|
||||
|
||||
// thread-safe
|
||||
// Because CORBA doesn't support PREPARE, so we need to combine the
|
||||
// query arguments into one string and do underlying query without arguments
|
||||
@@ -65,7 +50,7 @@ type (
|
||||
onError func(context.Context, error)
|
||||
beginTx beginnable
|
||||
brk breaker.Breaker
|
||||
accept func(error) bool
|
||||
accept breaker.Acceptable
|
||||
}
|
||||
|
||||
connProvider func() (*sql.DB, error)
|
||||
@@ -76,18 +61,6 @@ type (
|
||||
Query(query string, args ...any) (*sql.Rows, error)
|
||||
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
|
||||
}
|
||||
|
||||
statement struct {
|
||||
query string
|
||||
stmt *sql.Stmt
|
||||
}
|
||||
|
||||
stmtConn interface {
|
||||
Exec(args ...any) (sql.Result, error)
|
||||
ExecContext(ctx context.Context, args ...any) (sql.Result, error)
|
||||
Query(args ...any) (*sql.Rows, error)
|
||||
QueryContext(ctx context.Context, args ...any) (*sql.Rows, error)
|
||||
}
|
||||
)
|
||||
|
||||
// NewSqlConn returns a SqlConn with given driver name and datasource.
|
||||
@@ -189,8 +162,10 @@ func (db *commonSqlConn) PrepareCtx(ctx context.Context, query string) (stmt Stm
|
||||
}
|
||||
|
||||
stmt = statement{
|
||||
query: query,
|
||||
stmt: st,
|
||||
query: query,
|
||||
stmt: st,
|
||||
brk: db.brk,
|
||||
accept: db.acceptable,
|
||||
}
|
||||
return nil
|
||||
}, db.acceptable)
|
||||
@@ -311,7 +286,7 @@ func (db *commonSqlConn) acceptable(err error) bool {
|
||||
|
||||
func (db *commonSqlConn) queryRows(ctx context.Context, scanner func(*sql.Rows) error,
|
||||
q string, args ...any) (err error) {
|
||||
var qerr error
|
||||
var scanFailed bool
|
||||
err = db.brk.DoWithAcceptable(func() error {
|
||||
conn, err := db.connProv()
|
||||
if err != nil {
|
||||
@@ -320,11 +295,14 @@ func (db *commonSqlConn) queryRows(ctx context.Context, scanner func(*sql.Rows)
|
||||
}
|
||||
|
||||
return query(ctx, conn, func(rows *sql.Rows) error {
|
||||
qerr = scanner(rows)
|
||||
return qerr
|
||||
e := scanner(rows)
|
||||
if e != nil {
|
||||
scanFailed = true
|
||||
}
|
||||
return e
|
||||
}, q, args...)
|
||||
}, func(err error) bool {
|
||||
return errors.Is(err, qerr) || db.acceptable(err)
|
||||
return scanFailed || db.acceptable(err)
|
||||
})
|
||||
if errors.Is(err, breaker.ErrServiceUnavailable) {
|
||||
metricReqErr.Inc("queryRows", "breaker")
|
||||
@@ -333,87 +311,17 @@ func (db *commonSqlConn) queryRows(ctx context.Context, scanner func(*sql.Rows)
|
||||
return
|
||||
}
|
||||
|
||||
func (s statement) Close() error {
|
||||
return s.stmt.Close()
|
||||
}
|
||||
|
||||
func (s statement) Exec(args ...any) (sql.Result, error) {
|
||||
return s.ExecCtx(context.Background(), args...)
|
||||
}
|
||||
|
||||
func (s statement) ExecCtx(ctx context.Context, args ...any) (result sql.Result, err error) {
|
||||
ctx, span := startSpan(ctx, "Exec")
|
||||
defer func() {
|
||||
endSpan(span, err)
|
||||
}()
|
||||
|
||||
return execStmt(ctx, s.stmt, s.query, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRow(v any, args ...any) error {
|
||||
return s.QueryRowCtx(context.Background(), v, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowCtx(ctx context.Context, v any, args ...any) (err error) {
|
||||
ctx, span := startSpan(ctx, "QueryRow")
|
||||
defer func() {
|
||||
endSpan(span, err)
|
||||
}()
|
||||
|
||||
return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error {
|
||||
return unmarshalRow(v, rows, true)
|
||||
}, s.query, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowPartial(v any, args ...any) error {
|
||||
return s.QueryRowPartialCtx(context.Background(), v, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowPartialCtx(ctx context.Context, v any, args ...any) (err error) {
|
||||
ctx, span := startSpan(ctx, "QueryRowPartial")
|
||||
defer func() {
|
||||
endSpan(span, err)
|
||||
}()
|
||||
|
||||
return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error {
|
||||
return unmarshalRow(v, rows, false)
|
||||
}, s.query, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRows(v any, args ...any) error {
|
||||
return s.QueryRowsCtx(context.Background(), v, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowsCtx(ctx context.Context, v any, args ...any) (err error) {
|
||||
ctx, span := startSpan(ctx, "QueryRows")
|
||||
defer func() {
|
||||
endSpan(span, err)
|
||||
}()
|
||||
|
||||
return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error {
|
||||
return unmarshalRows(v, rows, true)
|
||||
}, s.query, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowsPartial(v any, args ...any) error {
|
||||
return s.QueryRowsPartialCtx(context.Background(), v, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowsPartialCtx(ctx context.Context, v any, args ...any) (err error) {
|
||||
ctx, span := startSpan(ctx, "QueryRowsPartial")
|
||||
defer func() {
|
||||
endSpan(span, err)
|
||||
}()
|
||||
|
||||
return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error {
|
||||
return unmarshalRows(v, rows, false)
|
||||
}, s.query, args...)
|
||||
}
|
||||
|
||||
// WithAcceptable returns a SqlOption that setting the acceptable function.
|
||||
// acceptable is the func to check if the error can be accepted.
|
||||
func WithAcceptable(acceptable func(err error) bool) SqlOption {
|
||||
return func(conn *commonSqlConn) {
|
||||
conn.accept = acceptable
|
||||
if conn.accept == nil {
|
||||
conn.accept = acceptable
|
||||
} else {
|
||||
pre := conn.accept
|
||||
conn.accept = func(err error) bool {
|
||||
return pre(err) || acceptable(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +156,7 @@ func TestStatement(t *testing.T) {
|
||||
st := statement{
|
||||
query: "foo",
|
||||
stmt: stmt,
|
||||
brk: breaker.NopBreaker(),
|
||||
}
|
||||
assert.NoError(t, st.Close())
|
||||
})
|
||||
@@ -263,6 +264,45 @@ func TestBreakerWithScanError(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestWithAcceptable(t *testing.T) {
|
||||
var (
|
||||
acceptableErr = errors.New("acceptable")
|
||||
acceptableErr2 = errors.New("acceptable2")
|
||||
acceptableErr3 = errors.New("acceptable3")
|
||||
)
|
||||
opts := []SqlOption{
|
||||
WithAcceptable(func(err error) bool {
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
return errors.Is(err, acceptableErr)
|
||||
}),
|
||||
WithAcceptable(func(err error) bool {
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
return errors.Is(err, acceptableErr2)
|
||||
}),
|
||||
WithAcceptable(func(err error) bool {
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
return errors.Is(err, acceptableErr3)
|
||||
}),
|
||||
}
|
||||
|
||||
var conn = &commonSqlConn{}
|
||||
for _, opt := range opts {
|
||||
opt(conn)
|
||||
}
|
||||
|
||||
assert.True(t, conn.accept(nil))
|
||||
assert.False(t, conn.accept(assert.AnError))
|
||||
assert.True(t, conn.accept(acceptableErr))
|
||||
assert.True(t, conn.accept(acceptableErr2))
|
||||
assert.True(t, conn.accept(acceptableErr3))
|
||||
}
|
||||
|
||||
func buildConn() (mock sqlmock.Sqlmock, err error) {
|
||||
_, err = connManager.GetResource(mockedDatasource, func() (io.Closer, error) {
|
||||
var db *sql.DB
|
||||
|
||||
@@ -3,8 +3,10 @@ package sqlx
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/breaker"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/syncx"
|
||||
"github.com/zeromicro/go-zero/core/timex"
|
||||
@@ -18,6 +20,145 @@ var (
|
||||
logSlowSql = syncx.ForAtomicBool(true)
|
||||
)
|
||||
|
||||
type (
|
||||
// StmtSession interface represents a session that can be used to execute statements.
|
||||
StmtSession interface {
|
||||
Close() error
|
||||
Exec(args ...any) (sql.Result, error)
|
||||
ExecCtx(ctx context.Context, args ...any) (sql.Result, error)
|
||||
QueryRow(v any, args ...any) error
|
||||
QueryRowCtx(ctx context.Context, v any, args ...any) error
|
||||
QueryRowPartial(v any, args ...any) error
|
||||
QueryRowPartialCtx(ctx context.Context, v any, args ...any) error
|
||||
QueryRows(v any, args ...any) error
|
||||
QueryRowsCtx(ctx context.Context, v any, args ...any) error
|
||||
QueryRowsPartial(v any, args ...any) error
|
||||
QueryRowsPartialCtx(ctx context.Context, v any, args ...any) error
|
||||
}
|
||||
|
||||
statement struct {
|
||||
query string
|
||||
stmt *sql.Stmt
|
||||
brk breaker.Breaker
|
||||
accept breaker.Acceptable
|
||||
}
|
||||
|
||||
stmtConn interface {
|
||||
Exec(args ...any) (sql.Result, error)
|
||||
ExecContext(ctx context.Context, args ...any) (sql.Result, error)
|
||||
Query(args ...any) (*sql.Rows, error)
|
||||
QueryContext(ctx context.Context, args ...any) (*sql.Rows, error)
|
||||
}
|
||||
)
|
||||
|
||||
func (s statement) Close() error {
|
||||
return s.stmt.Close()
|
||||
}
|
||||
|
||||
func (s statement) Exec(args ...any) (sql.Result, error) {
|
||||
return s.ExecCtx(context.Background(), args...)
|
||||
}
|
||||
|
||||
func (s statement) ExecCtx(ctx context.Context, args ...any) (result sql.Result, err error) {
|
||||
ctx, span := startSpan(ctx, "Exec")
|
||||
defer func() {
|
||||
endSpan(span, err)
|
||||
}()
|
||||
|
||||
err = s.brk.DoWithAcceptable(func() error {
|
||||
result, err = execStmt(ctx, s.stmt, s.query, args...)
|
||||
return err
|
||||
}, func(err error) bool {
|
||||
return s.accept(err)
|
||||
})
|
||||
if errors.Is(err, breaker.ErrServiceUnavailable) {
|
||||
metricReqErr.Inc("stmt_exec", "breaker")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s statement) QueryRow(v any, args ...any) error {
|
||||
return s.QueryRowCtx(context.Background(), v, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowCtx(ctx context.Context, v any, args ...any) (err error) {
|
||||
ctx, span := startSpan(ctx, "QueryRow")
|
||||
defer func() {
|
||||
endSpan(span, err)
|
||||
}()
|
||||
|
||||
return s.queryRows(ctx, func(v any, scanner rowsScanner) error {
|
||||
return unmarshalRow(v, scanner, true)
|
||||
}, v, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowPartial(v any, args ...any) error {
|
||||
return s.QueryRowPartialCtx(context.Background(), v, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowPartialCtx(ctx context.Context, v any, args ...any) (err error) {
|
||||
ctx, span := startSpan(ctx, "QueryRowPartial")
|
||||
defer func() {
|
||||
endSpan(span, err)
|
||||
}()
|
||||
|
||||
return s.queryRows(ctx, func(v any, scanner rowsScanner) error {
|
||||
return unmarshalRow(v, scanner, false)
|
||||
}, v, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRows(v any, args ...any) error {
|
||||
return s.QueryRowsCtx(context.Background(), v, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowsCtx(ctx context.Context, v any, args ...any) (err error) {
|
||||
ctx, span := startSpan(ctx, "QueryRows")
|
||||
defer func() {
|
||||
endSpan(span, err)
|
||||
}()
|
||||
|
||||
return s.queryRows(ctx, func(v any, scanner rowsScanner) error {
|
||||
return unmarshalRows(v, scanner, true)
|
||||
}, v, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowsPartial(v any, args ...any) error {
|
||||
return s.QueryRowsPartialCtx(context.Background(), v, args...)
|
||||
}
|
||||
|
||||
func (s statement) QueryRowsPartialCtx(ctx context.Context, v any, args ...any) (err error) {
|
||||
ctx, span := startSpan(ctx, "QueryRowsPartial")
|
||||
defer func() {
|
||||
endSpan(span, err)
|
||||
}()
|
||||
|
||||
return s.queryRows(ctx, func(v any, scanner rowsScanner) error {
|
||||
return unmarshalRows(v, scanner, false)
|
||||
}, v, args...)
|
||||
}
|
||||
|
||||
func (s statement) queryRows(ctx context.Context, scanFn func(any, rowsScanner) error,
|
||||
v any, args ...any) error {
|
||||
var scanFailed bool
|
||||
err := s.brk.DoWithAcceptable(func() error {
|
||||
return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error {
|
||||
err := scanFn(v, rows)
|
||||
if err != nil {
|
||||
scanFailed = true
|
||||
}
|
||||
return err
|
||||
}, s.query, args...)
|
||||
}, func(err error) bool {
|
||||
return scanFailed || s.accept(err)
|
||||
})
|
||||
if errors.Is(err, breaker.ErrServiceUnavailable) {
|
||||
metricReqErr.Inc("stmt_queryRows", "breaker")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DisableLog disables logging of sql statements, includes info and slow logs.
|
||||
func DisableLog() {
|
||||
logSql.Set(false)
|
||||
|
||||
@@ -7,7 +7,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/core/breaker"
|
||||
"github.com/zeromicro/go-zero/core/stores/dbtest"
|
||||
)
|
||||
|
||||
var errMockedPlaceholder = errors.New("placeholder")
|
||||
@@ -219,6 +222,74 @@ func TestNilGuard(t *testing.T) {
|
||||
assert.Equal(t, nilGuard{}, guard)
|
||||
}
|
||||
|
||||
func TestStmtBreaker(t *testing.T) {
|
||||
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||
mock.ExpectPrepare("any")
|
||||
|
||||
conn := NewSqlConnFromDB(db)
|
||||
stmt, err := conn.Prepare("any")
|
||||
assert.NoError(t, err)
|
||||
|
||||
var val struct {
|
||||
Foo int
|
||||
Bar string
|
||||
}
|
||||
for i := 0; i < 1000; i++ {
|
||||
row := sqlmock.NewRows([]string{"foo"}).AddRow("bar")
|
||||
mock.ExpectQuery("any").WillReturnRows(row)
|
||||
err := stmt.QueryRow(&val)
|
||||
assert.Error(t, err)
|
||||
assert.NotErrorIs(t, err, breaker.ErrServiceUnavailable)
|
||||
}
|
||||
})
|
||||
|
||||
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||
mock.ExpectPrepare("any")
|
||||
conn := NewSqlConnFromDB(db)
|
||||
stmt, err := conn.Prepare("any")
|
||||
assert.NoError(t, err)
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
assert.Error(t, conn.Transact(func(session Session) error {
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
var breakerTriggered bool
|
||||
for i := 0; i < 1000; i++ {
|
||||
_, err = stmt.Exec("any")
|
||||
if errors.Is(err, breaker.ErrServiceUnavailable) {
|
||||
breakerTriggered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, breakerTriggered)
|
||||
})
|
||||
|
||||
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||
mock.ExpectPrepare("any")
|
||||
conn := NewSqlConnFromDB(db)
|
||||
stmt, err := conn.Prepare("any")
|
||||
assert.NoError(t, err)
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
assert.Error(t, conn.Transact(func(session Session) error {
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
var breakerTriggered bool
|
||||
for i := 0; i < 1000; i++ {
|
||||
err = stmt.QueryRows(&struct{}{}, "any")
|
||||
if errors.Is(err, breaker.ErrServiceUnavailable) {
|
||||
breakerTriggered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, breakerTriggered)
|
||||
})
|
||||
}
|
||||
|
||||
type mockedSessionConn struct {
|
||||
lastInsertId int64
|
||||
rowsAffected int64
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/breaker"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -75,6 +77,7 @@ func (t txSession) PrepareCtx(ctx context.Context, q string) (stmtSession StmtSe
|
||||
return statement{
|
||||
query: q,
|
||||
stmt: stmt,
|
||||
brk: breaker.NopBreaker(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func FuzzNodeFind(f *testing.F) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
rand.NewSource(time.Now().UnixNano())
|
||||
|
||||
f.Add(10)
|
||||
f.Fuzz(func(t *testing.T, keys int) {
|
||||
|
||||
105
core/threading/stablerunner.go
Normal file
105
core/threading/stablerunner.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package threading
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const factor = 10
|
||||
|
||||
var (
|
||||
ErrRunnerClosed = errors.New("runner closed")
|
||||
|
||||
bufSize = runtime.NumCPU() * factor
|
||||
)
|
||||
|
||||
// StableRunner is a runner that guarantees messages are taken out with the pushed order.
|
||||
// This runner is typically useful for Kafka consumers with parallel processing.
|
||||
type StableRunner[I, O any] struct {
|
||||
handle func(I) O
|
||||
consumedIndex uint64
|
||||
writtenIndex uint64
|
||||
ring []*struct {
|
||||
value chan O
|
||||
lock sync.Mutex
|
||||
}
|
||||
runner *TaskRunner
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// NewStableRunner returns a new StableRunner with given message processor fn.
|
||||
func NewStableRunner[I, O any](fn func(I) O) *StableRunner[I, O] {
|
||||
ring := make([]*struct {
|
||||
value chan O
|
||||
lock sync.Mutex
|
||||
}, bufSize)
|
||||
for i := 0; i < bufSize; i++ {
|
||||
ring[i] = &struct {
|
||||
value chan O
|
||||
lock sync.Mutex
|
||||
}{
|
||||
value: make(chan O, 1),
|
||||
}
|
||||
}
|
||||
|
||||
return &StableRunner[I, O]{
|
||||
handle: fn,
|
||||
ring: ring,
|
||||
runner: NewTaskRunner(runtime.NumCPU()),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the next processed message in order.
|
||||
// This method should be called in one goroutine.
|
||||
func (r *StableRunner[I, O]) Get() (O, error) {
|
||||
defer atomic.AddUint64(&r.consumedIndex, 1)
|
||||
|
||||
index := atomic.LoadUint64(&r.consumedIndex)
|
||||
offset := index % uint64(bufSize)
|
||||
holder := r.ring[offset]
|
||||
|
||||
select {
|
||||
case o := <-holder.value:
|
||||
return o, nil
|
||||
case <-r.done:
|
||||
if atomic.LoadUint64(&r.consumedIndex) < atomic.LoadUint64(&r.writtenIndex) {
|
||||
return <-holder.value, nil
|
||||
}
|
||||
|
||||
var o O
|
||||
return o, ErrRunnerClosed
|
||||
}
|
||||
}
|
||||
|
||||
// Push pushes the message v into the runner and to be processed concurrently,
|
||||
// after processed, it will be cached to let caller take it in pushing order.
|
||||
func (r *StableRunner[I, O]) Push(v I) error {
|
||||
select {
|
||||
case <-r.done:
|
||||
return ErrRunnerClosed
|
||||
default:
|
||||
index := atomic.AddUint64(&r.writtenIndex, 1)
|
||||
offset := (index - 1) % uint64(bufSize)
|
||||
holder := r.ring[offset]
|
||||
holder.lock.Lock()
|
||||
r.runner.Schedule(func() {
|
||||
defer holder.lock.Unlock()
|
||||
o := r.handle(v)
|
||||
holder.value <- o
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Wait waits all the messages to be processed and taken from inner buffer.
|
||||
func (r *StableRunner[I, O]) Wait() {
|
||||
close(r.done)
|
||||
r.runner.Wait()
|
||||
for atomic.LoadUint64(&r.consumedIndex) < atomic.LoadUint64(&r.writtenIndex) {
|
||||
runtime.Gosched()
|
||||
}
|
||||
}
|
||||
97
core/threading/stablerunner_test.go
Normal file
97
core/threading/stablerunner_test.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package threading
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStableRunner(t *testing.T) {
|
||||
size := bufSize * 2
|
||||
rand.NewSource(time.Now().UnixNano())
|
||||
runner := NewStableRunner(func(v int) float64 {
|
||||
if v == 0 {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
} else {
|
||||
time.Sleep(time.Millisecond * time.Duration(rand.Intn(10)))
|
||||
}
|
||||
return float64(v) + 0.5
|
||||
})
|
||||
|
||||
var waitGroup sync.WaitGroup
|
||||
waitGroup.Add(1)
|
||||
go func() {
|
||||
for i := 0; i < size; i++ {
|
||||
assert.NoError(t, runner.Push(i))
|
||||
}
|
||||
runner.Wait()
|
||||
waitGroup.Done()
|
||||
}()
|
||||
|
||||
values := make([]float64, size)
|
||||
for i := 0; i < size; i++ {
|
||||
var err error
|
||||
values[i], err = runner.Get()
|
||||
assert.NoError(t, err)
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
assert.True(t, sort.Float64sAreSorted(values))
|
||||
waitGroup.Wait()
|
||||
|
||||
assert.Equal(t, ErrRunnerClosed, runner.Push(1))
|
||||
_, err := runner.Get()
|
||||
assert.Equal(t, ErrRunnerClosed, err)
|
||||
}
|
||||
|
||||
func FuzzStableRunner(f *testing.F) {
|
||||
rand.NewSource(time.Now().UnixNano())
|
||||
f.Add(uint64(bufSize))
|
||||
f.Fuzz(func(t *testing.T, n uint64) {
|
||||
runner := NewStableRunner(func(v int) float64 {
|
||||
if v == 0 {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
} else {
|
||||
time.Sleep(time.Millisecond * time.Duration(rand.Intn(10)))
|
||||
}
|
||||
return float64(v) + 0.5
|
||||
})
|
||||
|
||||
go func() {
|
||||
for i := 0; i < int(n); i++ {
|
||||
assert.NoError(t, runner.Push(i))
|
||||
}
|
||||
}()
|
||||
|
||||
values := make([]float64, n)
|
||||
for i := 0; i < int(n); i++ {
|
||||
var err error
|
||||
values[i], err = runner.Get()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
runner.Wait()
|
||||
assert.True(t, sort.Float64sAreSorted(values))
|
||||
|
||||
// make sure returning errors after runner is closed
|
||||
assert.Equal(t, ErrRunnerClosed, runner.Push(1))
|
||||
_, err := runner.Get()
|
||||
assert.Equal(t, ErrRunnerClosed, err)
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkStableRunner(b *testing.B) {
|
||||
runner := NewStableRunner(func(v int) float64 {
|
||||
time.Sleep(time.Millisecond * time.Duration(rand.Intn(10)))
|
||||
return float64(v) + 0.5
|
||||
})
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = runner.Push(i)
|
||||
_, _ = runner.Get()
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,13 @@ func StartAgent(c Config) {
|
||||
|
||||
// StopAgent shuts down the span processors in the order they were registered.
|
||||
func StopAgent() {
|
||||
_ = tp.Shutdown(context.Background())
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if tp != nil {
|
||||
_ = tp.Shutdown(context.Background())
|
||||
tp = nil
|
||||
}
|
||||
}
|
||||
|
||||
func createExporter(c Config) (sdktrace.SpanExporter, error) {
|
||||
|
||||
@@ -34,7 +34,7 @@ type mockSpan struct {
|
||||
options []trace.EventOption
|
||||
}
|
||||
|
||||
func (m *mockSpan) End(options ...trace.SpanEndOption) {
|
||||
func (m *mockSpan) End(_ ...trace.SpanEndOption) {
|
||||
}
|
||||
|
||||
func (m *mockSpan) AddEvent(name string, options ...trace.EventOption) {
|
||||
@@ -46,20 +46,20 @@ func (m *mockSpan) IsRecording() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *mockSpan) RecordError(err error, options ...trace.EventOption) {
|
||||
func (m *mockSpan) RecordError(_ error, _ ...trace.EventOption) {
|
||||
}
|
||||
|
||||
func (m *mockSpan) SpanContext() trace.SpanContext {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockSpan) SetStatus(code codes.Code, description string) {
|
||||
func (m *mockSpan) SetStatus(_ codes.Code, _ string) {
|
||||
}
|
||||
|
||||
func (m *mockSpan) SetName(name string) {
|
||||
func (m *mockSpan) SetName(_ string) {
|
||||
}
|
||||
|
||||
func (m *mockSpan) SetAttributes(kv ...attribute.KeyValue) {
|
||||
func (m *mockSpan) SetAttributes(_ ...attribute.KeyValue) {
|
||||
}
|
||||
|
||||
func (m *mockSpan) TracerProvider() trace.TracerProvider {
|
||||
|
||||
@@ -4,5 +4,5 @@ import "net/http"
|
||||
|
||||
// TraceIdKey is the trace id header.
|
||||
// https://www.w3.org/TR/trace-context/#trace-id
|
||||
// May change it to trace-id afterwards.
|
||||
// May change it to trace-id afterward.
|
||||
var TraceIdKey = http.CanonicalHeaderKey("x-trace-id")
|
||||
|
||||
35
go.mod
35
go.mod
@@ -6,22 +6,22 @@ toolchain go1.21.5
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
github.com/alicebob/miniredis/v2 v2.31.1
|
||||
github.com/alicebob/miniredis/v2 v2.32.1
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/fullstorydev/grpcurl v1.8.9
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/go-sql-driver/mysql v1.8.0
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jackc/pgx/v5 v5.5.3
|
||||
github.com/jackc/pgx/v5 v5.5.5
|
||||
github.com/jhump/protoreflect v1.15.6
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/pelletier/go-toml/v2 v2.1.1
|
||||
github.com/prometheus/client_golang v1.18.0
|
||||
github.com/redis/go-redis/v9 v9.4.0
|
||||
github.com/spaolacci/murmur3 v1.1.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.etcd.io/etcd/api/v3 v3.5.12
|
||||
go.etcd.io/etcd/client/v3 v3.5.12
|
||||
go.mongodb.org/mongo-driver v1.13.1
|
||||
@@ -35,12 +35,12 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.19.0
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
go.uber.org/goleak v1.2.1
|
||||
golang.org/x/net v0.21.0
|
||||
golang.org/x/sys v0.17.0
|
||||
golang.org/x/net v0.22.0
|
||||
golang.org/x/sys v0.18.0
|
||||
golang.org/x/time v0.5.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17
|
||||
google.golang.org/grpc v1.61.1
|
||||
google.golang.org/protobuf v1.32.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80
|
||||
google.golang.org/grpc v1.62.1
|
||||
google.golang.org/protobuf v1.33.0
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
@@ -51,6 +51,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bufbuild/protocompile v0.8.0 // indirect
|
||||
@@ -98,7 +99,7 @@ require (
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
|
||||
github.com/yuin/gopher-lua v1.1.0 // indirect
|
||||
github.com/yuin/gopher-lua v1.1.1 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.19.0 // indirect
|
||||
@@ -106,14 +107,14 @@ require (
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/oauth2 v0.14.0 // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.org/x/term v0.17.0 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/oauth2 v0.16.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/term v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
|
||||
78
go.sum
78
go.sum
@@ -1,11 +1,12 @@
|
||||
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/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0=
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis/v2 v2.31.1 h1:7XAt0uUg3DtwEKW5ZAGa+K7FZV2DdKQo5K/6TTnfX8Y=
|
||||
github.com/alicebob/miniredis/v2 v2.31.1/go.mod h1:UB/T2Uztp7MlFSDakaX1sTXUv5CASoprx0wulRT6HBg=
|
||||
github.com/alicebob/miniredis/v2 v2.32.1 h1:Bz7CciDnYSaa0mX5xODh6GUITRSx+cVhjNoOR4JssBo=
|
||||
github.com/alicebob/miniredis/v2 v2.32.1/go.mod h1:AqkLNAfUm0K07J28hnAyyQKf/x0YkCY/g5DCtuL01Mw=
|
||||
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=
|
||||
@@ -51,8 +52,8 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
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.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
|
||||
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
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=
|
||||
@@ -60,15 +61,14 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
|
||||
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
|
||||
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
@@ -94,8 +94,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-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.3 h1:Ces6/M3wbDXYpM8JyyPD57ivTtJACFZJd885pdIaV2s=
|
||||
github.com/jackc/pgx/v5 v5.5.3/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jhump/protoreflect v1.15.6 h1:WMYJbw2Wo+KOWwZFvgY0jMoVHM6i4XIvRs2RcBj5VmI=
|
||||
@@ -176,15 +176,17 @@ 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 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
@@ -198,8 +200,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=
|
||||
github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
|
||||
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c=
|
||||
go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.12 h1:EYDL6pWwyOsylrQyLp2w+HkQ46ATiOvoEdMarindU2A=
|
||||
@@ -246,8 +248,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -260,17 +262,17 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
|
||||
golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -284,12 +286,12 @@ 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.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -314,18 +316,18 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
|
||||
google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
|
||||
google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
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=
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
type DepositServer struct{}
|
||||
|
||||
// Deposit handles the deposit requests.
|
||||
func (*DepositServer) Deposit(ctx context.Context, req *DepositRequest) (*DepositResponse, error) {
|
||||
func (*DepositServer) Deposit(_ context.Context, req *DepositRequest) (*DepositResponse, error) {
|
||||
if req.GetAmount() < 0 {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "cannot deposit %v", req.GetAmount())
|
||||
}
|
||||
|
||||
10
readme-cn.md
10
readme-cn.md
@@ -120,12 +120,12 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro
|
||||
# docker for amd64 architecture
|
||||
docker pull kevinwan/goctl
|
||||
# run goctl like
|
||||
docker run --rm -it -v `pwd`:/app kevinwan/goctl goctl --help
|
||||
docker run --rm -it -v `pwd`:/app kevinwan/goctl --help
|
||||
|
||||
# docker for arm64(Mac) architecture
|
||||
docker pull kevinwan/goctl:latest-arm64
|
||||
# run goctl like
|
||||
docker run --rm -it -v `pwd`:/app kevinwan/goctl:latest-arm64 goctl --help
|
||||
docker run --rm -it -v `pwd`:/app kevinwan/goctl:latest-arm64 --help
|
||||
```
|
||||
|
||||
确保 goctl 可执行
|
||||
@@ -300,14 +300,16 @@ go-zero 已被许多公司用于生产部署,接入场景如在线教育、电
|
||||
>96. Sonderbase Technologies
|
||||
>97. 上海荣时信息科技有限公司
|
||||
>98. 上海同犀智能科技有限公司
|
||||
>99. 新华三技术有限公司
|
||||
>100. 上海邑脉科技有限公司
|
||||
|
||||
如果贵公司也已使用 go-zero,欢迎在 [登记地址](https://github.com/zeromicro/go-zero/issues/602) 登记,仅仅为了推广,不做其它用途。
|
||||
|
||||
## 10. CNCF 云原生技术全景图
|
||||
|
||||
<p float="left">
|
||||
<img src="https://landscape.cncf.io/images/left-logo.svg" width="150"/>
|
||||
<img src="https://landscape.cncf.io/images/right-logo.svg" width="200"/>
|
||||
<img src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/cncf-logo.svg" width="200"/>
|
||||
<img src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/cncf-landscape-logo.svg" width="150"/>
|
||||
</p>
|
||||
|
||||
go-zero 收录在 [CNCF Cloud Native 云原生技术全景图](https://landscape.cncf.io/?selected=go-zero)。
|
||||
|
||||
@@ -127,12 +127,12 @@ go get -u github.com/zeromicro/go-zero
|
||||
# docker for amd64 architecture
|
||||
docker pull kevinwan/goctl
|
||||
# run goctl like
|
||||
docker run --rm -it -v `pwd`:/app kevinwan/goctl goctl --help
|
||||
docker run --rm -it -v `pwd`:/app kevinwan/goctl --help
|
||||
|
||||
# docker for arm64(Mac) architecture
|
||||
docker pull kevinwan/goctl:latest-arm64
|
||||
# run goctl like
|
||||
docker run --rm -it -v `pwd`:/app kevinwan/goctl:latest-arm64 goctl --help
|
||||
docker run --rm -it -v `pwd`:/app kevinwan/goctl:latest-arm64 --help
|
||||
```
|
||||
|
||||
make sure goctl is executable.
|
||||
@@ -248,8 +248,8 @@ Join the chat via https://discord.gg/4JQvC5A4Fe
|
||||
## Cloud Native Landscape
|
||||
|
||||
<p float="left">
|
||||
<img src="https://landscape.cncf.io/images/left-logo.svg" width="150"/>
|
||||
<img src="https://landscape.cncf.io/images/right-logo.svg" width="200"/>
|
||||
<img src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/cncf-logo.svg" width="200"/>
|
||||
<img src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/cncf-landscape-logo.svg" width="150"/>
|
||||
</p>
|
||||
|
||||
go-zero enlisted in the [CNCF Cloud Native Landscape](https://landscape.cncf.io/?selected=go-zero).
|
||||
|
||||
@@ -217,9 +217,12 @@ func (ng *engine) notFoundHandler(next http.Handler) http.Handler {
|
||||
handler.TraceHandler(ng.conf.Name,
|
||||
"",
|
||||
handler.WithTraceIgnorePaths(ng.conf.TraceIgnorePaths)),
|
||||
ng.getLogHandler(),
|
||||
)
|
||||
|
||||
if ng.conf.Middlewares.Log {
|
||||
chn = chn.Append(ng.getLogHandler())
|
||||
}
|
||||
|
||||
var h http.Handler
|
||||
if next != nil {
|
||||
h = chn.Then(next)
|
||||
|
||||
@@ -435,7 +435,7 @@ type mockedRouter struct {
|
||||
func (m mockedRouter) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {
|
||||
}
|
||||
|
||||
func (m mockedRouter) Handle(_, _ string, handler http.Handler) error {
|
||||
func (m mockedRouter) Handle(_, _ string, _ http.Handler) error {
|
||||
return errors.New("foo")
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,8 @@ func executeCallbacks(w http.ResponseWriter, r *http.Request, next http.Handler,
|
||||
}
|
||||
}
|
||||
|
||||
func handleVerificationFailure(w http.ResponseWriter, r *http.Request, next http.Handler, strict bool, code int) {
|
||||
func handleVerificationFailure(w http.ResponseWriter, r *http.Request, next http.Handler,
|
||||
strict bool, _ int) {
|
||||
if strict {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
} else {
|
||||
|
||||
@@ -327,7 +327,7 @@ func buildRequest(rs requestSettings) (*http.Request, error) {
|
||||
query,
|
||||
bodySign,
|
||||
}, "\n")
|
||||
rs.signature = codec.HmacBase64([]byte(key), contentOfSign)
|
||||
rs.signature = codec.HmacBase64(key, contentOfSign)
|
||||
}
|
||||
|
||||
var mode string
|
||||
|
||||
@@ -224,7 +224,7 @@ func (m flushableResponseWriter) Write(p []byte) (int, error) {
|
||||
return m.writer.Write(p)
|
||||
}
|
||||
|
||||
func (m flushableResponseWriter) WriteHeader(statusCode int) {
|
||||
func (m flushableResponseWriter) WriteHeader(_ int) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
@@ -240,6 +240,6 @@ func (t *halfWriter) Write(p []byte) (n int, err error) {
|
||||
type badWriter struct {
|
||||
}
|
||||
|
||||
func (b *badWriter) Write(p []byte) (n int, err error) {
|
||||
func (b *badWriter) Write(_ []byte) (n int, err error) {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ type mockedReadCloser struct {
|
||||
errMsg string
|
||||
}
|
||||
|
||||
func (m mockedReadCloser) Read(p []byte) (n int, err error) {
|
||||
func (m mockedReadCloser) Read(_ []byte) (n int, err error) {
|
||||
return 0, errors.New(m.errMsg)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ var (
|
||||
Subsystem: "requests",
|
||||
Name: "duration_ms",
|
||||
Help: "http server requests duration(ms).",
|
||||
Labels: []string{"path", "method"},
|
||||
Labels: []string{"path", "method", "code"},
|
||||
Buckets: []float64{5, 10, 25, 50, 100, 250, 500, 750, 1000},
|
||||
})
|
||||
|
||||
@@ -26,7 +26,7 @@ var (
|
||||
Subsystem: "requests",
|
||||
Name: "code_total",
|
||||
Help: "http server requests error count.",
|
||||
Labels: []string{"path", "code", "method"},
|
||||
Labels: []string{"path", "method", "code"},
|
||||
})
|
||||
)
|
||||
|
||||
@@ -37,8 +37,9 @@ func PrometheusHandler(path, method string) func(http.Handler) http.Handler {
|
||||
startTime := timex.Now()
|
||||
cw := response.NewWithCodeResponseWriter(w)
|
||||
defer func() {
|
||||
metricServerReqDur.Observe(timex.Since(startTime).Milliseconds(), path, method)
|
||||
metricServerReqCodeTotal.Inc(path, strconv.Itoa(cw.Code), method)
|
||||
code := strconv.Itoa(cw.Code)
|
||||
metricServerReqDur.Observe(timex.Since(startTime).Milliseconds(), path, method, code)
|
||||
metricServerReqCodeTotal.Inc(path, method, code)
|
||||
}()
|
||||
|
||||
next.ServeHTTP(cw, r)
|
||||
|
||||
@@ -175,6 +175,6 @@ func (m mockedReader) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m mockedReader) Read(p []byte) (n int, err error) {
|
||||
func (m mockedReader) Read(_ []byte) (n int, err error) {
|
||||
return 0, errors.New("dummy")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package httpx
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const xForwardedFor = "X-Forwarded-For"
|
||||
|
||||
@@ -11,7 +14,7 @@ func GetFormValues(r *http.Request) (map[string]any, error) {
|
||||
}
|
||||
|
||||
if err := r.ParseMultipartForm(maxMemory); err != nil {
|
||||
if err != http.ErrNotMultipart {
|
||||
if !errors.Is(err, http.ErrNotMultipart) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
8
tools/goctl/.dockerignore
Normal file
8
tools/goctl/.dockerignore
Normal file
@@ -0,0 +1,8 @@
|
||||
test/
|
||||
.dockerignore
|
||||
.go-version
|
||||
Dockerfile
|
||||
goctl
|
||||
Makefile
|
||||
readme.md
|
||||
readme-cn.md
|
||||
@@ -8,13 +8,12 @@ ENV GOPROXY https://goproxy.cn,direct
|
||||
RUN apk update --no-cache && apk add --no-cache tzdata
|
||||
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||
RUN addgroup -g 1000 -S app && adduser -u 1000 -S app -G app
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
ADD go.mod .
|
||||
ADD go.sum .
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN go mod download
|
||||
RUN go build -ldflags="-s -w" -o /app/goctl ./goctl.go
|
||||
|
||||
|
||||
@@ -22,13 +21,20 @@ FROM golang:alpine
|
||||
|
||||
RUN apk update --no-cache && apk add --no-cache protoc
|
||||
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
|
||||
COPY --from=builder /go/bin/protoc-gen-go /usr/bin/protoc-gen-go
|
||||
COPY --from=builder /go/bin/protoc-gen-go-grpc /usr/bin/protoc-gen-go-grpc
|
||||
COPY --from=builder /etc/passwd /etc/group /etc/
|
||||
COPY --from=builder /usr/share/zoneinfo/ /usr/share/zoneinfo/
|
||||
COPY --from=builder --chown=1000:1000 /go/bin/protoc-gen-go* /app/goctl /usr/local/bin/
|
||||
ENV TZ Asia/Shanghai
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/goctl /usr/bin/goctl
|
||||
USER app
|
||||
|
||||
CMD ["goctl"]
|
||||
LABEL org.opencontainers.image.authors="Kevin Wan"
|
||||
LABEL org.opencontainers.image.base.name="docker.io/library/golang:alpine"
|
||||
LABEL org.opencontainers.image.description="A cloud-native Go microservices framework with cli tool for productivity."
|
||||
LABEL org.opencontainers.image.licenses="MIT"
|
||||
LABEL org.opencontainers.image.source="https://github.com/zeromicro/go-zero"
|
||||
LABEL org.opencontainers.image.title="goctl (cli)"
|
||||
LABEL org.opencontainers.image.version="v1.6.3"
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/goctl"]
|
||||
|
||||
@@ -17,9 +17,9 @@ type parser struct {
|
||||
spec *spec.ApiSpec
|
||||
}
|
||||
|
||||
// Parse parses the api file.
|
||||
// Depreacted: use tools/goctl/pkg/parser/api/parser/parser.go:18 instead,
|
||||
// it will be removed in the future.
|
||||
// Parse parses the api file.
|
||||
func Parse(filename string) (*spec.ApiSpec, error) {
|
||||
if env.UseExperimental() {
|
||||
return apiParser.Parse(filename, "")
|
||||
|
||||
@@ -8,22 +8,23 @@ require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
github.com/emicklei/proto v1.13.2
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/go-sql-driver/mysql v1.8.0
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1
|
||||
github.com/zeromicro/antlr v0.0.1
|
||||
github.com/zeromicro/ddl-parser v1.0.5
|
||||
github.com/zeromicro/go-zero v1.6.2
|
||||
github.com/zeromicro/go-zero v1.6.3
|
||||
golang.org/x/text v0.14.0
|
||||
google.golang.org/grpc v1.61.1
|
||||
google.golang.org/protobuf v1.32.0
|
||||
google.golang.org/grpc v1.62.1
|
||||
google.golang.org/protobuf v1.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
|
||||
github.com/alicebob/miniredis/v2 v2.31.1 // indirect
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec // indirect
|
||||
@@ -52,7 +53,7 @@ require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.2 // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.4 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
@@ -93,23 +94,23 @@ require (
|
||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/oauth2 v0.14.0 // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/term v0.16.0 // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/oauth2 v0.16.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/term v0.17.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.29.1 // indirect
|
||||
k8s.io/apimachinery v0.29.1 // indirect
|
||||
k8s.io/client-go v0.29.1 // indirect
|
||||
k8s.io/api v0.29.2 // indirect
|
||||
k8s.io/apimachinery v0.29.2 // indirect
|
||||
k8s.io/client-go v0.29.2 // 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-20230726121419-3b25d923346b // indirect
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
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/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0=
|
||||
@@ -51,13 +53,13 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
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.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
|
||||
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
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/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
|
||||
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
@@ -90,8 +92,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-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.2 h1:iLlpgp4Cp/gC9Xuscl7lFL1PhhW+ZLtXZcrfCt4C3tA=
|
||||
github.com/jackc/pgx/v5 v5.5.2/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8=
|
||||
github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
@@ -155,15 +157,16 @@ 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 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
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/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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 h1:+dBg5k7nuTE38VVdoroRsT0Z88fmvdYrI2EjzJst35I=
|
||||
github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1/go.mod h1:nmuySobZb4kFgFy6BptpXp/BBw+xFSyvVPP6auoJB4k=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||
@@ -178,8 +181,8 @@ github.com/zeromicro/antlr v0.0.1 h1:CQpIn/dc0pUjgGQ81y98s/NGOm2Hfru2NNio2I9mQgk
|
||||
github.com/zeromicro/antlr v0.0.1/go.mod h1:nfpjEwFR6Q4xGDJMcZnCL9tEfQRgszMwu3rDz2Z+p5M=
|
||||
github.com/zeromicro/ddl-parser v1.0.5 h1:LaVqHdzMTjasua1yYpIYaksxKqRzFrEukj2Wi2EbWaQ=
|
||||
github.com/zeromicro/ddl-parser v1.0.5/go.mod h1:ISU/8NuPyEpl9pa17Py9TBPetMjtsiHrb9f5XGiYbo8=
|
||||
github.com/zeromicro/go-zero v1.6.2 h1:c1gXp6JTO0e+dtfwNZRE7OZgzjipfW8i1iBMoBnDwBI=
|
||||
github.com/zeromicro/go-zero v1.6.2/go.mod h1:mQKK/c/er/sbIAo7DWyFBZX8oa0eOkc7QJdG15b2GBw=
|
||||
github.com/zeromicro/go-zero v1.6.3 h1:OL0NnHD5LdRNDolfcK9vUkJt7K8TcBE3RkzfM8poOVw=
|
||||
github.com/zeromicro/go-zero v1.6.3/go.mod h1:XZL435ZxVi9MSXXtw2MRQhHgx6OoX3++MRMOE9xU70c=
|
||||
go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c=
|
||||
go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.12 h1:EYDL6pWwyOsylrQyLp2w+HkQ46ATiOvoEdMarindU2A=
|
||||
@@ -221,8 +224,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-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.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -234,17 +237,17 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
|
||||
golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -257,12 +260,12 @@ 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.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
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=
|
||||
@@ -284,18 +287,18 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
|
||||
google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
|
||||
google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
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=
|
||||
@@ -308,12 +311,12 @@ 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.29.1 h1:DAjwWX/9YT7NQD4INu49ROJuZAAAP/Ijki48GUPzxqw=
|
||||
k8s.io/api v0.29.1/go.mod h1:7Kl10vBRUXhnQQI8YR/R327zXC8eJ7887/+Ybta+RoQ=
|
||||
k8s.io/apimachinery v0.29.1 h1:KY4/E6km/wLBguvCZv8cKTeOwwOBqFNjwJIdMkMbbRc=
|
||||
k8s.io/apimachinery v0.29.1/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
|
||||
k8s.io/client-go v0.29.1 h1:19B/+2NGEwnFLzt0uB5kNJnfTsbV8w6TgQRz9l7ti7A=
|
||||
k8s.io/client-go v0.29.1/go.mod h1:TDG/psL9hdet0TI9mGyHJSgRkW3H9JZk2dNEUS7bRks=
|
||||
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
|
||||
k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
|
||||
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
|
||||
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
|
||||
k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
|
||||
k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
|
||||
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=
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
// BuildVersion is the version of goctl.
|
||||
const BuildVersion = "1.6.2"
|
||||
const BuildVersion = "1.6.3"
|
||||
|
||||
var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5}
|
||||
|
||||
|
||||
@@ -84,6 +84,20 @@ func (t *TokenNode) SetLeadingCommentGroup(cg CommentGroup) {
|
||||
t.LeadingCommentGroup = cg
|
||||
}
|
||||
|
||||
// RawText returns the node's raw text.
|
||||
func (t *TokenNode) RawText() string {
|
||||
text := t.Token.Text
|
||||
if strings.HasPrefix(text, "`") {
|
||||
text = strings.TrimPrefix(text, "`")
|
||||
text = strings.TrimSuffix(text, "`")
|
||||
} else if strings.HasPrefix(text, `"`) {
|
||||
text = strings.TrimPrefix(text, `"`)
|
||||
text = strings.TrimSuffix(text, `"`)
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
func (t *TokenNode) HasLeadingCommentGroup() bool {
|
||||
return t.LeadingCommentGroup.Valid() || t.leadingFlag
|
||||
}
|
||||
|
||||
@@ -108,6 +108,8 @@ func (a *Analyzer) astTypeToSpec(in ast.DataType) (spec.Type, error) {
|
||||
}
|
||||
|
||||
func (a *Analyzer) convert2Spec() error {
|
||||
a.fillInfo()
|
||||
|
||||
if err := a.fillTypes(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -128,7 +130,7 @@ func (a *Analyzer) convert2Spec() error {
|
||||
groups = append(groups, v)
|
||||
}
|
||||
sort.SliceStable(groups, func(i, j int) bool {
|
||||
return groups[i].Annotation.Properties["group"] < groups[j].Annotation.Properties["group"]
|
||||
return groups[i].Annotation.Properties[groupKeyText] < groups[j].Annotation.Properties[groupKeyText]
|
||||
})
|
||||
a.spec.Service.Groups = groups
|
||||
|
||||
@@ -150,8 +152,13 @@ func (a *Analyzer) convertKV(kv []*ast.KVExpr) map[string]string {
|
||||
var ret = map[string]string{}
|
||||
for _, v := range kv {
|
||||
key := strings.TrimSuffix(v.Key.Token.Text, ":")
|
||||
ret[key] = v.Value.Token.Text
|
||||
if key == summaryKeyText {
|
||||
ret[key] = v.Value.RawText()
|
||||
} else {
|
||||
ret[key] = v.Value.Token.Text
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -270,6 +277,27 @@ func (a *Analyzer) fillService() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Analyzer) fillInfo() {
|
||||
properties := make(map[string]string)
|
||||
if a.api.info != nil {
|
||||
for _, kv := range a.api.info.Values {
|
||||
key := kv.Key.Token.Text
|
||||
properties[strings.TrimSuffix(key, ":")] = kv.Value.RawText()
|
||||
}
|
||||
}
|
||||
a.spec.Info.Properties = properties
|
||||
infoKeyValue := make(map[string]string)
|
||||
for key, value := range properties {
|
||||
titleKey := strings.Title(strings.TrimSuffix(key, ":"))
|
||||
infoKeyValue[titleKey] = value
|
||||
}
|
||||
a.spec.Info.Title = infoKeyValue[infoTitleKey]
|
||||
a.spec.Info.Desc = infoKeyValue[infoDescKey]
|
||||
a.spec.Info.Version = infoKeyValue[infoVersionKey]
|
||||
a.spec.Info.Author = infoKeyValue[infoAuthorKey]
|
||||
a.spec.Info.Email = infoKeyValue[infoEmailKey]
|
||||
}
|
||||
|
||||
func (a *Analyzer) fillTypes() error {
|
||||
for _, item := range a.api.TypeStmt {
|
||||
switch v := (item).(type) {
|
||||
|
||||
@@ -9,14 +9,40 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/assertx"
|
||||
)
|
||||
|
||||
func Test_Parse(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
_, err := Parse("./testdata/example.api", nil)
|
||||
apiSpec, err := Parse("./testdata/example.api", nil)
|
||||
assert.Nil(t, err)
|
||||
ast := assert.New(t)
|
||||
ast.Equal(spec.Info{
|
||||
Title: "type title here",
|
||||
Desc: "type desc here",
|
||||
Version: "type version here",
|
||||
Author: "type author here",
|
||||
Email: "type email here",
|
||||
Properties: map[string]string{
|
||||
"title": "type title here",
|
||||
"desc": "type desc here",
|
||||
"version": "type version here",
|
||||
"author": "type author here",
|
||||
"email": "type email here",
|
||||
},
|
||||
}, apiSpec.Info)
|
||||
ast.True(func() bool {
|
||||
for _, group := range apiSpec.Service.Groups {
|
||||
value, ok := group.Annotation.Properties["summary"]
|
||||
if ok {
|
||||
return value == "test"
|
||||
}
|
||||
}
|
||||
return false
|
||||
}())
|
||||
})
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
data, err := os.ReadFile("./testdata/invalid.api")
|
||||
assert.NoError(t, err)
|
||||
@@ -38,12 +64,19 @@ func Test_Parse(t *testing.T) {
|
||||
assertx.Error(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("circleImport", func(t *testing.T) {
|
||||
_, err := Parse("./testdata/base.api", nil)
|
||||
assertx.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("link_import", func(t *testing.T) {
|
||||
_, err := Parse("./testdata/link_import.api", nil)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("duplicate_types", func(t *testing.T) {
|
||||
_, err := Parse("./testdata/duplicate_type.api", nil)
|
||||
assertx.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/ast"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/placeholder"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
|
||||
)
|
||||
|
||||
type filterBuilder struct {
|
||||
filename string
|
||||
m map[string]placeholder.Type
|
||||
m map[string]token.Position
|
||||
checkExprName string
|
||||
errorManager *errorManager
|
||||
}
|
||||
@@ -17,10 +17,10 @@ type filterBuilder struct {
|
||||
func (b *filterBuilder) check(nodes ...*ast.TokenNode) {
|
||||
for _, node := range nodes {
|
||||
fileNodeText := fmt.Sprintf("%s/%s", b.filename, node.Token.Text)
|
||||
if _, ok := b.m[fileNodeText]; ok {
|
||||
if pos, ok := b.m[fileNodeText]; ok && pos != node.Token.Position {
|
||||
b.errorManager.add(ast.DuplicateStmtError(node.Pos(), "duplicate "+b.checkExprName))
|
||||
} else {
|
||||
b.m[fileNodeText] = placeholder.PlaceHolder
|
||||
b.m[fileNodeText] = node.Token.Position
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,10 @@ func (b *filterBuilder) check(nodes ...*ast.TokenNode) {
|
||||
func (b *filterBuilder) checkNodeWithPrefix(prefix string, nodes ...*ast.TokenNode) {
|
||||
for _, node := range nodes {
|
||||
joinText := fmt.Sprintf("%s/%s", prefix, node.Token.Text)
|
||||
if _, ok := b.m[joinText]; ok {
|
||||
if pos, ok := b.m[joinText]; ok && pos != node.Token.Position {
|
||||
b.errorManager.add(ast.DuplicateStmtError(node.Pos(), "duplicate "+b.checkExprName))
|
||||
} else {
|
||||
b.m[joinText] = placeholder.PlaceHolder
|
||||
b.m[joinText] = node.Token.Position
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func newFilter() *filter {
|
||||
func (f *filter) addCheckItem(filename, checkExprName string) *filterBuilder {
|
||||
b := &filterBuilder{
|
||||
filename: filename,
|
||||
m: make(map[string]placeholder.Type),
|
||||
m: make(map[string]token.Position),
|
||||
checkExprName: checkExprName,
|
||||
errorManager: newErrorManager(),
|
||||
}
|
||||
|
||||
@@ -12,7 +12,17 @@ import (
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
|
||||
)
|
||||
|
||||
const idAPI = "api"
|
||||
const (
|
||||
idAPI = "api"
|
||||
summaryKeyExprText = "summary:"
|
||||
summaryKeyText = "summary"
|
||||
groupKeyText = "group"
|
||||
infoTitleKey = "Title"
|
||||
infoDescKey = "Desc"
|
||||
infoVersionKey = "Version"
|
||||
infoAuthorKey = "Author"
|
||||
infoEmailKey = "Email"
|
||||
)
|
||||
|
||||
// Parser is the parser for api file.
|
||||
type Parser struct {
|
||||
@@ -1134,7 +1144,7 @@ func (p *Parser) parseAtServerKVExpression() *ast.KVExpr {
|
||||
|
||||
var valueTok token.Token
|
||||
var leadingCommentGroup ast.CommentGroup
|
||||
if p.notExpectPeekToken(token.QUO, token.DURATION, token.IDENT, token.INT) {
|
||||
if p.notExpectPeekToken(token.QUO, token.DURATION, token.IDENT, token.INT, token.STRING) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1144,13 +1154,27 @@ func (p *Parser) parseAtServerKVExpression() *ast.KVExpr {
|
||||
}
|
||||
|
||||
slashTok := p.curTok
|
||||
var pathText = slashTok.Text
|
||||
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
||||
return nil
|
||||
}
|
||||
|
||||
idTok := p.curTok
|
||||
pathText += p.curTok.Text
|
||||
if p.peekTokenIs(token.SUB) { // parse abc-efg format
|
||||
if !p.nextToken() {
|
||||
return nil
|
||||
}
|
||||
|
||||
pathText += p.curTok.Text
|
||||
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
||||
return nil
|
||||
}
|
||||
|
||||
pathText += p.curTok.Text
|
||||
}
|
||||
|
||||
valueTok = token.Token{
|
||||
Text: slashTok.Text + idTok.Text,
|
||||
Text: pathText,
|
||||
Position: slashTok.Position,
|
||||
}
|
||||
leadingCommentGroup = p.curTokenNode().LeadingCommentGroup
|
||||
@@ -1170,6 +1194,23 @@ func (p *Parser) parseAtServerKVExpression() *ast.KVExpr {
|
||||
return nil
|
||||
}
|
||||
|
||||
valueTok = p.curTok
|
||||
leadingCommentGroup = p.curTokenNode().LeadingCommentGroup
|
||||
node := ast.NewTokenNode(valueTok)
|
||||
node.SetLeadingCommentGroup(leadingCommentGroup)
|
||||
expr.Value = node
|
||||
return expr
|
||||
} else if p.peekTokenIs(token.STRING) {
|
||||
if expr.Key.Token.Text != summaryKeyExprText {
|
||||
if p.notExpectPeekToken(token.QUO, token.DURATION, token.IDENT, token.INT) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if !p.nextToken() {
|
||||
return nil
|
||||
}
|
||||
|
||||
valueTok = p.curTok
|
||||
leadingCommentGroup = p.curTokenNode().LeadingCommentGroup
|
||||
node := ast.NewTokenNode(valueTok)
|
||||
@@ -1221,13 +1262,28 @@ func (p *Parser) parseAtServerKVExpression() *ast.KVExpr {
|
||||
}
|
||||
|
||||
slashTok := p.curTok
|
||||
var pathText = valueTok.Text
|
||||
pathText += slashTok.Text
|
||||
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
||||
return nil
|
||||
}
|
||||
|
||||
idTok := p.curTok
|
||||
pathText += p.curTok.Text
|
||||
if p.peekTokenIs(token.SUB) { // parse abc-efg format
|
||||
if !p.nextToken() {
|
||||
return nil
|
||||
}
|
||||
|
||||
pathText += p.curTok.Text
|
||||
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
||||
return nil
|
||||
}
|
||||
|
||||
pathText += p.curTok.Text
|
||||
}
|
||||
|
||||
valueTok = token.Token{
|
||||
Text: valueTok.Text + slashTok.Text + idTok.Text,
|
||||
Text: pathText,
|
||||
Position: valueTok.Position,
|
||||
}
|
||||
leadingCommentGroup = p.curTokenNode().LeadingCommentGroup
|
||||
|
||||
@@ -299,6 +299,11 @@ func TestParser_Parse_atServerStmt(t *testing.T) {
|
||||
"timeout6:": "10ns",
|
||||
"timeout7:": "1h10m10s10ms10µs10ns",
|
||||
"maxBytes:": `1024`,
|
||||
"prefix:": "/v1",
|
||||
"prefix1:": "/v1/v2_test/v2-beta",
|
||||
"prefix2:": "v1/v2_test/v2-beta",
|
||||
"prefix3:": "v1/v2_",
|
||||
"summary:": `"test"`,
|
||||
}
|
||||
|
||||
p := New("foo.api", atServerTestAPI)
|
||||
@@ -349,6 +354,8 @@ func TestParser_Parse_atServerStmt(t *testing.T) {
|
||||
`@server(foo:/v1/v2`,
|
||||
`@server(foo: m1,`,
|
||||
`@server(foo: m1,)`,
|
||||
`@server(foo: v1/v2-)`,
|
||||
`@server(foo:"test")`,
|
||||
}
|
||||
for _, v := range testData {
|
||||
p := New("foo.api", v)
|
||||
|
||||
@@ -13,4 +13,9 @@
|
||||
timeout6: 10ns
|
||||
timeout7: 1h10m10s10ms10µs10ns
|
||||
maxBytes: 1024
|
||||
)
|
||||
prefix: /v1
|
||||
prefix1: /v1/v2_test/v2-beta
|
||||
prefix2: v1/v2_test/v2-beta
|
||||
prefix3: v1/v2_
|
||||
summary:"test"
|
||||
)
|
||||
|
||||
8
tools/goctl/pkg/parser/api/parser/testdata/duplicate_type.api
vendored
Normal file
8
tools/goctl/pkg/parser/api/parser/testdata/duplicate_type.api
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
syntax = "v1"
|
||||
|
||||
type Example{
|
||||
A string
|
||||
}
|
||||
type Example{
|
||||
B string
|
||||
}
|
||||
@@ -188,3 +188,17 @@ service example {
|
||||
post /example/nest2 (NestDemoReq2) returns (NestDemoResp2)
|
||||
}
|
||||
|
||||
@server (
|
||||
group: /g1/g2_test/g2_beta
|
||||
prefix: /v1/v2_test/v2-beta
|
||||
summary: "test"
|
||||
)
|
||||
service example {
|
||||
@handler nestDemo1
|
||||
post /a/b_c/d-e/:f/123/g (NestDemoReq1) returns (NestDemoResp1)
|
||||
|
||||
@handler nestDemo2
|
||||
post /example/nest2 (NestDemoReq2) returns (NestDemoResp2)
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user