Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
07d4085201 | |||
45f30c0be3 | |||
bcaea675a7 | |||
3087ba1d73 | |||
3f5b19497c | |||
37d937d7ae | |||
7d68f2396e | |||
0854a7ea72 |
@@ -17,7 +17,7 @@ syntax = "proto3";
|
||||
package micro.codec;
|
||||
|
||||
option cc_enable_arenas = true;
|
||||
option go_package = "github.com/unistack-org/micro/v3/codec;codec";
|
||||
option go_package = "go.unistack.org/micro/v3/codec;codec";
|
||||
option java_multiple_files = true;
|
||||
option java_outer_classname = "MicroCodec";
|
||||
option java_package = "micro.codec";
|
||||
|
2
go.mod
2
go.mod
@@ -8,7 +8,7 @@ require (
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/silas/dag v0.0.0-20210626123444-3804bac2d6d4
|
||||
github.com/unistack-org/micro-proto v0.0.9
|
||||
go.unistack.org/micro-proto/v3 v3.1.0
|
||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
)
|
||||
|
10
go.sum
10
go.sum
@@ -1,7 +1,5 @@
|
||||
github.com/ef-ds/deque v1.0.4 h1:iFAZNmveMT9WERAkqLJ+oaABF9AcVQ5AjXem/hroniI=
|
||||
github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0=
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
@@ -11,14 +9,10 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34 h1:vBfVmA5mZhsQa2jr1FOL9nfA37N/jnbBmi5XUfviVTI=
|
||||
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
||||
github.com/silas/dag v0.0.0-20210626123444-3804bac2d6d4 h1:fOH64AB0C3ixGf9emky61STvPJL3smxJg+1Zwx1oCdg=
|
||||
github.com/silas/dag v0.0.0-20210626123444-3804bac2d6d4/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
||||
github.com/unistack-org/micro-proto v0.0.9 h1:KrWLS4FUX7UAWNAilQf70uad6ZPf/0EudeddCXllRVc=
|
||||
github.com/unistack-org/micro-proto v0.0.9/go.mod h1:Cckwmzd89gvS7ThxzZp9kQR/EOdksFQcsTAtDDyKwrg=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
go.unistack.org/micro-proto/v3 v3.1.0 h1:q39FwjFiRZn+Ux/tt+d3bJTmDtsQQWa+3SLYVo1vLfA=
|
||||
go.unistack.org/micro-proto/v3 v3.1.0/go.mod h1:DpRhYCBXlmSJ/AAXTmntvlh7kQkYU6eFvlmYAx4BQS8=
|
||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b h1:eB48h3HiRycXNy8E0Gf5e0hv7YT6Kt14L/D73G1fuwo=
|
||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@@ -1,12 +1,12 @@
|
||||
package meter
|
||||
|
||||
//go:generate sh -c "protoc -I./handler -I../ -I$(go list -f '{{ .Dir }}' -m github.com/unistack-org/micro-proto) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./handler handler/handler.proto"
|
||||
//go:generate sh -c "protoc -I./handler -I../ -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./handler handler/handler.proto"
|
||||
|
||||
import (
|
||||
|
||||
// import required packages
|
||||
_ "github.com/unistack-org/micro-proto/api"
|
||||
_ "go.unistack.org/micro-proto/v3/api"
|
||||
|
||||
// import required packages
|
||||
_ "github.com/unistack-org/micro-proto/openapiv2"
|
||||
_ "go.unistack.org/micro-proto/v3/openapiv3"
|
||||
)
|
||||
|
@@ -1,24 +1,20 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package micro.meter.handler;
|
||||
option go_package = "github.com/unistack-org/micro/v3/meter/handler;handler";
|
||||
option go_package = "go.unistack.org/micro/v3/meter/handler;handler";
|
||||
|
||||
import "api/annotations.proto";
|
||||
import "openapiv2/annotations.proto";
|
||||
import "openapiv3/annotations.proto";
|
||||
import "codec/frame.proto";
|
||||
|
||||
service Meter {
|
||||
rpc Metrics(micro.codec.Frame) returns (micro.codec.Frame) {
|
||||
option (micro.openapiv2.openapiv2_operation) = {
|
||||
option (micro.openapiv3.openapiv3_operation) = {
|
||||
operation_id: "Metrics";
|
||||
responses: {
|
||||
response_code: {
|
||||
name: "default";
|
||||
value: {
|
||||
json_reference: {
|
||||
description: "Error response";
|
||||
_ref: "micro.codec.Frame";
|
||||
};
|
||||
default: {
|
||||
reference: {
|
||||
_ref: "micro.codec.Frame";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@@ -1,12 +1,11 @@
|
||||
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
|
||||
// protoc-gen-go-micro version: v3.4.2
|
||||
// protoc-gen-go-micro version: v3.5.2
|
||||
// source: handler.proto
|
||||
|
||||
package handler
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
api "go.unistack.org/micro/v3/api"
|
||||
codec "go.unistack.org/micro/v3/codec"
|
||||
)
|
||||
|
@@ -1,12 +1,11 @@
|
||||
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
|
||||
// protoc-gen-go-micro version: v3.4.2
|
||||
// protoc-gen-go-micro version: v3.5.2
|
||||
// source: handler.proto
|
||||
|
||||
package handler
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
api "go.unistack.org/micro/v3/api"
|
||||
codec "go.unistack.org/micro/v3/codec"
|
||||
server "go.unistack.org/micro/v3/server"
|
||||
|
@@ -28,17 +28,31 @@ var (
|
||||
|
||||
// Meter is an interface for collecting and instrumenting metrics
|
||||
type Meter interface {
|
||||
// Name returns meter name
|
||||
Name() string
|
||||
// Init initialize meter
|
||||
Init(opts ...Option) error
|
||||
// Clone create meter copy with new options
|
||||
Clone(opts ...Option) Meter
|
||||
// Counter get or create counter
|
||||
Counter(name string, labels ...string) Counter
|
||||
// FloatCounter get or create float counter
|
||||
FloatCounter(name string, labels ...string) FloatCounter
|
||||
// Gauge get or create gauge
|
||||
Gauge(name string, fn func() float64, labels ...string) Gauge
|
||||
// Set create new meter metrics set
|
||||
Set(opts ...Option) Meter
|
||||
// Histogram get or create histogram
|
||||
Histogram(name string, labels ...string) Histogram
|
||||
// Summary get or create summary
|
||||
Summary(name string, labels ...string) Summary
|
||||
// SummaryExt get or create summary with spcified quantiles and window time
|
||||
SummaryExt(name string, window time.Duration, quantiles []float64, labels ...string) Summary
|
||||
// Write writes metrics to io.Writer
|
||||
Write(w io.Writer, opts ...Option) error
|
||||
// Options returns meter options
|
||||
Options() Options
|
||||
// String return meter type
|
||||
String() string
|
||||
}
|
||||
|
||||
|
@@ -15,6 +15,15 @@ func NewMeter(opts ...Option) Meter {
|
||||
return &noopMeter{opts: NewOptions(opts...)}
|
||||
}
|
||||
|
||||
// Clone return old meter with new options
|
||||
func (r *noopMeter) Clone(opts ...Option) Meter {
|
||||
options := r.opts
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
return &noopMeter{opts: options}
|
||||
}
|
||||
|
||||
func (r *noopMeter) Name() string {
|
||||
return r.opts.Name
|
||||
}
|
||||
|
@@ -51,6 +51,20 @@ func NewOptions(opt ...Option) Options {
|
||||
return opts
|
||||
}
|
||||
|
||||
// LabelPrefix sets the labels prefix
|
||||
func LabelPrefix(pref string) Option {
|
||||
return func(o *Options) {
|
||||
o.LabelPrefix = pref
|
||||
}
|
||||
}
|
||||
|
||||
// MetricPrefix sets the metric prefix
|
||||
func MetricPrefix(pref string) Option {
|
||||
return func(o *Options) {
|
||||
o.MetricPrefix = pref
|
||||
}
|
||||
}
|
||||
|
||||
// Context sets the metrics context
|
||||
func Context(ctx context.Context) Option {
|
||||
return func(o *Options) {
|
||||
|
@@ -1,12 +1,12 @@
|
||||
package server
|
||||
|
||||
//go:generate sh -c "protoc -I./health -I../ -I$(go list -f '{{ .Dir }}' -m github.com/unistack-org/micro-proto) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./health health/health.proto"
|
||||
//go:generate sh -c "protoc -I./health -I../ -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./health health/health.proto"
|
||||
|
||||
import (
|
||||
|
||||
// import required packages
|
||||
_ "github.com/unistack-org/micro-proto/api"
|
||||
_ "go.unistack.org/micro-proto/v3/api"
|
||||
|
||||
// import required packages
|
||||
_ "github.com/unistack-org/micro-proto/openapiv2"
|
||||
_ "go.unistack.org/micro-proto/v3/openapiv3"
|
||||
)
|
||||
|
@@ -1,24 +1,20 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package micro.server.health;
|
||||
option go_package = "github.com/unistack-org/micro/v3/server/health;health";
|
||||
option go_package = "go.unistack.org/micro/v3/server/health;health";
|
||||
|
||||
import "api/annotations.proto";
|
||||
import "openapiv2/annotations.proto";
|
||||
import "openapiv3/annotations.proto";
|
||||
import "codec/frame.proto";
|
||||
|
||||
service Health {
|
||||
rpc Live(micro.codec.Frame) returns (micro.codec.Frame) {
|
||||
option (micro.openapiv2.openapiv2_operation) = {
|
||||
option (micro.openapiv3.openapiv3_operation) = {
|
||||
operation_id: "Live";
|
||||
responses: {
|
||||
response_code: {
|
||||
name: "default";
|
||||
value: {
|
||||
json_reference: {
|
||||
description: "Error response";
|
||||
_ref: "micro.codec.Frame";
|
||||
};
|
||||
default: {
|
||||
reference: {
|
||||
_ref: "micro.codec.Frame";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -26,16 +22,12 @@ service Health {
|
||||
option (micro.api.http) = { get: "/live"; };
|
||||
};
|
||||
rpc Ready(micro.codec.Frame) returns (micro.codec.Frame) {
|
||||
option (micro.openapiv2.openapiv2_operation) = {
|
||||
option (micro.openapiv3.openapiv3_operation) = {
|
||||
operation_id: "Ready";
|
||||
responses: {
|
||||
response_code: {
|
||||
name: "default";
|
||||
value: {
|
||||
json_reference: {
|
||||
description: "Error response";
|
||||
_ref: "micro.codec.Frame";
|
||||
};
|
||||
default: {
|
||||
reference: {
|
||||
_ref: "micro.codec.Frame";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -43,16 +35,12 @@ service Health {
|
||||
option (micro.api.http) = { get: "/ready"; };
|
||||
};
|
||||
rpc Version(micro.codec.Frame) returns (micro.codec.Frame) {
|
||||
option (micro.openapiv2.openapiv2_operation) = {
|
||||
option (micro.openapiv3.openapiv3_operation) = {
|
||||
operation_id: "Version";
|
||||
responses: {
|
||||
response_code: {
|
||||
name: "default";
|
||||
value: {
|
||||
json_reference: {
|
||||
description: "Error response";
|
||||
_ref: "micro.codec.Frame";
|
||||
};
|
||||
default: {
|
||||
reference: {
|
||||
_ref: "micro.codec.Frame";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@@ -1,12 +1,11 @@
|
||||
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
|
||||
// protoc-gen-go-micro version: v3.4.2
|
||||
// protoc-gen-go-micro version: v3.5.2
|
||||
// source: health.proto
|
||||
|
||||
package health
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
api "go.unistack.org/micro/v3/api"
|
||||
codec "go.unistack.org/micro/v3/codec"
|
||||
)
|
||||
|
@@ -1,12 +1,11 @@
|
||||
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
|
||||
// protoc-gen-go-micro version: v3.4.2
|
||||
// protoc-gen-go-micro version: v3.5.2
|
||||
// source: health.proto
|
||||
|
||||
package health
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
api "go.unistack.org/micro/v3/api"
|
||||
codec "go.unistack.org/micro/v3/codec"
|
||||
server "go.unistack.org/micro/v3/server"
|
||||
|
@@ -392,8 +392,12 @@ type nameIface interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
func getNameIndex(n string, ifaces ...interface{}) int {
|
||||
for idx, iface := range ifaces {
|
||||
func getNameIndex(n string, ifaces interface{}) int {
|
||||
values, ok := ifaces.([]interface{})
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
for idx, iface := range values {
|
||||
if ifc, ok := iface.(nameIface); ok && ifc.Name() == n {
|
||||
return idx
|
||||
}
|
||||
|
22
service_test.go
Normal file
22
service_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package micro
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testItem struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (ti *testItem) Name() string {
|
||||
return ti.name
|
||||
}
|
||||
|
||||
func TestGetNameIndex(t *testing.T) {
|
||||
item1 := &testItem{name: "first"}
|
||||
item2 := &testItem{name: "second"}
|
||||
items := []interface{}{item1, item2}
|
||||
if idx := getNameIndex("second", items); idx != 1 {
|
||||
t.Fatalf("getNameIndex func error, item not found")
|
||||
}
|
||||
}
|
@@ -8,14 +8,14 @@ import (
|
||||
type tracerKey struct{}
|
||||
|
||||
// FromContext returns a tracer from context
|
||||
func FromContext(ctx context.Context) Tracer {
|
||||
func FromContext(ctx context.Context) (Tracer, bool) {
|
||||
if ctx == nil {
|
||||
return DefaultTracer
|
||||
return nil, false
|
||||
}
|
||||
if tracer, ok := ctx.Value(tracerKey{}).(Tracer); ok {
|
||||
return tracer
|
||||
return tracer, true
|
||||
}
|
||||
return DefaultTracer
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// NewContext saves the tracer in the context
|
||||
@@ -29,14 +29,14 @@ func NewContext(ctx context.Context, tracer Tracer) context.Context {
|
||||
type spanKey struct{}
|
||||
|
||||
// SpanFromContext returns a span from context
|
||||
func SpanFromContext(ctx context.Context) Span {
|
||||
func SpanFromContext(ctx context.Context) (Span, bool) {
|
||||
if ctx == nil {
|
||||
return &noopSpan{}
|
||||
return nil, false
|
||||
}
|
||||
if span, ok := ctx.Value(spanKey{}).(Span); ok {
|
||||
return span
|
||||
return span, true
|
||||
}
|
||||
return &noopSpan{}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// NewSpanContext saves the span in the context
|
||||
|
@@ -35,6 +35,7 @@ type noopSpan struct {
|
||||
ctx context.Context
|
||||
tracer Tracer
|
||||
name string
|
||||
labels []Label
|
||||
}
|
||||
|
||||
func (s *noopSpan) Finish(opts ...SpanOption) {
|
||||
@@ -56,6 +57,7 @@ func (s *noopSpan) SetName(name string) {
|
||||
}
|
||||
|
||||
func (s *noopSpan) SetLabels(labels ...Label) {
|
||||
s.labels = labels
|
||||
}
|
||||
|
||||
// NewTracer returns new memory tracer
|
||||
|
@@ -38,26 +38,26 @@ type Label struct {
|
||||
key string
|
||||
}
|
||||
|
||||
func Any(k string, v interface{}) Label {
|
||||
func LabelAny(k string, v interface{}) Label {
|
||||
return Label{key: k, val: v}
|
||||
}
|
||||
|
||||
func String(k string, v string) Label {
|
||||
func LabelString(k string, v string) Label {
|
||||
return Label{key: k, val: v}
|
||||
}
|
||||
|
||||
func Int(k string, v int) Label {
|
||||
func LabelInt(k string, v int) Label {
|
||||
return Label{key: k, val: v}
|
||||
}
|
||||
|
||||
func Int64(k string, v int64) Label {
|
||||
func LabelInt64(k string, v int64) Label {
|
||||
return Label{key: k, val: v}
|
||||
}
|
||||
|
||||
func Float64(k string, v float64) Label {
|
||||
func LabelFloat64(k string, v float64) Label {
|
||||
return Label{key: k, val: v}
|
||||
}
|
||||
|
||||
func Bool(k string, v bool) Label {
|
||||
func LabelBool(k string, v bool) Label {
|
||||
return Label{key: k, val: v}
|
||||
}
|
||||
|
@@ -69,6 +69,89 @@ func StructFieldByTag(src interface{}, tkey string, tval string) (interface{}, e
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
// ZeroFieldByPath clean struct field by its path
|
||||
func ZeroFieldByPath(src interface{}, path string) error {
|
||||
var err error
|
||||
val := reflect.ValueOf(src)
|
||||
|
||||
for _, p := range strings.Split(path, ".") {
|
||||
val, err = structValueByName(val, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if IsEmpty(val) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !val.CanSet() {
|
||||
return ErrInvalidStruct
|
||||
}
|
||||
|
||||
val.Set(reflect.Zero(val.Type()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetFieldByPath set struct field by its path
|
||||
func SetFieldByPath(src interface{}, dst interface{}, path string) error {
|
||||
var err error
|
||||
val := reflect.ValueOf(src)
|
||||
|
||||
for _, p := range strings.Split(path, ".") {
|
||||
val, err = structValueByName(val, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !val.CanSet() {
|
||||
return ErrInvalidStruct
|
||||
}
|
||||
|
||||
val.Set(reflect.ValueOf(dst))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// structValueByName get struct field by its name
|
||||
func structValueByName(sv reflect.Value, tkey string) (reflect.Value, error) {
|
||||
if sv.Kind() == reflect.Ptr {
|
||||
sv = sv.Elem()
|
||||
}
|
||||
if sv.Kind() != reflect.Struct {
|
||||
return reflect.Zero(reflect.TypeOf(sv)), ErrInvalidStruct
|
||||
}
|
||||
|
||||
typ := sv.Type()
|
||||
for idx := 0; idx < typ.NumField(); idx++ {
|
||||
fld := typ.Field(idx)
|
||||
val := sv.Field(idx)
|
||||
if len(fld.PkgPath) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if fld.Name == tkey || strings.EqualFold(strings.ToLower(fld.Name), strings.ToLower(tkey)) {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
if val = val.Elem(); val.Kind() == reflect.Struct {
|
||||
if iface, err := structValueByName(val, tkey); err == nil {
|
||||
return iface, nil
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
if iface, err := structValueByName(val, tkey); err == nil {
|
||||
return iface, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return reflect.Zero(reflect.TypeOf(sv)), ErrNotFound
|
||||
}
|
||||
|
||||
// StructFieldByPath get struct field by its path
|
||||
func StructFieldByPath(src interface{}, path string) (interface{}, error) {
|
||||
var err error
|
||||
@@ -98,10 +181,7 @@ func StructFieldByName(src interface{}, tkey string) (interface{}, error) {
|
||||
if len(fld.PkgPath) != 0 {
|
||||
continue
|
||||
}
|
||||
if fld.Name == tkey {
|
||||
if val.Kind() != reflect.Ptr && val.CanAddr() {
|
||||
val = val.Addr()
|
||||
}
|
||||
if fld.Name == tkey || strings.EqualFold(strings.ToLower(fld.Name), strings.ToLower(tkey)) {
|
||||
return val.Interface(), nil
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,80 @@ import (
|
||||
rutil "go.unistack.org/micro/v3/util/reflect"
|
||||
)
|
||||
|
||||
func TestSetFieldByPath(t *testing.T) {
|
||||
type NestedStr struct {
|
||||
BBB string `json:"bbb"`
|
||||
CCC int `json:"ccc"`
|
||||
}
|
||||
type Str1 struct {
|
||||
Name []string `json:"name" codec:"flatten"`
|
||||
XXX string `json:"xxx"`
|
||||
Nested NestedStr `json:"nested"`
|
||||
}
|
||||
type Str2 struct {
|
||||
XXX string `json:"xxx"`
|
||||
Nested *NestedStr `json:"nested"`
|
||||
Name []string `json:"name" codec:"flatten"`
|
||||
}
|
||||
var err error
|
||||
val1 := &Str1{Name: []string{"first", "second"}, XXX: "ttt", Nested: NestedStr{BBB: "ddd", CCC: 9}}
|
||||
val2 := &Str2{Name: []string{"first", "second"}, XXX: "ttt", Nested: &NestedStr{BBB: "ddd", CCC: 9}}
|
||||
err = rutil.SetFieldByPath(val1, "xxx", "Nested.BBB")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if val1.Nested.BBB != "xxx" {
|
||||
t.Fatalf("SetFieldByPath not works: %#+v", val1)
|
||||
}
|
||||
err = rutil.SetFieldByPath(val2, "xxx", "Nested.BBB")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if val2.Nested.BBB != "xxx" {
|
||||
t.Fatalf("SetFieldByPath not works: %#+v", val1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZeroFieldByPath(t *testing.T) {
|
||||
type NestedStr struct {
|
||||
BBB string `json:"bbb"`
|
||||
CCC int `json:"ccc"`
|
||||
}
|
||||
type Str1 struct {
|
||||
Name []string `json:"name" codec:"flatten"`
|
||||
XXX string `json:"xxx"`
|
||||
Nested NestedStr `json:"nested"`
|
||||
}
|
||||
type Str2 struct {
|
||||
XXX string `json:"xxx"`
|
||||
Nested *NestedStr `json:"nested"`
|
||||
Name []string `json:"name" codec:"flatten"`
|
||||
}
|
||||
var err error
|
||||
val1 := &Str1{Name: []string{"first", "second"}, XXX: "ttt", Nested: NestedStr{BBB: "ddd", CCC: 9}}
|
||||
|
||||
err = rutil.ZeroFieldByPath(val1, "Nested.BBB")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = rutil.ZeroFieldByPath(val1, "Nested")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if val1.Nested.BBB == "ddd" {
|
||||
t.Fatalf("zero field not works: %v", val1)
|
||||
}
|
||||
|
||||
val2 := &Str2{Name: []string{"first", "second"}, XXX: "ttt", Nested: &NestedStr{BBB: "ddd", CCC: 9}}
|
||||
err = rutil.ZeroFieldByPath(val2, "Nested")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if val2.Nested != nil {
|
||||
t.Fatalf("zero field not works: %v", val2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructFieldsMap(t *testing.T) {
|
||||
type NestedStr struct {
|
||||
BBB string
|
||||
@@ -108,9 +182,9 @@ func TestStructByName(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v, ok := iface.(*[]string); !ok {
|
||||
t.Fatalf("not *[]string %v", iface)
|
||||
} else if len(*v) != 2 {
|
||||
if v, ok := iface.([]string); !ok {
|
||||
t.Fatalf("not []string %v", iface)
|
||||
} else if len(v) != 2 {
|
||||
t.Fatalf("invalid number %v", iface)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user