Compare commits

...

35 Commits

Author SHA1 Message Date
Kevin Wan
3315e60272 chore: performance tunning for stable runner (#4670) 2025-02-26 19:19:24 +08:00
dependabot[bot]
327ef73700 chore(deps): bump go.mongodb.org/mongo-driver from 1.17.2 to 1.17.3 (#4669) 2025-02-26 10:03:43 +08:00
dependabot[bot]
eb11521655 chore(deps): bump github.com/redis/go-redis/v9 from 9.7.0 to 9.7.1 (#4665) 2025-02-22 08:19:28 +08:00
Kevin Wan
4c37545e55 Update readme-cn.md (#4664) 2025-02-20 21:13:33 +08:00
dependabot[bot]
2f47c1fba4 chore(deps): bump github.com/prometheus/client_golang from 1.20.5 to 1.21.0 (#4663) 2025-02-20 11:14:13 +08:00
dependabot[bot]
16d54d0ace chore(deps): bump github.com/go-sql-driver/mysql from 1.8.1 to 1.9.0 in /tools/goctl (#4662)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-19 11:38:41 +08:00
dependabot[bot]
9925bcbf99 chore(deps): bump github.com/go-sql-driver/mysql from 1.8.1 to 1.9.0 (#4661)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-19 11:13:41 +08:00
dependabot[bot]
38a5ecb796 chore(deps): bump github.com/spf13/cobra from 1.8.1 to 1.9.1 in /tools/goctl (#4660)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-18 10:47:42 +08:00
Kevin Wan
af78fc7c5f chore: add more tests (#4656) 2025-02-14 23:43:34 +08:00
Kevin Wan
790302b486 fix: should not ignore slowThreshold (#4655) 2025-02-14 23:14:57 +08:00
Nanosk07
6a0672b801 fix: SlowThreshold configuration not taking effect (#4654) 2025-02-14 14:56:25 +00:00
Kevin Wan
560c61612c chore: refactor (#4652) 2025-02-14 09:41:54 +08:00
Kevin Wan
6a988dc4a9 fix: make test purpose method private (#4649) 2025-02-14 00:31:53 +08:00
Kevin Wan
15842c3c7a Create version-check.yml (#4646) 2025-02-13 19:39:22 +08:00
Rui Chen
f2914a74df fix: update version to match with the release (#4645) 2025-02-13 01:11:09 +00:00
Kevin Wan
f113d512e8 chore: coding style (#4644) 2025-02-12 23:48:39 +08:00
kesonan
7a4818da59 Generate caches that support custom key prefix. (#4643) 2025-02-12 15:31:30 +00:00
dependabot[bot]
48d0709ca6 chore(deps): bump golang.org/x/net from 0.34.0 to 0.35.0 (#4640) 2025-02-11 09:25:16 +08:00
Kevin Wan
f747585518 chore: simplify http query array parsing (#4637) 2025-02-09 01:00:52 +08:00
xuerbujia
507ff96546 feat add tag switch to disable form array of split comma format (#4633)
Co-authored-by: wuhongyu <readboy@DESKTOP-T8INU17>
2025-02-09 00:34:41 +08:00
Kevin Wan
651eabb4c6 chore: refactor gateway http context (#4636) 2025-02-08 21:18:07 +08:00
#Suyghur
e6b4372056 fix(gateway): fixed http gateway context propagation error (#4634) 2025-02-08 09:50:26 +00:00
Kevin Wan
24073969a1 fix: redis username not working in redis v7 (#4632) 2025-02-08 12:21:35 +08:00
Kevin Wan
ca797ed22c chore: add trailing newlines (#4631) 2025-02-07 23:39:02 +08:00
youzipi
e347d3f8f8 fix(goctl): allow duplicate_path_expression under different prefix (#4626) 2025-02-07 08:37:11 +00:00
dependabot[bot]
396393b336 chore(deps): bump google.golang.org/protobuf from 1.36.4 to 1.36.5 (#4627) 2025-02-07 10:05:22 +08:00
dependabot[bot]
1f0531b254 chore(deps): bump google.golang.org/protobuf from 1.36.4 to 1.36.5 in /tools/goctl (#4628) 2025-02-07 08:25:54 +08:00
ningzi
77fb271a06 feat(goctl): support go work (#4332) (#4344) 2025-02-05 14:22:40 +00:00
dependabot[bot]
af7cf79963 chore(deps): bump golang.org/x/time from 0.9.0 to 0.10.0 (#4621)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-05 22:00:54 +08:00
dependabot[bot]
7926d396d7 chore(deps): bump golang.org/x/text from 0.21.0 to 0.22.0 in /tools/goctl (#4620)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-05 10:16:06 +08:00
dependabot[bot]
080cd3df84 chore(deps): bump golang.org/x/sys from 0.29.0 to 0.30.0 (#4622) 2025-02-05 09:27:03 +08:00
Kevin Wan
c4e1a6a2d8 chore: refactor mapreduce (#4619) 2025-02-01 00:12:37 +08:00
Kevin Wan
4e71e95e44 chore: add comments (#4618) 2025-01-31 22:42:46 +08:00
JiChen
84db9bcd15 fix: global fields apply to Third-party log module (#4400) 2025-01-31 13:51:20 +00:00
dependabot[bot]
b28f79ac11 chore(deps): bump github.com/spf13/pflag from 1.0.5 to 1.0.6 in /tools/goctl (#4615) 2025-01-30 16:49:47 +08:00
45 changed files with 661 additions and 401 deletions

37
.github/workflows/version-check.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Release Version Check
on:
push:
tags:
- 'tools/goctl/v*'
workflow_dispatch:
jobs:
version-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Extract tag version
id: get_version
run: |
# Extract version from tools/goctl/v* format
echo "VERSION=${GITHUB_REF#refs/tags/tools/goctl/v}" >> $GITHUB_ENV
echo "Extracted version: $VERSION"
- name: Check version in goctl source code
run: |
# Change to goctl directory
cd tools/goctl
# Check version in BuildVersion constant
VERSION_IN_CODE=$(grep -r "const BuildVersion =" . | grep -o '".*"' | tr -d '"')
if [ "$VERSION_IN_CODE" != "$VERSION" ]; then
echo "Version mismatch: Version in code ($VERSION_IN_CODE) doesn't match tag version ($VERSION)"
exit 1
fi

View File

@@ -560,7 +560,7 @@ func shallLogStat() bool {
// 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.
func writeDebug(val any, fields ...LogField) {
getWriter().Debug(val, addCaller(fields...)...)
getWriter().Debug(val, mergeGlobalFields(addCaller(fields...))...)
}
// writeError writes v into the error log.
@@ -568,7 +568,7 @@ func writeDebug(val any, fields ...LogField) {
// 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.
func writeError(val any, fields ...LogField) {
getWriter().Error(val, addCaller(fields...)...)
getWriter().Error(val, mergeGlobalFields(addCaller(fields...))...)
}
// writeInfo writes v into info log.
@@ -576,7 +576,7 @@ func writeError(val any, fields ...LogField) {
// 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.
func writeInfo(val any, fields ...LogField) {
getWriter().Info(val, addCaller(fields...)...)
getWriter().Info(val, mergeGlobalFields(addCaller(fields...))...)
}
// writeSevere writes v into severe log.
@@ -592,7 +592,7 @@ func writeSevere(msg string) {
// 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.
func writeSlow(val any, fields ...LogField) {
getWriter().Slow(val, addCaller(fields...)...)
getWriter().Slow(val, mergeGlobalFields(addCaller(fields...))...)
}
// writeStack writes v into stack log.
@@ -608,5 +608,5 @@ func writeStack(msg string) {
// 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.
func writeStat(msg string) {
getWriter().Stat(msg, addCaller()...)
getWriter().Stat(msg, mergeGlobalFields(addCaller())...)
}

View File

@@ -206,7 +206,9 @@ func (l *richLogger) WithFields(fields ...LogField) Logger {
func (l *richLogger) buildFields(fields ...LogField) []LogField {
fields = append(l.fields, fields...)
// caller field should always appear together with global fields
fields = append(fields, Field(callerKey, getCaller(callerDepth+l.callerSkip)))
fields = mergeGlobalFields(fields)
if l.ctx == nil {
return fields

View File

@@ -17,15 +17,27 @@ import (
)
type (
// Writer is the interface for writing logs.
// It's designed to let users customize their own log writer,
// such as writing logs to a kafka, a database, or using third-party loggers.
Writer interface {
// Alert sends an alert message, if your writer implemented alerting functionality.
Alert(v any)
// Close closes the writer.
Close() error
// Debug logs a message at debug level.
Debug(v any, fields ...LogField)
// Error logs a message at error level.
Error(v any, fields ...LogField)
// Info logs a message at info level.
Info(v any, fields ...LogField)
// Severe logs a message at severe level.
Severe(v any)
// Slow logs a message at slow level.
Slow(v any, fields ...LogField)
// Stack logs a message at error level.
Stack(v any)
// Stat logs a message at stat level.
Stat(v any, fields ...LogField)
}
@@ -324,20 +336,6 @@ func buildPlainFields(fields logEntry) []string {
return items
}
func combineGlobalFields(fields []LogField) []LogField {
globals := globalFields.Load()
if globals == nil {
return fields
}
gf := globals.([]LogField)
ret := make([]LogField, 0, len(gf)+len(fields))
ret = append(ret, gf...)
ret = append(ret, fields...)
return ret
}
func marshalJson(t interface{}) ([]byte, error) {
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
@@ -352,6 +350,20 @@ func marshalJson(t interface{}) ([]byte, error) {
return buf.Bytes(), err
}
func mergeGlobalFields(fields []LogField) []LogField {
globals := globalFields.Load()
if globals == nil {
return fields
}
gf := globals.([]LogField)
ret := make([]LogField, 0, len(gf)+len(fields))
ret = append(ret, gf...)
ret = append(ret, fields...)
return ret
}
func output(writer io.Writer, level string, val any, fields ...LogField) {
// only truncate string content, don't know how to truncate the values of other types.
if v, ok := val.(string); ok {
@@ -362,7 +374,6 @@ func output(writer io.Writer, level string, val any, fields ...LogField) {
}
}
fields = combineGlobalFields(fields)
// +3 for timestamp, level and content
entry := make(logEntry, len(fields)+3)
for _, field := range fields {

View File

@@ -19,7 +19,6 @@ import (
)
const (
comma = ","
defaultKeyName = "key"
delimiter = '.'
ignoreKey = "-"
@@ -38,7 +37,6 @@ var (
defaultCacheLock sync.Mutex
emptyMap = map[string]any{}
emptyValue = reflect.ValueOf(lang.Placeholder)
stringSliceType = reflect.TypeOf([]string{})
)
type (
@@ -152,10 +150,6 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value,
return nil
}
if u.opts.fromArray {
refValue = makeStringSlice(refValue)
}
var valid bool
conv := reflect.MakeSlice(reflect.SliceOf(baseType), refValue.Len(), refValue.Cap())
@@ -1189,35 +1183,6 @@ func join(elem ...string) string {
return builder.String()
}
func makeStringSlice(refValue reflect.Value) reflect.Value {
if refValue.Len() != 1 {
return refValue
}
element := refValue.Index(0)
if element.Kind() != reflect.String {
return refValue
}
val, ok := element.Interface().(string)
if !ok {
return refValue
}
splits := strings.Split(val, comma)
if len(splits) <= 1 {
return refValue
}
slice := reflect.MakeSlice(stringSliceType, len(splits), len(splits))
for i, split := range splits {
// allow empty strings
slice.Index(i).Set(reflect.ValueOf(split))
}
return slice
}
func newInitError(name string) error {
return fmt.Errorf("field %q is not set", name)
}

View File

@@ -1462,9 +1462,7 @@ func TestUnmarshalIntSlice(t *testing.T) {
ast := assert.New(t)
unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray())
if ast.NoError(unmarshaler.Unmarshal(m, &v)) {
ast.ElementsMatch([]int{1, 2}, v.Ages)
}
ast.Error(unmarshaler.Unmarshal(m, &v))
})
}
@@ -1546,7 +1544,22 @@ func TestUnmarshalStringSliceFromString(t *testing.T) {
ast := assert.New(t)
unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray())
if ast.NoError(unmarshaler.Unmarshal(m, &v)) {
ast.ElementsMatch([]string{"", ""}, v.Names)
ast.ElementsMatch([]string{","}, v.Names)
}
})
t.Run("slice from valid strings with comma", func(t *testing.T) {
var v struct {
Names []string `key:"names"`
}
m := map[string]any{
"names": []string{"aa,bb"},
}
ast := assert.New(t)
unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray())
if ast.NoError(unmarshaler.Unmarshal(m, &v)) {
ast.ElementsMatch([]string{"aa,bb"}, v.Names)
}
})

View File

@@ -142,89 +142,6 @@ func MapReduceChan[T, U, V any](source <-chan T, mapper MapperFunc[T, U], reduce
return mapReduceWithPanicChan(source, panicChan, mapper, reducer, opts...)
}
// mapReduceWithPanicChan maps all elements from source, and reduce the output elements with given reducer.
func mapReduceWithPanicChan[T, U, V any](source <-chan T, panicChan *onceChan, mapper MapperFunc[T, U],
reducer ReducerFunc[U, V], opts ...Option) (val V, err error) {
options := buildOptions(opts...)
// output is used to write the final result
output := make(chan V)
defer func() {
// reducer can only write once, if more, panic
for range output {
panic("more than one element written in reducer")
}
}()
// collector is used to collect data from mapper, and consume in reducer
collector := make(chan U, options.workers)
// if done is closed, all mappers and reducer should stop processing
done := make(chan struct{})
writer := newGuardedWriter(options.ctx, output, done)
var closeOnce sync.Once
// use atomic type to avoid data race
var retErr errorx.AtomicError
finish := func() {
closeOnce.Do(func() {
close(done)
close(output)
})
}
cancel := once(func(err error) {
if err != nil {
retErr.Set(err)
} else {
retErr.Set(ErrCancelWithNil)
}
drain(source)
finish()
})
go func() {
defer func() {
drain(collector)
if r := recover(); r != nil {
panicChan.write(r)
}
finish()
}()
reducer(collector, writer, cancel)
}()
go executeMappers(mapperContext[T, U]{
ctx: options.ctx,
mapper: func(item T, w Writer[U]) {
mapper(item, w, cancel)
},
source: source,
panicChan: panicChan,
collector: collector,
doneChan: done,
workers: options.workers,
})
select {
case <-options.ctx.Done():
cancel(context.DeadlineExceeded)
err = context.DeadlineExceeded
case v := <-panicChan.channel:
// drain output here, otherwise for loop panic in defer
drain(output)
panic(v)
case v, ok := <-output:
if e := retErr.Load(); e != nil {
err = e
} else if ok {
val = v
} else {
err = ErrReduceNoOutput
}
}
return
}
// MapReduceVoid maps all elements generated from given generate,
// and reduce the output elements with given reducer.
func MapReduceVoid[T, U any](generate GenerateFunc[T], mapper MapperFunc[T, U],
@@ -330,6 +247,89 @@ func executeMappers[T, U any](mCtx mapperContext[T, U]) {
}
}
// mapReduceWithPanicChan maps all elements from source, and reduce the output elements with given reducer.
func mapReduceWithPanicChan[T, U, V any](source <-chan T, panicChan *onceChan, mapper MapperFunc[T, U],
reducer ReducerFunc[U, V], opts ...Option) (val V, err error) {
options := buildOptions(opts...)
// output is used to write the final result
output := make(chan V)
defer func() {
// reducer can only write once, if more, panic
for range output {
panic("more than one element written in reducer")
}
}()
// collector is used to collect data from mapper, and consume in reducer
collector := make(chan U, options.workers)
// if done is closed, all mappers and reducer should stop processing
done := make(chan struct{})
writer := newGuardedWriter(options.ctx, output, done)
var closeOnce sync.Once
// use atomic type to avoid data race
var retErr errorx.AtomicError
finish := func() {
closeOnce.Do(func() {
close(done)
close(output)
})
}
cancel := once(func(err error) {
if err != nil {
retErr.Set(err)
} else {
retErr.Set(ErrCancelWithNil)
}
drain(source)
finish()
})
go func() {
defer func() {
drain(collector)
if r := recover(); r != nil {
panicChan.write(r)
}
finish()
}()
reducer(collector, writer, cancel)
}()
go executeMappers(mapperContext[T, U]{
ctx: options.ctx,
mapper: func(item T, w Writer[U]) {
mapper(item, w, cancel)
},
source: source,
panicChan: panicChan,
collector: collector,
doneChan: done,
workers: options.workers,
})
select {
case <-options.ctx.Done():
cancel(context.DeadlineExceeded)
err = context.DeadlineExceeded
case v := <-panicChan.channel:
// drain output here, otherwise for loop panic in defer
drain(output)
panic(v)
case v, ok := <-output:
if e := retErr.Load(); e != nil {
err = e
} else if ok {
val = v
} else {
err = ErrReduceNoOutput
}
}
return
}
func newOptions() *mapReduceOptions {
return &mapReduceOptions{
ctx: context.Background(),

View File

@@ -21,6 +21,7 @@ func CreateBlockingNode(r *Redis) (ClosableNode, error) {
case NodeType:
client := red.NewClient(&red.Options{
Addr: r.Addr,
Username: r.User,
Password: r.Pass,
DB: defaultDatabase,
MaxRetries: maxRetries,
@@ -32,6 +33,7 @@ func CreateBlockingNode(r *Redis) (ClosableNode, error) {
case ClusterType:
client := red.NewClusterClient(&red.ClusterOptions{
Addrs: splitClusterAddrs(r.Addr),
Username: r.User,
Password: r.Pass,
MaxRetries: maxRetries,
PoolSize: 1,

View File

@@ -31,6 +31,7 @@ func getClient(r *Redis) (*red.Client, error) {
}
store := red.NewClient(&red.Options{
Addr: r.Addr,
Username: r.User,
Password: r.Pass,
DB: defaultDatabase,
MaxRetries: maxRetries,

View File

@@ -28,6 +28,7 @@ func getCluster(r *Redis) (*red.ClusterClient, error) {
}
store := red.NewClusterClient(&red.ClusterOptions{
Addrs: splitClusterAddrs(r.Addr),
Username: r.User,
Password: r.Pass,
MaxRetries: maxRetries,
MinIdleConns: idleConns,

View File

@@ -5,6 +5,7 @@ import (
"runtime"
"sync"
"sync/atomic"
"time"
)
const factor = 10
@@ -100,6 +101,6 @@ func (r *StableRunner[I, O]) Wait() {
close(r.done)
r.runner.Wait()
for atomic.LoadUint64(&r.consumedIndex) < atomic.LoadUint64(&r.writtenIndex) {
runtime.Gosched()
time.Sleep(time.Millisecond)
}
}

View File

@@ -185,6 +185,8 @@ func (s *Server) buildHttpHandler(target *HttpClientConf) http.HandlerFunc {
return
}
// set the timeout if it's configured, take effect only if it's greater than 0
// and less than the deadline of the original request
if target.Timeout > 0 {
timeout := time.Duration(target.Timeout) * time.Millisecond
ctx, cancel := context.WithTimeout(r.Context(), timeout)
@@ -276,7 +278,7 @@ func buildRequestWithNewTarget(r *http.Request, target *HttpClientConf) (*http.R
}
}
return &http.Request{
newReq := &http.Request{
Method: r.Method,
URL: &u,
Header: r.Header.Clone(),
@@ -285,7 +287,10 @@ func buildRequestWithNewTarget(r *http.Request, target *HttpClientConf) (*http.R
ProtoMinor: r.ProtoMinor,
ContentLength: r.ContentLength,
Body: io.NopCloser(r.Body),
}, nil
}
// make sure the context is passed to the new request
return newReq.WithContext(r.Context()), nil
}
func createDescriptorSource(cli zrpc.Client, up Upstream) (grpcurl.DescriptorSource, error) {

30
go.mod
View File

@@ -7,7 +7,7 @@ require (
github.com/alicebob/miniredis/v2 v2.34.0
github.com/fatih/color v1.18.0
github.com/fullstorydev/grpcurl v1.9.2
github.com/go-sql-driver/mysql v1.8.1
github.com/go-sql-driver/mysql v1.9.0
github.com/golang-jwt/jwt/v4 v4.5.1
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.4
@@ -16,13 +16,13 @@ require (
github.com/jhump/protoreflect v1.17.0
github.com/olekukonko/tablewriter v0.0.5
github.com/pelletier/go-toml/v2 v2.2.2
github.com/prometheus/client_golang v1.20.5
github.com/redis/go-redis/v9 v9.7.0
github.com/prometheus/client_golang v1.21.0
github.com/redis/go-redis/v9 v9.7.1
github.com/spaolacci/murmur3 v1.1.0
github.com/stretchr/testify v1.10.0
go.etcd.io/etcd/api/v3 v3.5.15
go.etcd.io/etcd/client/v3 v3.5.15
go.mongodb.org/mongo-driver v1.17.2
go.mongodb.org/mongo-driver v1.17.3
go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
@@ -33,12 +33,12 @@ require (
go.opentelemetry.io/otel/trace v1.24.0
go.uber.org/automaxprocs v1.6.0
go.uber.org/goleak v1.3.0
golang.org/x/net v0.34.0
golang.org/x/sys v0.29.0
golang.org/x/time v0.9.0
golang.org/x/net v0.35.0
golang.org/x/sys v0.30.0
golang.org/x/time v0.10.0
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.36.4
google.golang.org/protobuf v1.36.5
gopkg.in/cheggaaa/pb.v1 v1.0.28
gopkg.in/h2non/gock.v1 v1.1.2
gopkg.in/yaml.v2 v2.4.0
@@ -80,7 +80,7 @@ require (
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@@ -93,7 +93,7 @@ require (
github.com/openzipkin/zipkin-go v0.4.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
@@ -109,11 +109,11 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

60
go.sum
View File

@@ -55,8 +55,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.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo=
github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw=
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=
@@ -103,8 +103,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -151,16 +151,16 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/redis/go-redis/v9 v9.7.1 h1:4LhKRCIduqXqtvCUlaq9c8bdHOkICjDMrr1+Zb3osAc=
github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
@@ -203,8 +203,8 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5
go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
go.mongodb.org/mongo-driver v1.17.2 h1:gvZyk8352qSfzyZ2UMWcpDpMSGEr1eqE4T793SqyhzM=
go.mongodb.org/mongo-driver v1.17.2/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
@@ -241,8 +241,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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/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=
@@ -254,17 +254,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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -276,20 +276,20 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.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.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@@ -308,8 +308,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@@ -301,6 +301,7 @@ go-zero 已被许多公司用于生产部署,接入场景如在线教育、电
>102. 深圳市兴海物联科技有限公司
>103. 爱芯元智半导体股份有限公司
>104. 杭州升恒科技有限公司
>105. 昆仑万维科技股份有限公司
如果贵公司也已使用 go-zero欢迎在 [登记地址](https://github.com/zeromicro/go-zero/issues/602) 登记,仅仅为了推广,不做其它用途。

View File

@@ -160,7 +160,7 @@ func TestParseFormArray(t *testing.T) {
http.NoBody)
assert.NoError(t, err)
if assert.NoError(t, Parse(r, &v)) {
assert.ElementsMatch(t, []string{"1", "2", "3"}, v.Names)
assert.ElementsMatch(t, []string{"1,2,3"}, v.Names)
}
})
@@ -189,9 +189,7 @@ func TestParseFormArray(t *testing.T) {
"/a?numbers=1,2,3",
http.NoBody)
assert.NoError(t, err)
if assert.NoError(t, Parse(r, &v)) {
assert.ElementsMatch(t, []int{1, 2, 3}, v.Numbers)
}
assert.Error(t, Parse(r, &v))
})
t.Run("slice with one value on array format brackets", func(t *testing.T) {
@@ -268,6 +266,36 @@ func TestParseFormArray(t *testing.T) {
assert.ElementsMatch(t, []float64{2}, v.Numbers)
}
})
t.Run("slice with one value", func(t *testing.T) {
var v struct {
Codes []string `form:"codes"`
}
r, err := http.NewRequest(
http.MethodGet,
"/a?codes=aaa,bbb,ccc",
http.NoBody)
assert.NoError(t, err)
if assert.NoError(t, Parse(r, &v)) {
assert.ElementsMatch(t, []string{"aaa,bbb,ccc"}, v.Codes)
}
})
t.Run("slice with multiple values", func(t *testing.T) {
var v struct {
Codes []string `form:"codes,arrayComma=false"`
}
r, err := http.NewRequest(
http.MethodGet,
"/a?codes=aaa,bbb,ccc&codes=ccc,ddd,eee",
http.NoBody)
assert.NoError(t, err)
if assert.NoError(t, Parse(r, &v)) {
assert.ElementsMatch(t, []string{"aaa,bbb,ccc", "ccc,ddd,eee"}, v.Codes)
}
})
}
func TestParseForm_Error(t *testing.T) {

View File

@@ -95,25 +95,6 @@ func (s *Server) Routes() []Route {
return routes
}
// ServeHTTP is for test purpose, allow developer to do a unit test with
// all defined router without starting an HTTP Server.
//
// For example:
//
// server := MustNewServer(...)
// server.addRoute(...) // router a
// server.addRoute(...) // router b
// server.addRoute(...) // router c
//
// r, _ := http.NewRequest(...)
// w := httptest.NewRecorder(...)
// server.ServeHTTP(w, r)
// // verify the response
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.ngin.bindRoutes(s.router)
s.router.ServeHTTP(w, r)
}
// Start starts the Server.
// Graceful shutdown is enabled by default.
// Use proc.SetTimeToForceQuit to customize the graceful shutdown period.

View File

@@ -231,7 +231,7 @@ func TestWithFileServerMiddleware(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, tt.requestPath, nil)
rr := httptest.NewRecorder()
server.ServeHTTP(rr, req)
serve(server, rr, req)
assert.Equal(t, tt.expectedStatus, rr.Code)
if len(tt.expectedContent) > 0 {
@@ -458,7 +458,7 @@ Port: 54321
// we would need to verify the behavior here. Since we don't have
// direct access to headers, we'll mock newCorsRouter to capture it.
w := httptest.NewRecorder()
svr.ServeHTTP(w, httptest.NewRequest(http.MethodOptions, "/", nil))
serve(svr, w, httptest.NewRequest(http.MethodOptions, "/", nil))
vals := w.Header().Values("Access-Control-Allow-Headers")
respHeaders := make(map[string]struct{})
@@ -748,7 +748,7 @@ Port: 54321
t.Run(test.name, func(t *testing.T) {
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", test.path, nil)
svr.ServeHTTP(w, req)
serve(svr, w, req)
assert.Equal(t, test.code, w.Code)
})
}
@@ -765,6 +765,25 @@ func TestServerEmbedFileSystem(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, "/assets/sample.txt", http.NoBody)
assert.Nil(t, err)
rr := httptest.NewRecorder()
server.ServeHTTP(rr, req)
serve(server, rr, req)
assert.Equal(t, sampleContent, rr.Body.String())
}
// serve is for test purpose, allow developer to do a unit test with
// all defined router without starting an HTTP Server.
//
// For example:
//
// server := MustNewServer(...)
// server.addRoute(...) // router a
// server.addRoute(...) // router b
// server.addRoute(...) // router c
//
// r, _ := http.NewRequest(...)
// w := httptest.NewRecorder(...)
// serve(server, w, r)
// // verify the response
func serve(s *Server, w http.ResponseWriter, r *http.Request) {
s.ngin.bindRoutes(s.router)
s.router.ServeHTTP(w, r)
}

View File

@@ -10,10 +10,10 @@ const (
import 'package:shared_preferences/shared_preferences.dart';
import '../data/tokens.dart';
/// 保存tokens到本地
/// store tokens to local
///
/// 传入null则删除本地tokens
/// 返回true设置成功 false:设置失败
/// pass null will clean local stored tokens
/// returns true if success, otherwise false
Future<bool> setTokens(Tokens tokens) async {
var sp = await SharedPreferences.getInstance();
if (tokens == null) {
@@ -23,9 +23,9 @@ Future<bool> setTokens(Tokens tokens) async {
return await sp.setString('tokens', jsonEncode(tokens.toJson()));
}
/// 获取本地存储的tokens
/// get local stored tokens
///
/// 如果没有,则返回null
/// if no, returns null
Future<Tokens> getTokens() async {
try {
var sp = await SharedPreferences.getInstance();
@@ -82,7 +82,8 @@ func genVars(dir string, isLegacy bool, scheme string, hostname string) error {
}
if !fileExists(dir + "vars.dart") {
err = os.WriteFile(dir+"vars.dart", []byte(fmt.Sprintf(`const serverHost='%s://%s';`, scheme, hostname)), 0o644)
err = os.WriteFile(dir+"vars.dart", []byte(fmt.Sprintf(`const serverHost='%s://%s';`,
scheme, hostname)), 0o644)
if err != nil {
return err
}

View File

@@ -59,11 +59,11 @@ type (
// Member describes the field of a structure
Member struct {
Name string
// 数据类型字面值,如:string、map[int]string、[]int64、[]*User
// data type, for example, string、map[int]string、[]int64、[]*User
Type Type
Tag string
Comment string
// 成员头顶注释说明
// document for the field
Docs Doc
IsInline bool
}

View File

@@ -6,19 +6,19 @@ require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/emicklei/proto v1.14.0
github.com/fatih/structtag v1.2.0
github.com/go-sql-driver/mysql v1.8.1
github.com/go-sql-driver/mysql v1.9.0
github.com/gookit/color v1.5.4
github.com/iancoleman/strcase v0.3.0
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
github.com/stretchr/testify v1.10.0
github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1
github.com/zeromicro/antlr v0.0.1
github.com/zeromicro/ddl-parser v1.0.5
github.com/zeromicro/go-zero v1.8.0
golang.org/x/text v0.21.0
golang.org/x/text v0.22.0
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.36.4
google.golang.org/protobuf v1.36.5
gopkg.in/yaml.v2 v2.4.0
)
@@ -96,7 +96,7 @@ require (
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/time v0.9.0 // indirect

View File

@@ -24,7 +24,7 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -52,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.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo=
github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw=
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=
@@ -156,10 +156,10 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
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/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -248,8 +248,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -265,8 +265,8 @@ golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -286,8 +286,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d h1:
google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@@ -151,6 +151,7 @@
"short": "Generate mysql model",
"strict": "Generate model in strict mode",
"ignore-columns": "Ignore columns while creating or updating rows",
"prefix": "The cache prefix, effective when --cache is true",
"datasource": {
"short": "Generate model from datasource",
"url": "The data source of database,like \"root:password@tcp(127.0.0.1:3306)/database",

View File

@@ -6,7 +6,7 @@ import (
)
// BuildVersion is the version of goctl.
const BuildVersion = "1.7.6"
const BuildVersion = "1.7.7"
var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5}

View File

@@ -71,6 +71,7 @@ func init() {
mysqlCmd.PersistentFlags().BoolVar(&command.VarBoolStrict, "strict")
mysqlCmd.PersistentFlags().StringSliceVarPWithDefaultValue(&command.VarStringSliceIgnoreColumns,
"ignore-columns", "i", []string{"create_at", "created_at", "create_time", "update_at", "updated_at", "update_time"})
mysqlCmd.PersistentFlags().StringVarPWithDefaultValue(&command.VarStringCachePrefix, "prefix", "p", "cache")
mysqlCmd.AddCommand(datasourceCmd, ddlCmd)
pgCmd.AddCommand(pgDatasourceCmd)

View File

@@ -50,6 +50,8 @@ var (
VarBoolStrict bool
// VarStringSliceIgnoreColumns represents the columns which are ignored.
VarStringSliceIgnoreColumns []string
// VarStringCachePrefix describes the prefix of cache.
VarStringCachePrefix string
)
var errNotMatched = errors.New("sql not matched")
@@ -57,6 +59,9 @@ var errNotMatched = errors.New("sql not matched")
// MysqlDDL generates model code from ddl
func MysqlDDL(_ *cobra.Command, _ []string) error {
migrationnotes.BeforeCommands(VarStringDir, VarStringStyle)
if VarBoolCache && len(VarStringCachePrefix) == 0 {
return errors.New("cache prefix is empty")
}
src := VarStringSrc
dir := VarStringDir
cache := VarBoolCache
@@ -89,6 +94,7 @@ func MysqlDDL(_ *cobra.Command, _ []string) error {
database: database,
strict: VarBoolStrict,
ignoreColumns: mergeColumns(VarStringSliceIgnoreColumns),
prefix: VarStringCachePrefix,
}
return fromDDL(arg)
}
@@ -96,6 +102,9 @@ func MysqlDDL(_ *cobra.Command, _ []string) error {
// MySqlDataSource generates model code from datasource
func MySqlDataSource(_ *cobra.Command, _ []string) error {
migrationnotes.BeforeCommands(VarStringDir, VarStringStyle)
if VarBoolCache && len(VarStringCachePrefix) == 0 {
return errors.New("cache prefix is empty")
}
url := strings.TrimSpace(VarStringURL)
dir := strings.TrimSpace(VarStringDir)
cache := VarBoolCache
@@ -130,6 +139,7 @@ func MySqlDataSource(_ *cobra.Command, _ []string) error {
idea: idea,
strict: VarBoolStrict,
ignoreColumns: mergeColumns(VarStringSliceIgnoreColumns),
prefix: VarStringCachePrefix,
}
return fromMysqlDataSource(arg)
}
@@ -225,6 +235,7 @@ type ddlArg struct {
database string
strict bool
ignoreColumns []string
prefix string
}
func fromDDL(arg ddlArg) error {
@@ -243,7 +254,7 @@ func fromDDL(arg ddlArg) error {
return errNotMatched
}
generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg,
generator, err := gen.NewDefaultGenerator(arg.prefix, arg.dir, arg.cfg,
gen.WithConsoleOption(log), gen.WithIgnoreColumns(arg.ignoreColumns))
if err != nil {
return err
@@ -266,6 +277,7 @@ type dataSourceArg struct {
cache, idea bool
strict bool
ignoreColumns []string
prefix string
}
func fromMysqlDataSource(arg dataSourceArg) error {
@@ -318,7 +330,7 @@ func fromMysqlDataSource(arg dataSourceArg) error {
return errors.New("no tables matched")
}
generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg,
generator, err := gen.NewDefaultGenerator(arg.prefix, arg.dir, arg.cfg,
gen.WithConsoleOption(log), gen.WithIgnoreColumns(arg.ignoreColumns))
if err != nil {
return err
@@ -369,7 +381,7 @@ func fromPostgreSqlDataSource(url string, pattern pattern, dir, schema string, c
return errors.New("no tables matched")
}
generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log), gen.WithPostgreSql(), gen.WithIgnoreColumns(ignoreColumns))
generator, err := gen.NewDefaultGenerator("", dir, cfg, gen.WithConsoleOption(log), gen.WithPostgreSql(), gen.WithIgnoreColumns(ignoreColumns))
if err != nil {
return err
}

View File

@@ -3,15 +3,15 @@
# generate model with cache from ddl
fromDDLWithCache:
goctl template clean
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/cache" -cache
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/cache" -cache --prefix gozero
fromDDLWithCacheAndIgnoreColumns:
goctl template clean
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/ignore_columns/cache" -cache -i 'gmt_create,create_at' -i 'gmt_modified,update_at'
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/ignore_columns/cache" -cache -i 'gmt_create,create_at' -i 'gmt_modified,update_at' --prefix gozero
fromDDLWithCacheAndDb:
goctl template clean
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/cache_db" -database="1gozero" -cache
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/cache_db" -database="1gozero" -cache --prefix gozero
fromDDLWithoutCache:
goctl template clean;
@@ -19,11 +19,11 @@ fromDDLWithoutCache:
# generate model with cache from data source
user=root
password=password
datasource=127.0.0.1:3306
database=gozero
user=app_user
password=PLO75FbcfmFYRuQEGmygZ9PyQCQbmgeD5
datasource=k8s-istiosys-unifydev-2cdcebd306-b64e09be84220820.elb.ap-southeast-1.amazonaws.com:3306
database=db_fiat
fromDataSource:
goctl template clean
goctl model mysql datasource -url="$(user):$(password)@tcp($(datasource))/$(database)" -table="*" -dir ./model/cache -c -style gozero
goctl model mysql datasource -url="$(user):$(password)@tcp($(datasource))/$(database)" -table="biz_switch" -dir ./model/cache -c -style gozero --prefix gozero

View File

@@ -30,6 +30,7 @@ type (
cfg *config.Config
isPostgreSql bool
ignoreColumns []string
prefix string
}
// Option defines a function with argument defaultGenerator
@@ -56,7 +57,7 @@ type (
)
// NewDefaultGenerator creates an instance for defaultGenerator
func NewDefaultGenerator(dir string, cfg *config.Config, opt ...Option) (*defaultGenerator, error) {
func NewDefaultGenerator(prefix, dir string, cfg *config.Config, opt ...Option) (*defaultGenerator, error) {
if dir == "" {
dir = pwd
}
@@ -72,7 +73,7 @@ func NewDefaultGenerator(dir string, cfg *config.Config, opt ...Option) (*defaul
return nil, err
}
generator := &defaultGenerator{dir: dir, cfg: cfg, pkg: pkg}
generator := &defaultGenerator{dir: dir, cfg: cfg, pkg: pkg, prefix: prefix}
var optionList []Option
optionList = append(optionList, newDefaultOption())
optionList = append(optionList, opt...)
@@ -260,7 +261,7 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
return "", fmt.Errorf("table %s: missing primary key", in.Name.Source())
}
primaryKey, uniqueKey := genCacheKeys(in)
primaryKey, uniqueKey := genCacheKeys(g.prefix, in)
var table Table
table.Table = in

View File

@@ -34,7 +34,7 @@ func TestCacheModel(t *testing.T) {
dir := filepath.Join(pathx.MustTempDir(), "./testmodel")
cacheDir := filepath.Join(dir, "cache")
noCacheDir := filepath.Join(dir, "nocache")
g, err := NewDefaultGenerator(cacheDir, &config.Config{
g, err := NewDefaultGenerator("cache", cacheDir, &config.Config{
NamingFormat: "GoZero",
})
assert.Nil(t, err)
@@ -45,7 +45,7 @@ func TestCacheModel(t *testing.T) {
_, err := os.Stat(filepath.Join(cacheDir, "TestUserModel.go"))
return err == nil
}())
g, err = NewDefaultGenerator(noCacheDir, &config.Config{
g, err = NewDefaultGenerator("cache", noCacheDir, &config.Config{
NamingFormat: "gozero",
})
assert.Nil(t, err)
@@ -72,7 +72,7 @@ func TestNamingModel(t *testing.T) {
defer func() {
_ = os.RemoveAll(dir)
}()
g, err := NewDefaultGenerator(camelDir, &config.Config{
g, err := NewDefaultGenerator("cache", camelDir, &config.Config{
NamingFormat: "GoZero",
})
assert.Nil(t, err)
@@ -83,7 +83,7 @@ func TestNamingModel(t *testing.T) {
_, err := os.Stat(filepath.Join(camelDir, "TestUserModel.go"))
return err == nil
}())
g, err = NewDefaultGenerator(snakeDir, &config.Config{
g, err = NewDefaultGenerator("cache", snakeDir, &config.Config{
NamingFormat: "go_zero",
})
assert.Nil(t, err)
@@ -110,7 +110,7 @@ func TestFolderName(t *testing.T) {
defer func() {
_ = os.RemoveAll(dir)
}()
g, err := NewDefaultGenerator(camelDir, &config.Config{
g, err := NewDefaultGenerator("cache", camelDir, &config.Config{
NamingFormat: "GoZero",
})
assert.Nil(t, err)
@@ -125,7 +125,7 @@ func TestFolderName(t *testing.T) {
}())
assert.Equal(t, pkg, g.pkg)
g, err = NewDefaultGenerator(snakeDir, &config.Config{
g, err = NewDefaultGenerator("cache", snakeDir, &config.Config{
NamingFormat: "go_zero",
})
assert.Nil(t, err)
@@ -180,7 +180,7 @@ func Test_genPublicModel(t *testing.T) {
err = os.WriteFile(modelFilename, []byte(source), 0o777)
require.NoError(t, err)
g, err := NewDefaultGenerator(modelDir, &config.Config{
g, err := NewDefaultGenerator("cache", modelDir, &config.Config{
NamingFormat: config.DefaultFormat,
})
require.NoError(t, err)

View File

@@ -37,12 +37,12 @@ type Key struct {
// Join describes an alias of string slice
type Join []string
func genCacheKeys(table parser.Table) (Key, []Key) {
func genCacheKeys(prefix string, table parser.Table) (Key, []Key) {
var primaryKey Key
var uniqueKey []Key
primaryKey = genCacheKey(table.Db, table.Name, []*parser.Field{&table.PrimaryKey.Field})
primaryKey = genCacheKey(prefix, table.Db, table.Name, []*parser.Field{&table.PrimaryKey.Field})
for _, each := range table.UniqueIndex {
uniqueKey = append(uniqueKey, genCacheKey(table.Db, table.Name, each))
uniqueKey = append(uniqueKey, genCacheKey(prefix, table.Db, table.Name, each))
}
sort.Slice(uniqueKey, func(i, j int) bool {
return uniqueKey[i].VarLeft < uniqueKey[j].VarLeft
@@ -51,7 +51,7 @@ func genCacheKeys(table parser.Table) (Key, []Key) {
return primaryKey, uniqueKey
}
func genCacheKey(db, table stringx.String, in []*parser.Field) Key {
func genCacheKey(prefix string, db, table stringx.String, in []*parser.Field) Key {
var (
varLeftJoin, varRightJoin, fieldNameJoin Join
varLeft, varRight, varExpression string
@@ -62,12 +62,12 @@ func genCacheKey(db, table stringx.String, in []*parser.Field) Key {
dbName, tableName := util.SafeString(db.Source()), util.SafeString(table.Source())
if len(dbName) > 0 {
varLeftJoin = append(varLeftJoin, "cache", dbName, tableName)
varRightJoin = append(varRightJoin, "cache", dbName, tableName)
varLeftJoin = append(varLeftJoin, prefix, dbName, tableName)
varRightJoin = append(varRightJoin, prefix, dbName, tableName)
keyLeftJoin = append(keyLeftJoin, dbName, tableName)
} else {
varLeftJoin = append(varLeftJoin, "cache", tableName)
varRightJoin = append(varRightJoin, "cache", tableName)
varLeftJoin = append(varLeftJoin, prefix, tableName)
varRightJoin = append(varRightJoin, prefix, tableName)
keyLeftJoin = append(keyLeftJoin, tableName)
}

View File

@@ -34,7 +34,7 @@ func TestGenCacheKeys(t *testing.T) {
Comment: "姓名",
SeqInIndex: 2,
}
primariCacheKey, uniqueCacheKey := genCacheKeys(parser.Table{
primariCacheKey, uniqueCacheKey := genCacheKeys("cache", parser.Table{
Name: stringx.From("user"),
Db: stringx.From("go_zero"),
PrimaryKey: parser.Primary{
@@ -129,7 +129,7 @@ func TestGenCacheKeys(t *testing.T) {
}())
})
t.Run("no database name", func(t *testing.T) {
primariCacheKey, _ = genCacheKeys(parser.Table{
primariCacheKey, _ = genCacheKeys("cache", parser.Table{
Name: stringx.From("user"),
Db: stringx.From(""),
PrimaryKey: parser.Primary{

View File

@@ -13,10 +13,12 @@ import (
)
var (
userFieldNames = builder.RawFieldNames(&User{})
userRows = strings.Join(userFieldNames, ",")
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
userFieldNames = builder.RawFieldNames(&User{})
userRows = strings.Join(userFieldNames, ",")
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames,
"`id`", "`create_time`", "`update_time`"), ",")
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames,
"`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
)
type (
@@ -39,12 +41,12 @@ type (
// User defines an data structure for mysql
User struct {
ID int64 `db:"id"`
User string `db:"user"` // 用户
Name string `db:"name"` // 用户名称
Password string `db:"password"` // 用户密码
Mobile string `db:"mobile"` // 手机号
Gender string `db:"gender"` // 男|女|未公开
Nickname string `db:"nickname"` // 用户昵称
User string `db:"user"` // user
Name string `db:"name"` // user name
Password string `db:"password"` // user password
Mobile string `db:"mobile"` // user mobile
Gender string `db:"gender"` // male | female | unknown
Nickname string `db:"nickname"` // user nickname
CreateTime time.Time `db:"create_time"`
UpdateTime time.Time `db:"update_time"`
}

View File

@@ -14,69 +14,97 @@ import (
)
func Test_Parse(t *testing.T) {
t.Run("valid", func(t *testing.T) {
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(
"valid", func(t *testing.T) {
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)
splits := bytes.Split(data, []byte("-----"))
var testFile []string
for idx, split := range splits {
replacer := strings.NewReplacer(" ", "", "\t", "", "\n", "", "\r", "", "\f", "")
r := replacer.Replace(string(split))
if len(r) == 0 {
continue
}
filename := filepath.Join(t.TempDir(), fmt.Sprintf("invalid%d.api", idx))
err := os.WriteFile(filename, split, 0666)
t.Run(
"invalid", func(t *testing.T) {
data, err := os.ReadFile("./testdata/invalid.api")
assert.NoError(t, err)
testFile = append(testFile, filename)
}
for _, v := range testFile {
_, err := Parse(v, nil)
splits := bytes.Split(data, []byte("-----"))
var testFile []string
for idx, split := range splits {
replacer := strings.NewReplacer(" ", "", "\t", "", "\n", "", "\r", "", "\f", "")
r := replacer.Replace(string(split))
if len(r) == 0 {
continue
}
filename := filepath.Join(t.TempDir(), fmt.Sprintf("invalid%d.api", idx))
err := os.WriteFile(filename, split, 0666)
assert.NoError(t, err)
testFile = append(testFile, filename)
}
for _, v := range testFile {
_, err := Parse(v, nil)
assertx.Error(t, err)
}
},
)
t.Run(
"circleImport", func(t *testing.T) {
_, err := Parse("./testdata/base.api", nil)
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("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)
},
)
t.Run("duplicate_types", func(t *testing.T) {
_, err := Parse("./testdata/duplicate_type.api", nil)
assertx.Error(t, err)
})
t.Run(
"duplicate_path_expression", func(t *testing.T) {
_, err := Parse("./testdata/duplicate_path_expression.api", nil)
assertx.Error(t, err)
},
)
t.Run(
"duplicate_path_expression_different_prefix", func(t *testing.T) {
_, err := Parse("./testdata/duplicate_path_expression_different_prefix.api", nil)
assert.Nil(t, err)
},
)
}

View File

@@ -13,8 +13,8 @@ import (
)
const (
atServerGroupKey = "group:"
atServerPrefixKey = "prefix:"
atServerGroupKey = "group"
atServerPrefixKey = "prefix"
)
// API is the parsed api file.
@@ -38,18 +38,24 @@ func convert2API(a *ast.AST, importSet map[string]lang.PlaceholderType, is *impo
syntax, ok := one.(*ast.SyntaxStmt)
if !ok {
syntax = &ast.SyntaxStmt{
Syntax: ast.NewTokenNode(token.Token{
Type: token.IDENT,
Text: token.Syntax,
}),
Assign: ast.NewTokenNode(token.Token{
Type: token.ASSIGN,
Text: "=",
}),
Value: ast.NewTokenNode(token.Token{
Type: token.STRING,
Text: `"v1"`,
}),
Syntax: ast.NewTokenNode(
token.Token{
Type: token.IDENT,
Text: token.Syntax,
},
),
Assign: ast.NewTokenNode(
token.Token{
Type: token.ASSIGN,
Text: "=",
},
),
Value: ast.NewTokenNode(
token.Token{
Type: token.STRING,
Text: `"v1"`,
},
),
}
}
@@ -133,10 +139,14 @@ func (api *API) checkServiceStmt() error {
for _, item := range v.Routes {
handlerChecker.checkNodeWithPrefix(group, item.AtHandler.Name)
path := fmt.Sprintf("[%s]:%s", prefix, item.Route.Format(""))
pathChecker.check(ast.NewTokenNode(token.Token{
Text: path,
Position: item.Route.Pos(),
}))
pathChecker.check(
ast.NewTokenNode(
token.Token{
Text: path,
Position: item.Route.Pos(),
},
),
)
}
}
return f.error()
@@ -237,7 +247,8 @@ func (api *API) getAtServerValue(atServer *ast.AtServerStmt, key string) string
func (api *API) mergeAPI(in *API) error {
if api.Syntax.Value.Format() != in.Syntax.Value.Format() {
return ast.SyntaxError(in.Syntax.Value.Pos(),
return ast.SyntaxError(
in.Syntax.Value.Pos(),
"multiple syntax value expression, expected <%s>, got <%s>",
api.Syntax.Value.Format(),
in.Syntax.Value.Format(),

View File

@@ -0,0 +1,21 @@
syntax = "v1"
@server (
group: path2
middleware: Path
prefix: /v1/v3
timeout: 100ms
)
service example {
@doc (
desc: "path demo"
)
@handler getPath1
get / (string) returns (string)
@doc (
desc: "path demo"
)
@handler getPath2
get / (string) returns (string)
}

View File

@@ -0,0 +1,2 @@
import "duplicate_path_expression_different_prefix__prefix_1.api"
import "duplicate_path_expression_different_prefix__prefix_2.api"

View File

@@ -0,0 +1,16 @@
syntax = "v1"
@server (
group: group2
middleware: Path
prefix: /v1/group2
timeout: 100ms
)
service example {
@doc (
desc: "path demo"
)
@handler getPath2
get / (string) returns (string)
}

View File

@@ -0,0 +1,15 @@
syntax = "v1"
@server (
group: group1
middleware: Path
prefix: /v1/group1
timeout: 100ms
)
service example {
@doc (
desc: "path demo"
)
@handler getPath1
get / (string) returns (string)
}

View File

@@ -51,6 +51,10 @@ func projectFromGoMod(workDir string) (*ProjectContext, error) {
return nil, err
}
if err := UpdateGoWorkIfExist(workDir); err != nil {
return nil, err
}
m, err := getRealModule(workDir, execx.Run)
if err != nil {
return nil, err

View File

@@ -101,54 +101,54 @@ func Test_getRealModule(t *testing.T) {
{
name: "go work duplicate prefix",
args: args{
workDir: "D:\\code\\company\\core-ee\\service",
workDir: "/code/company/core-ee/service",
execRun: func(arg, dir string, in ...*bytes.Buffer) (string, error) {
return `
{
"Path": "gitee.com/unitedrhino/core",
"Dir": "D:\\code\\company\\core",
"GoMod": "D:\\code\\company\\core\\go.mod",
"Dir": "/code/company/core",
"GoMod": "/code/company/core/go.mod",
"GoVersion": "1.21.4"
}
{
"Path": "gitee.com/unitedrhino/core-ee",
"Dir": "D:\\code\\company\\core-ee",
"GoMod": "D:\\code\\company\\core-ee\\go.mod",
"Dir": "/code/company/core-ee",
"GoMod": "/code/company/core-ee/go.mod",
"GoVersion": "1.21.4"
}`, nil
},
},
want: &Module{
Path: "gitee.com/unitedrhino/core-ee",
Dir: "D:\\code\\company\\core-ee",
GoMod: "D:\\code\\company\\core-ee\\go.mod",
Dir: "/code/company/core-ee",
GoMod: "/code/company/core-ee/go.mod",
GoVersion: "1.21.4",
},
},
{
name: "go work duplicate prefix2",
args: args{
workDir: "D:\\code\\company\\core-ee",
workDir: "/code/company/core-ee",
execRun: func(arg, dir string, in ...*bytes.Buffer) (string, error) {
return `
{
"Path": "gitee.com/unitedrhino/core",
"Dir": "D:\\code\\company\\core",
"GoMod": "D:\\code\\company\\core\\go.mod",
"Dir": "/code/company/core",
"GoMod": "/code/company/core/go.mod",
"GoVersion": "1.21.4"
}
{
"Path": "gitee.com/unitedrhino/core-ee",
"Dir": "D:\\code\\company\\core-ee",
"GoMod": "D:\\code\\company\\core-ee\\go.mod",
"Dir": "/code/company/core-ee",
"GoMod": "/code/company/core-ee/go.mod",
"GoVersion": "1.21.4"
}`, nil
},
},
want: &Module{
Path: "gitee.com/unitedrhino/core-ee",
Dir: "D:\\code\\company\\core-ee",
GoMod: "D:\\code\\company\\core-ee\\go.mod",
Dir: "/code/company/core-ee",
GoMod: "/code/company/core-ee/go.mod",
GoVersion: "1.21.4",
},
},

View File

@@ -0,0 +1,33 @@
package ctx
import (
"errors"
"os"
"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
)
// UpdateGoWorkIfExist updates go work if workDir is in a go workspace
func UpdateGoWorkIfExist(workDir string) error {
if isGoWork, err := isGoWork(workDir); !isGoWork || err != nil {
return err
}
_, err := execx.Run("go work use .", workDir)
return err
}
// isGoWork detect if the workDir is in a go workspace
func isGoWork(workDir string) (bool, error) {
if len(workDir) == 0 {
return false, errors.New("the work directory is not found")
}
if _, err := os.Stat(workDir); err != nil {
return false, err
}
goWorkPath, err := execx.Run("go env GOWORK", workDir)
if err != nil {
return false, err
}
return len(goWorkPath) > 0, nil
}

View File

@@ -0,0 +1,40 @@
package ctx
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/stringx"
"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
)
func Test_isGoWork(t *testing.T) {
dir := filepath.Join("/tmp", stringx.Rand())
err := pathx.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer os.RemoveAll(dir)
gowork, err := isGoWork(dir)
assert.False(t, gowork)
assert.Nil(t, err)
_, err = execx.Run("go work init", dir)
assert.Nil(t, err)
gowork, err = isGoWork(dir)
assert.True(t, gowork)
assert.Nil(t, err)
subDir := filepath.Join(dir, stringx.Rand())
err = pathx.MkdirIfNotExist(subDir)
assert.Nil(t, err)
gowork, err = isGoWork(subDir)
assert.True(t, gowork)
assert.Nil(t, err)
}

View File

@@ -7,7 +7,7 @@ import (
"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
)
// IsGoMod is used to determine whether workDir is a go module project through command `go list -json -m`
// IsGoMod is used to determine whether workDir is a go module project through command `go env GOMOD`
func IsGoMod(workDir string) (bool, error) {
if len(workDir) == 0 {
return false, errors.New("the work directory is not found")
@@ -16,8 +16,8 @@ func IsGoMod(workDir string) (bool, error) {
return false, err
}
data, err := execx.Run("go list -m -f '{{.GoMod}}'", workDir)
if err != nil || len(data) == 0 {
data, err := execx.Run("go env GOMOD", workDir)
if err != nil || data == "/dev/null" {
return false, nil
}

View File

@@ -84,7 +84,7 @@ func logDuration(ctx context.Context, method string, req any, duration time.Dura
content, err := json.Marshal(req)
if err != nil {
logx.WithContext(ctx).Errorf("%s - %s", addr, err.Error())
} else if duration > slowThreshold.Load() {
} else if isSlow(duration, durationThreshold) {
logger.Slowf("[RPC] slowcall - %s - %s - %s", addr, method, string(content))
} else {
logger.Infof("%s - %s - %s", addr, method, string(content))

View File

@@ -43,10 +43,11 @@ func TestLogDuration(t *testing.T) {
assert.True(t, len(addrs) > 0)
tests := []struct {
name string
ctx context.Context
req any
duration time.Duration
name string
ctx context.Context
req any
duration time.Duration
durationThreshold time.Duration
}{
{
name: "normal",
@@ -59,10 +60,11 @@ func TestLogDuration(t *testing.T) {
req: make(chan lang.PlaceholderType), // not marshalable
},
{
name: "timeout",
ctx: context.Background(),
req: "foo",
duration: time.Second,
name: "timeout",
ctx: context.Background(),
req: "foo",
duration: time.Second,
durationThreshold: time.Millisecond * 500,
},
{
name: "timeout",
@@ -86,7 +88,7 @@ func TestLogDuration(t *testing.T) {
assert.NotPanics(t, func() {
logDuration(test.ctx, "foo", test.req, test.duration,
collection.NewSet(), 0)
collection.NewSet(), test.durationThreshold)
})
})
}
@@ -98,10 +100,11 @@ func TestLogDurationWithoutContent(t *testing.T) {
assert.True(t, len(addrs) > 0)
tests := []struct {
name string
ctx context.Context
req any
duration time.Duration
name string
ctx context.Context
req any
duration time.Duration
durationThreshold time.Duration
}{
{
name: "normal",
@@ -114,10 +117,11 @@ func TestLogDurationWithoutContent(t *testing.T) {
req: make(chan lang.PlaceholderType), // not marshalable
},
{
name: "timeout",
ctx: context.Background(),
req: "foo",
duration: time.Second,
name: "timeout",
ctx: context.Background(),
req: "foo",
duration: time.Second,
durationThreshold: time.Millisecond * 500,
},
{
name: "timeout",
@@ -146,7 +150,7 @@ func TestLogDurationWithoutContent(t *testing.T) {
assert.NotPanics(t, func() {
logDuration(test.ctx, "foo", test.req, test.duration,
collection.NewSet(), 0)
collection.NewSet(), test.durationThreshold)
})
})
}
@@ -257,7 +261,8 @@ func Test_isSlow(t *testing.T) {
t.Cleanup(func() {
slowThreshold = syncx.ForAtomicDuration(defaultSlowThreshold)
})
assert.Equalf(t, tt.want, isSlow(tt.args.duration, tt.args.staticSlowThreshold), "isSlow(%v, %v)", tt.args.duration, tt.args.staticSlowThreshold)
assert.Equalf(t, tt.want, isSlow(tt.args.duration, tt.args.staticSlowThreshold),
"isSlow(%v, %v)", tt.args.duration, tt.args.staticSlowThreshold)
})
}
}