Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
8abc913b28 | |||
3247d144a8 | |||
7b2e3cc8aa | |||
8688179acd | |||
3e40bac5f4 | |||
e3fee6f8a6 | |||
15c020fac5 | |||
3bc046e5d4 | |||
542f36cfa5 | |||
8237e6a08e | |||
ecb60e4dc5 | |||
a1999ff81c | |||
d0f2bc8346 | |||
|
dd29bf457e | ||
d062c248e3 |
2
.github/workflows/dependabot-automerge.yml
vendored
2
.github/workflows/dependabot-automerge.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
|| (github.event.workflow_run.event == 'push' && github.event.workflow_run.pull_requests[0].base.ref == github.event.repository.default_branch ))
|
||||
steps:
|
||||
- name: Approve Changes and Merge changes if label 'dependencies' is set
|
||||
uses: actions/github-script@v4
|
||||
uses: actions/github-script@v5
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
@@ -30,7 +30,7 @@ linters:
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goimports
|
||||
- golint
|
||||
- revive
|
||||
- gosec
|
||||
- makezero
|
||||
- misspell
|
||||
|
11
api/api.go
11
api/api.go
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/unistack-org/micro/v3/server"
|
||||
)
|
||||
|
||||
// nolint: revive
|
||||
// Api interface
|
||||
type Api interface {
|
||||
// Initialise options
|
||||
@@ -125,14 +126,14 @@ func Validate(e *Endpoint) error {
|
||||
ps := p[0]
|
||||
pe := p[len(p)-1]
|
||||
|
||||
if ps == '^' && pe == '$' {
|
||||
_, err := regexp.CompilePOSIX(p)
|
||||
if err != nil {
|
||||
switch {
|
||||
case ps == '^' && pe == '$':
|
||||
if _, err := regexp.CompilePOSIX(p); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if ps == '^' && pe != '$' {
|
||||
case ps == '^' && pe != '$':
|
||||
return errors.New("invalid path")
|
||||
} else if ps != '^' && pe == '$' {
|
||||
case ps != '^' && pe == '$':
|
||||
return errors.New("invalid path")
|
||||
}
|
||||
}
|
||||
|
164
broker/memory.go
164
broker/memory.go
@@ -89,36 +89,15 @@ func (m *memoryBroker) Init(opts ...Option) error {
|
||||
}
|
||||
|
||||
func (m *memoryBroker) Publish(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error {
|
||||
m.RLock()
|
||||
if !m.connected {
|
||||
m.RUnlock()
|
||||
return ErrNotConnected
|
||||
}
|
||||
m.RUnlock()
|
||||
|
||||
options := NewPublishOptions(opts...)
|
||||
vs := make([]msgWrapper, 0, 1)
|
||||
if m.opts.Codec == nil || options.BodyOnly {
|
||||
topic, _ := msg.Header.Get(metadata.HeaderTopic)
|
||||
vs = append(vs, msgWrapper{topic: topic, body: msg})
|
||||
} else {
|
||||
topic, _ := msg.Header.Get(metadata.HeaderTopic)
|
||||
buf, err := m.opts.Codec.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vs = append(vs, msgWrapper{topic: topic, body: buf})
|
||||
}
|
||||
|
||||
return m.publish(ctx, vs, opts...)
|
||||
}
|
||||
|
||||
type msgWrapper struct {
|
||||
topic string
|
||||
body interface{}
|
||||
msg.Header.Set(metadata.HeaderTopic, topic)
|
||||
return m.publish(ctx, []*Message{msg}, opts...)
|
||||
}
|
||||
|
||||
func (m *memoryBroker) BatchPublish(ctx context.Context, msgs []*Message, opts ...PublishOption) error {
|
||||
return m.publish(ctx, msgs, opts...)
|
||||
}
|
||||
|
||||
func (m *memoryBroker) publish(ctx context.Context, msgs []*Message, opts ...PublishOption) error {
|
||||
m.RLock()
|
||||
if !m.connected {
|
||||
m.RUnlock()
|
||||
@@ -126,88 +105,81 @@ func (m *memoryBroker) BatchPublish(ctx context.Context, msgs []*Message, opts .
|
||||
}
|
||||
m.RUnlock()
|
||||
|
||||
options := NewPublishOptions(opts...)
|
||||
vs := make([]msgWrapper, 0, len(msgs))
|
||||
if m.opts.Codec == nil || options.BodyOnly {
|
||||
for _, msg := range msgs {
|
||||
topic, _ := msg.Header.Get(metadata.HeaderTopic)
|
||||
vs = append(vs, msgWrapper{topic: topic, body: msg})
|
||||
}
|
||||
} else {
|
||||
for _, msg := range msgs {
|
||||
topic, _ := msg.Header.Get(metadata.HeaderTopic)
|
||||
buf, err := m.opts.Codec.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vs = append(vs, msgWrapper{topic: topic, body: buf})
|
||||
}
|
||||
}
|
||||
|
||||
return m.publish(ctx, vs, opts...)
|
||||
}
|
||||
|
||||
func (m *memoryBroker) publish(ctx context.Context, vs []msgWrapper, opts ...PublishOption) error {
|
||||
var err error
|
||||
|
||||
msgTopicMap := make(map[string]Events)
|
||||
for _, v := range vs {
|
||||
p := &memoryEvent{
|
||||
topic: v.topic,
|
||||
message: v.body,
|
||||
opts: m.opts,
|
||||
}
|
||||
msgTopicMap[p.topic] = append(msgTopicMap[p.topic], p)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
options := NewPublishOptions(opts...)
|
||||
|
||||
beh := m.opts.BatchErrorHandler
|
||||
eh := m.opts.ErrorHandler
|
||||
msgTopicMap := make(map[string]Events)
|
||||
for _, v := range msgs {
|
||||
p := &memoryEvent{opts: m.opts}
|
||||
|
||||
for t, ms := range msgTopicMap {
|
||||
m.RLock()
|
||||
subs, ok := m.subscribers[t]
|
||||
m.RUnlock()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, sub := range subs {
|
||||
// batch processing
|
||||
if sub.batchhandler != nil {
|
||||
if err = sub.batchhandler(ms); err != nil {
|
||||
ms.SetError(err)
|
||||
if sub.opts.BatchErrorHandler != nil {
|
||||
beh = sub.opts.BatchErrorHandler
|
||||
}
|
||||
if beh != nil {
|
||||
beh(ms)
|
||||
} else if m.opts.Logger.V(logger.ErrorLevel) {
|
||||
m.opts.Logger.Error(m.opts.Context, err.Error())
|
||||
}
|
||||
} else if sub.opts.AutoAck {
|
||||
if err = ms.Ack(); err != nil {
|
||||
m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err)
|
||||
}
|
||||
if m.opts.Codec == nil || options.BodyOnly {
|
||||
p.topic, _ = v.Header.Get(metadata.HeaderTopic)
|
||||
p.message = v.Body
|
||||
} else {
|
||||
p.topic, _ = v.Header.Get(metadata.HeaderTopic)
|
||||
p.message, err = m.opts.Codec.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// single processing
|
||||
if sub.handler != nil {
|
||||
for _, p := range ms {
|
||||
if err = sub.handler(p); err != nil {
|
||||
p.SetError(err)
|
||||
if sub.opts.ErrorHandler != nil {
|
||||
eh = sub.opts.ErrorHandler
|
||||
}
|
||||
if eh != nil {
|
||||
eh(p)
|
||||
msgTopicMap[p.topic] = append(msgTopicMap[p.topic], p)
|
||||
}
|
||||
|
||||
beh := m.opts.BatchErrorHandler
|
||||
eh := m.opts.ErrorHandler
|
||||
|
||||
for t, ms := range msgTopicMap {
|
||||
m.RLock()
|
||||
subs, ok := m.subscribers[t]
|
||||
m.RUnlock()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, sub := range subs {
|
||||
if sub.opts.BatchErrorHandler != nil {
|
||||
beh = sub.opts.BatchErrorHandler
|
||||
}
|
||||
if sub.opts.ErrorHandler != nil {
|
||||
eh = sub.opts.ErrorHandler
|
||||
}
|
||||
|
||||
switch {
|
||||
// batch processing
|
||||
case sub.batchhandler != nil:
|
||||
if err = sub.batchhandler(ms); err != nil {
|
||||
ms.SetError(err)
|
||||
if beh != nil {
|
||||
_ = beh(ms)
|
||||
} else if m.opts.Logger.V(logger.ErrorLevel) {
|
||||
m.opts.Logger.Error(m.opts.Context, err.Error())
|
||||
}
|
||||
} else if sub.opts.AutoAck {
|
||||
if err = p.Ack(); err != nil {
|
||||
if err = ms.Ack(); err != nil {
|
||||
m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err)
|
||||
}
|
||||
}
|
||||
// single processing
|
||||
case sub.handler != nil:
|
||||
for _, p := range ms {
|
||||
if err = sub.handler(p); err != nil {
|
||||
p.SetError(err)
|
||||
if eh != nil {
|
||||
_ = eh(p)
|
||||
} else if m.opts.Logger.V(logger.ErrorLevel) {
|
||||
m.opts.Logger.Error(m.opts.Context, err.Error())
|
||||
}
|
||||
} else if sub.opts.AutoAck {
|
||||
if err = p.Ack(); err != nil {
|
||||
m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ func TestMemoryBatchBroker(t *testing.T) {
|
||||
t.Fatalf("Unexpected error subscribing %v", err)
|
||||
}
|
||||
|
||||
msgs := make([]*Message, 0, 0)
|
||||
msgs := make([]*Message, 0, count)
|
||||
for i := 0; i < count; i++ {
|
||||
message := &Message{
|
||||
Header: map[string]string{
|
||||
@@ -53,6 +53,7 @@ func TestMemoryBatchBroker(t *testing.T) {
|
||||
t.Fatalf("Unexpected connect error %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemoryBroker(t *testing.T) {
|
||||
b := NewBroker()
|
||||
ctx := context.Background()
|
||||
@@ -73,7 +74,7 @@ func TestMemoryBroker(t *testing.T) {
|
||||
t.Fatalf("Unexpected error subscribing %v", err)
|
||||
}
|
||||
|
||||
msgs := make([]*Message, 0, 0)
|
||||
msgs := make([]*Message, 0, count)
|
||||
for i := 0; i < count; i++ {
|
||||
message := &Message{
|
||||
Header: map[string]string{
|
||||
|
@@ -19,6 +19,8 @@ import (
|
||||
|
||||
// Options holds client options
|
||||
type Options struct {
|
||||
// Transport used for transfer messages
|
||||
Transport transport.Transport
|
||||
// Selector used to select needed address
|
||||
Selector selector.Selector
|
||||
// Logger used to log messages
|
||||
@@ -29,18 +31,16 @@ type Options struct {
|
||||
Broker broker.Broker
|
||||
// Meter used for metrics
|
||||
Meter meter.Meter
|
||||
// Router used to get route
|
||||
Router router.Router
|
||||
// Transport used for transfer messages
|
||||
Transport transport.Transport
|
||||
// Context is used for external options
|
||||
Context context.Context
|
||||
// Lookup func used to get destination addr
|
||||
Lookup LookupFunc
|
||||
// Codecs map
|
||||
Codecs map[string]codec.Codec
|
||||
// Router used to get route
|
||||
Router router.Router
|
||||
// TLSConfig specifies tls.Config for secure connection
|
||||
TLSConfig *tls.Config
|
||||
// Codecs map
|
||||
Codecs map[string]codec.Codec
|
||||
// Lookup func used to get destination addr
|
||||
Lookup LookupFunc
|
||||
// Proxy is used for proxy requests
|
||||
Proxy string
|
||||
// ContentType is used to select codec
|
||||
@@ -68,12 +68,12 @@ func NewCallOptions(opts ...CallOption) CallOptions {
|
||||
|
||||
// CallOptions holds client call options
|
||||
type CallOptions struct {
|
||||
// Router used for route
|
||||
Router router.Router
|
||||
// Selector selects addr
|
||||
Selector selector.Selector
|
||||
// Context used for deadline
|
||||
Context context.Context
|
||||
// Router used for route
|
||||
Router router.Router
|
||||
// Retry func used for retries
|
||||
Retry RetryFunc
|
||||
// Backoff func used for backoff when retry
|
||||
@@ -82,22 +82,22 @@ type CallOptions struct {
|
||||
Network string
|
||||
// Content-Type
|
||||
ContentType string
|
||||
// CallWrappers call wrappers
|
||||
CallWrappers []CallWrapper
|
||||
// SelectOptions selector options
|
||||
SelectOptions []selector.SelectOption
|
||||
// AuthToken string
|
||||
AuthToken string
|
||||
// Address specifies static addr list
|
||||
Address []string
|
||||
// Retries specifies retries num
|
||||
Retries int
|
||||
// SelectOptions selector options
|
||||
SelectOptions []selector.SelectOption
|
||||
// CallWrappers call wrappers
|
||||
CallWrappers []CallWrapper
|
||||
// StreamTimeout stream timeout
|
||||
StreamTimeout time.Duration
|
||||
// RequestTimeout request timeout
|
||||
RequestTimeout time.Duration
|
||||
// DialTimeout dial timeout
|
||||
DialTimeout time.Duration
|
||||
// AuthToken string
|
||||
AuthToken string
|
||||
// Retries specifies retries num
|
||||
Retries int
|
||||
}
|
||||
|
||||
// Context pass context to client
|
||||
@@ -118,12 +118,12 @@ func NewPublishOptions(opts ...PublishOption) PublishOptions {
|
||||
|
||||
// PublishOptions holds publish options
|
||||
type PublishOptions struct {
|
||||
// BodyOnly will publish only message body
|
||||
BodyOnly bool
|
||||
// Context used for external options
|
||||
Context context.Context
|
||||
// Exchange topic exchange name
|
||||
Exchange string
|
||||
// BodyOnly will publish only message body
|
||||
BodyOnly bool
|
||||
}
|
||||
|
||||
// NewMessageOptions creates message options struct
|
||||
@@ -267,7 +267,7 @@ func Transport(t transport.Transport) Option {
|
||||
func Register(r register.Register) Option {
|
||||
return func(o *Options) {
|
||||
if o.Router != nil {
|
||||
o.Router.Init(router.Register(r))
|
||||
_ = o.Router.Init(router.Register(r))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -331,7 +331,7 @@ func TLSConfig(t *tls.Config) Option {
|
||||
// already set. Required for Init call below.
|
||||
|
||||
// set the transport tls
|
||||
o.Transport.Init(
|
||||
_ = o.Transport.Init(
|
||||
transport.TLSConfig(t),
|
||||
)
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ var (
|
||||
|
||||
var (
|
||||
// DefaultMaxMsgSize specifies how much data codec can handle
|
||||
DefaultMaxMsgSize int = 1024 * 1024 * 4 // 4Mb
|
||||
DefaultMaxMsgSize = 1024 * 1024 * 4 // 4Mb
|
||||
// DefaultCodec is the global default codec
|
||||
DefaultCodec Codec = NewCodec()
|
||||
// DefaultTagName specifies struct tag name to control codec Marshal/Unmarshal
|
||||
@@ -58,7 +58,7 @@ type Message struct {
|
||||
Method string
|
||||
Endpoint string
|
||||
Error string
|
||||
Id string
|
||||
ID string
|
||||
Body []byte
|
||||
Type MessageType
|
||||
}
|
||||
@@ -67,3 +67,20 @@ type Message struct {
|
||||
func NewMessage(t MessageType) *Message {
|
||||
return &Message{Type: t, Header: metadata.New(0)}
|
||||
}
|
||||
|
||||
// MarshalAppend calls codec.Marshal(v) and returns the data appended to buf.
|
||||
// If codec implements MarshalAppend, that is called instead.
|
||||
func MarshalAppend(buf []byte, c Codec, v interface{}, opts ...Option) ([]byte, error) {
|
||||
if nc, ok := c.(interface {
|
||||
MarshalAppend([]byte, interface{}, ...Option) ([]byte, error)
|
||||
}); ok {
|
||||
return nc.MarshalAppend(buf, v, opts...)
|
||||
}
|
||||
|
||||
mbuf, err := c.Marshal(v, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(buf, mbuf...), nil
|
||||
}
|
||||
|
@@ -19,12 +19,12 @@ type Options struct {
|
||||
Logger logger.Logger
|
||||
// Tracer used for tracing
|
||||
Tracer tracer.Tracer
|
||||
// MaxMsgSize specifies max messages size that reads by codec
|
||||
MaxMsgSize int
|
||||
// TagName specifies tag name in struct to control codec
|
||||
TagName string
|
||||
// Context stores additional codec options
|
||||
Context context.Context
|
||||
// TagName specifies tag name in struct to control codec
|
||||
TagName string
|
||||
// MaxMsgSize specifies max messages size that reads by codec
|
||||
MaxMsgSize int
|
||||
}
|
||||
|
||||
// MaxMsgSize sets the max message size
|
||||
|
@@ -13,7 +13,7 @@ var DefaultConfig Config = NewConfig()
|
||||
// DefaultWatcherMinInterval default min interval for poll changes
|
||||
var DefaultWatcherMinInterval = 5 * time.Second
|
||||
|
||||
// DefaultWatcherMinInterval default max interval for poll changes
|
||||
// DefaultWatcherMaxInterval default max interval for poll changes
|
||||
var DefaultWatcherMaxInterval = 9 * time.Second
|
||||
|
||||
var (
|
||||
@@ -64,3 +64,53 @@ func Load(ctx context.Context, cs []Config, opts ...LoadOption) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultAfterLoad = func(ctx context.Context, c Config) error {
|
||||
for _, fn := range c.Options().AfterLoad {
|
||||
if err := fn(ctx, c); err != nil {
|
||||
c.Options().Logger.Errorf(ctx, "%s AfterLoad err: %v", c.String(), err)
|
||||
if !c.Options().AllowFail {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
DefaultAfterSave = func(ctx context.Context, c Config) error {
|
||||
for _, fn := range c.Options().AfterSave {
|
||||
if err := fn(ctx, c); err != nil {
|
||||
c.Options().Logger.Errorf(ctx, "%s AfterSave err: %v", c.String(), err)
|
||||
if !c.Options().AllowFail {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
DefaultBeforeLoad = func(ctx context.Context, c Config) error {
|
||||
for _, fn := range c.Options().BeforeLoad {
|
||||
if err := fn(ctx, c); err != nil {
|
||||
c.Options().Logger.Errorf(ctx, "%s BeforeLoad err: %v", c.String(), err)
|
||||
if !c.Options().AllowFail {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
DefaultBeforeSave = func(ctx context.Context, c Config) error {
|
||||
for _, fn := range c.Options().BeforeSave {
|
||||
if err := fn(ctx, c); err != nil {
|
||||
c.Options().Logger.Errorf(ctx, "%s BeforeSavec err: %v", c.String(), err)
|
||||
if !c.Options().AllowFail {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
)
|
||||
|
@@ -27,10 +27,8 @@ func (c *defaultConfig) Init(opts ...Option) error {
|
||||
}
|
||||
|
||||
func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error {
|
||||
for _, fn := range c.opts.BeforeLoad {
|
||||
if err := fn(ctx, c); err != nil && !c.opts.AllowFail {
|
||||
return err
|
||||
}
|
||||
if err := DefaultBeforeLoad(ctx, c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options := NewLoadOptions(opts...)
|
||||
@@ -48,20 +46,26 @@ func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error {
|
||||
}
|
||||
|
||||
src, err := rutil.Zero(dst)
|
||||
if err == nil {
|
||||
if err = fillValues(reflect.ValueOf(src), c.opts.StructTag); err == nil {
|
||||
err = mergo.Merge(dst, src, mopts...)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil && !c.opts.AllowFail {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fn := range c.opts.AfterLoad {
|
||||
if err := fn(ctx, c); err != nil && !c.opts.AllowFail {
|
||||
if err != nil {
|
||||
if !c.opts.AllowFail {
|
||||
return err
|
||||
}
|
||||
return DefaultAfterLoad(ctx, c)
|
||||
}
|
||||
|
||||
if err = fillValues(reflect.ValueOf(src), c.opts.StructTag); err == nil {
|
||||
err = mergo.Merge(dst, src, mopts...)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.opts.Logger.Errorf(ctx, "default load error: %v", err)
|
||||
if !c.opts.AllowFail {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := DefaultAfterLoad(ctx, c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -247,16 +251,12 @@ func fillValues(valueOf reflect.Value, tname string) error {
|
||||
}
|
||||
|
||||
func (c *defaultConfig) Save(ctx context.Context, opts ...SaveOption) error {
|
||||
for _, fn := range c.opts.BeforeSave {
|
||||
if err := fn(ctx, c); err != nil && !c.opts.AllowFail {
|
||||
return err
|
||||
}
|
||||
if err := DefaultBeforeSave(ctx, c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fn := range c.opts.AfterSave {
|
||||
if err := fn(ctx, c); err != nil && !c.opts.AllowFail {
|
||||
return err
|
||||
}
|
||||
if err := DefaultAfterSave(ctx, c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -48,5 +48,5 @@ func TestDefault(t *testing.T) {
|
||||
t.Fatal("AfterLoad option not working")
|
||||
}
|
||||
_ = conf
|
||||
//t.Logf("%#+v\n", conf)
|
||||
// t.Logf("%#+v\n", conf)
|
||||
}
|
||||
|
@@ -209,14 +209,14 @@ func Name(n string) Option {
|
||||
type WatchOptions struct {
|
||||
// Context used by non default options
|
||||
Context context.Context
|
||||
// Coalesce multiple events to one
|
||||
Coalesce bool
|
||||
// Struct for filling
|
||||
Struct interface{}
|
||||
// MinInterval specifies the min time.Duration interval for poll changes
|
||||
MinInterval time.Duration
|
||||
// MaxInterval specifies the max time.Duration interval for poll changes
|
||||
MaxInterval time.Duration
|
||||
// Struct for filling
|
||||
Struct interface{}
|
||||
// Coalesce multiple events to one
|
||||
Coalesce bool
|
||||
}
|
||||
|
||||
type WatchOption func(*WatchOptions)
|
||||
|
@@ -37,8 +37,8 @@ var (
|
||||
|
||||
// Error type
|
||||
type Error struct {
|
||||
// Id holds error id or service, usually someting like my_service or id
|
||||
Id string
|
||||
// ID holds error id or service, usually someting like my_service or id
|
||||
ID string
|
||||
// Detail holds some useful details about error
|
||||
Detail string
|
||||
// Status usually holds text of http status
|
||||
@@ -56,7 +56,7 @@ func (e *Error) Error() string {
|
||||
// New generates a custom error
|
||||
func New(id, detail string, code int32) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: code,
|
||||
Detail: detail,
|
||||
Status: http.StatusText(int(code)),
|
||||
@@ -77,7 +77,7 @@ func Parse(err string) *Error {
|
||||
// BadRequest generates a 400 error.
|
||||
func BadRequest(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 400,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(400),
|
||||
@@ -87,7 +87,7 @@ func BadRequest(id, format string, a ...interface{}) error {
|
||||
// Unauthorized generates a 401 error.
|
||||
func Unauthorized(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 401,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(401),
|
||||
@@ -97,7 +97,7 @@ func Unauthorized(id, format string, a ...interface{}) error {
|
||||
// Forbidden generates a 403 error.
|
||||
func Forbidden(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 403,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(403),
|
||||
@@ -107,7 +107,7 @@ func Forbidden(id, format string, a ...interface{}) error {
|
||||
// NotFound generates a 404 error.
|
||||
func NotFound(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 404,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(404),
|
||||
@@ -117,7 +117,7 @@ func NotFound(id, format string, a ...interface{}) error {
|
||||
// MethodNotAllowed generates a 405 error.
|
||||
func MethodNotAllowed(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 405,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(405),
|
||||
@@ -127,7 +127,7 @@ func MethodNotAllowed(id, format string, a ...interface{}) error {
|
||||
// Timeout generates a 408 error.
|
||||
func Timeout(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 408,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(408),
|
||||
@@ -137,7 +137,7 @@ func Timeout(id, format string, a ...interface{}) error {
|
||||
// Conflict generates a 409 error.
|
||||
func Conflict(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 409,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(409),
|
||||
@@ -147,7 +147,7 @@ func Conflict(id, format string, a ...interface{}) error {
|
||||
// InternalServerError generates a 500 error.
|
||||
func InternalServerError(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 500,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(500),
|
||||
@@ -157,7 +157,7 @@ func InternalServerError(id, format string, a ...interface{}) error {
|
||||
// NotImplemented generates a 501 error
|
||||
func NotImplemented(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 501,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(501),
|
||||
@@ -167,7 +167,7 @@ func NotImplemented(id, format string, a ...interface{}) error {
|
||||
// BadGateway generates a 502 error
|
||||
func BadGateway(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 502,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(502),
|
||||
@@ -177,7 +177,7 @@ func BadGateway(id, format string, a ...interface{}) error {
|
||||
// ServiceUnavailable generates a 503 error
|
||||
func ServiceUnavailable(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 503,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(503),
|
||||
@@ -187,7 +187,7 @@ func ServiceUnavailable(id, format string, a ...interface{}) error {
|
||||
// GatewayTimeout generates a 504 error
|
||||
func GatewayTimeout(id, format string, a ...interface{}) error {
|
||||
return &Error{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Code: 504,
|
||||
Detail: fmt.Sprintf(format, a...),
|
||||
Status: http.StatusText(504),
|
||||
|
@@ -9,12 +9,12 @@ import (
|
||||
func TestFromError(t *testing.T) {
|
||||
err := NotFound("go.micro.test", "%s", "example")
|
||||
merr := FromError(err)
|
||||
if merr.Id != "go.micro.test" || merr.Code != 404 {
|
||||
if merr.ID != "go.micro.test" || merr.Code != 404 {
|
||||
t.Fatalf("invalid conversation %v != %v", err, merr)
|
||||
}
|
||||
err = er.New(err.Error())
|
||||
merr = FromError(err)
|
||||
if merr.Id != "go.micro.test" || merr.Code != 404 {
|
||||
if merr.ID != "go.micro.test" || merr.Code != 404 {
|
||||
t.Fatalf("invalid conversation %v != %v", err, merr)
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ func TestEqual(t *testing.T) {
|
||||
func TestErrors(t *testing.T) {
|
||||
testData := []*Error{
|
||||
{
|
||||
Id: "test",
|
||||
ID: "test",
|
||||
Code: 500,
|
||||
Detail: "Internal server error",
|
||||
Status: http.StatusText(500),
|
||||
@@ -44,7 +44,7 @@ func TestErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, e := range testData {
|
||||
ne := New(e.Id, e.Detail, e.Code)
|
||||
ne := New(e.ID, e.Detail, e.Code)
|
||||
|
||||
if e.Error() != ne.Error() {
|
||||
t.Fatalf("Expected %s got %s", e.Error(), ne.Error())
|
||||
@@ -56,8 +56,8 @@ func TestErrors(t *testing.T) {
|
||||
t.Fatalf("Expected error got nil %v", pe)
|
||||
}
|
||||
|
||||
if pe.Id != e.Id {
|
||||
t.Fatalf("Expected %s got %s", e.Id, pe.Id)
|
||||
if pe.ID != e.ID {
|
||||
t.Fatalf("Expected %s got %s", e.ID, pe.ID)
|
||||
}
|
||||
|
||||
if pe.Detail != e.Detail {
|
||||
|
@@ -20,13 +20,13 @@ type microFlow struct {
|
||||
}
|
||||
|
||||
type microWorkflow struct {
|
||||
id string
|
||||
g *dag.AcyclicGraph
|
||||
init bool
|
||||
sync.RWMutex
|
||||
opts Options
|
||||
g *dag.AcyclicGraph
|
||||
steps map[string]Step
|
||||
id string
|
||||
status Status
|
||||
sync.RWMutex
|
||||
init bool
|
||||
}
|
||||
|
||||
func (w *microWorkflow) ID() string {
|
||||
@@ -241,6 +241,7 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu
|
||||
w.opts.Logger.Tracef(nctx, "will be executed %v", steps[idx][nidx])
|
||||
}
|
||||
cstep := steps[idx][nidx]
|
||||
// nolint: nestif
|
||||
if len(cstep.Requires()) == 0 {
|
||||
wg.Add(1)
|
||||
go func(step Step) {
|
||||
@@ -264,17 +265,16 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu
|
||||
}
|
||||
cherr <- serr
|
||||
return
|
||||
} else {
|
||||
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "rsp"), rsp); werr != nil {
|
||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||
cherr <- werr
|
||||
return
|
||||
}
|
||||
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "status"), &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||
cherr <- werr
|
||||
return
|
||||
}
|
||||
}
|
||||
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "rsp"), rsp); werr != nil {
|
||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||
cherr <- werr
|
||||
return
|
||||
}
|
||||
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "status"), &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||
cherr <- werr
|
||||
return
|
||||
}
|
||||
}(cstep)
|
||||
wg.Wait()
|
||||
@@ -298,16 +298,15 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu
|
||||
}
|
||||
cherr <- serr
|
||||
return
|
||||
} else {
|
||||
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "rsp"), rsp); werr != nil {
|
||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||
cherr <- werr
|
||||
return
|
||||
}
|
||||
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "status"), &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||
cherr <- werr
|
||||
return
|
||||
}
|
||||
}
|
||||
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "rsp"), rsp); werr != nil {
|
||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||
cherr <- werr
|
||||
return
|
||||
}
|
||||
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "status"), &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||
cherr <- werr
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,17 +336,14 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu
|
||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusAborted.String())}); werr != nil {
|
||||
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||
}
|
||||
break
|
||||
case err == nil:
|
||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||
}
|
||||
break
|
||||
case err != nil:
|
||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil {
|
||||
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return eid, err
|
||||
@@ -429,11 +425,11 @@ func (f *microFlow) WorkflowLoad(ctx context.Context, id string) (Workflow, erro
|
||||
}
|
||||
|
||||
type microCallStep struct {
|
||||
opts StepOptions
|
||||
service string
|
||||
method string
|
||||
rsp *Message
|
||||
req *Message
|
||||
service string
|
||||
method string
|
||||
opts StepOptions
|
||||
status Status
|
||||
}
|
||||
|
||||
@@ -499,10 +495,12 @@ func (s *microCallStep) Execute(ctx context.Context, req *Message, opts ...Execu
|
||||
rsp := &codec.Frame{}
|
||||
copts := []client.CallOption{client.WithRetries(0)}
|
||||
if options.Timeout > 0 {
|
||||
copts = append(copts, client.WithRequestTimeout(options.Timeout), client.WithDialTimeout(options.Timeout))
|
||||
copts = append(copts,
|
||||
client.WithRequestTimeout(options.Timeout),
|
||||
client.WithDialTimeout(options.Timeout))
|
||||
}
|
||||
nctx := metadata.NewOutgoingContext(ctx, req.Header)
|
||||
err := options.Client.Call(nctx, options.Client.NewRequest(s.service, s.method, &codec.Frame{Data: req.Body}), rsp)
|
||||
err := options.Client.Call(nctx, options.Client.NewRequest(s.service, s.method, &codec.Frame{Data: req.Body}), rsp, copts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -511,10 +509,10 @@ func (s *microCallStep) Execute(ctx context.Context, req *Message, opts ...Execu
|
||||
}
|
||||
|
||||
type microPublishStep struct {
|
||||
opts StepOptions
|
||||
topic string
|
||||
req *Message
|
||||
rsp *Message
|
||||
topic string
|
||||
opts StepOptions
|
||||
status Status
|
||||
}
|
||||
|
||||
@@ -553,7 +551,7 @@ func (s *microPublishStep) String() string {
|
||||
if s.opts.ID != "" {
|
||||
return s.opts.ID
|
||||
}
|
||||
return fmt.Sprintf("%s", s.topic)
|
||||
return s.topic
|
||||
}
|
||||
|
||||
func (s *microPublishStep) Name() string {
|
||||
|
@@ -96,8 +96,8 @@ type WorkflowOption func(*WorkflowOptions)
|
||||
|
||||
// WorkflowOptions holds workflow options
|
||||
type WorkflowOptions struct {
|
||||
ID string
|
||||
Context context.Context
|
||||
ID string
|
||||
}
|
||||
|
||||
// WorkflowID set workflow id
|
||||
@@ -120,10 +120,10 @@ type ExecuteOptions struct {
|
||||
Context context.Context
|
||||
// Start step
|
||||
Start string
|
||||
// Reverse execution
|
||||
Reverse bool
|
||||
// Timeout for execution
|
||||
Timeout time.Duration
|
||||
// Reverse execution
|
||||
Reverse bool
|
||||
// Async enables async execution
|
||||
Async bool
|
||||
}
|
||||
@@ -193,10 +193,10 @@ func NewExecuteOptions(opts ...ExecuteOption) ExecuteOptions {
|
||||
}
|
||||
|
||||
type StepOptions struct {
|
||||
ID string
|
||||
Context context.Context
|
||||
Requires []string
|
||||
Fallback string
|
||||
ID string
|
||||
Requires []string
|
||||
}
|
||||
|
||||
type StepOption func(*StepOptions)
|
||||
|
7
go.mod
7
go.mod
@@ -4,10 +4,11 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/ef-ds/deque v1.0.4
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34
|
||||
github.com/silas/dag v0.0.0-20210626123444-3804bac2d6d4
|
||||
github.com/unistack-org/micro-proto v0.0.9
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
)
|
||||
|
13
go.sum
13
go.sum
@@ -2,6 +2,8 @@ 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=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
@@ -11,17 +13,22 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR
|
||||
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-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
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=
|
||||
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=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
|
@@ -12,11 +12,11 @@ import (
|
||||
)
|
||||
|
||||
type defaultLogger struct {
|
||||
enc *json.Encoder
|
||||
opts Options
|
||||
sync.RWMutex
|
||||
enc *json.Encoder
|
||||
logFunc LogFunc
|
||||
logfFunc LogfFunc
|
||||
opts Options
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// Init(opts...) should only overwrite provided options
|
||||
@@ -81,7 +81,7 @@ func (l *defaultLogger) Fields(fields ...interface{}) Logger {
|
||||
} else if len(fields)%2 != 0 {
|
||||
fields = fields[:len(fields)-1]
|
||||
}
|
||||
nl.opts.Fields = append(l.opts.Fields, fields...)
|
||||
nl.opts.Fields = append(nl.opts.Fields, fields...)
|
||||
return nl
|
||||
}
|
||||
|
||||
|
@@ -19,12 +19,12 @@ type Options struct {
|
||||
Fields []interface{}
|
||||
// Name holds the logger name
|
||||
Name string
|
||||
// CallerSkipCount number of frmaes to skip
|
||||
CallerSkipCount int
|
||||
// The logging level the logger should log
|
||||
Level Level
|
||||
// Wrappers logger wrapper that called before actual Log/Logf function
|
||||
Wrappers []Wrapper
|
||||
// The logging level the logger should log
|
||||
Level Level
|
||||
// CallerSkipCount number of frmaes to skip
|
||||
CallerSkipCount int
|
||||
}
|
||||
|
||||
// NewOptions creates new options struct
|
||||
|
@@ -125,23 +125,29 @@ func getArgs(args []interface{}) []interface{} {
|
||||
var err error
|
||||
for _, arg := range args {
|
||||
val := reflect.ValueOf(arg)
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
if val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
}
|
||||
narg := arg
|
||||
if val.Kind() == reflect.Struct {
|
||||
if narg, err = rutil.Zero(arg); err == nil {
|
||||
rutil.CopyDefaults(narg, arg)
|
||||
if flds, ferr := rutil.StructFields(narg); ferr == nil {
|
||||
for _, fld := range flds {
|
||||
if tv, ok := fld.Field.Tag.Lookup("logger"); ok && tv == "omit" {
|
||||
fld.Value.Set(reflect.Zero(fld.Value.Type()))
|
||||
}
|
||||
}
|
||||
if val.Kind() != reflect.Struct {
|
||||
nargs = append(nargs, narg)
|
||||
continue
|
||||
}
|
||||
|
||||
if narg, err = rutil.Zero(arg); err != nil {
|
||||
nargs = append(nargs, narg)
|
||||
continue
|
||||
}
|
||||
|
||||
rutil.CopyDefaults(narg, arg)
|
||||
if flds, ferr := rutil.StructFields(narg); ferr == nil {
|
||||
for _, fld := range flds {
|
||||
if tv, ok := fld.Field.Tag.Lookup("logger"); ok && tv == "omit" {
|
||||
fld.Value.Set(reflect.Zero(fld.Value.Type()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nargs = append(nargs, narg)
|
||||
}
|
||||
return nargs
|
||||
|
@@ -107,13 +107,16 @@ func New(size int) Metadata {
|
||||
|
||||
// Merge merges metadata to existing metadata, overwriting if specified
|
||||
func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
|
||||
var ok bool
|
||||
nmd := Copy(omd)
|
||||
for key, val := range mmd {
|
||||
if _, ok := nmd[key]; ok && !overwrite {
|
||||
// skip
|
||||
} else if val != "" {
|
||||
_, ok = nmd[key]
|
||||
switch {
|
||||
case ok && !overwrite:
|
||||
continue
|
||||
case val != "":
|
||||
nmd.Set(key, val)
|
||||
} else {
|
||||
case ok && val == "":
|
||||
nmd.Del(key)
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,10 @@ 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"
|
||||
|
||||
import (
|
||||
|
||||
// import required packages
|
||||
_ "github.com/unistack-org/micro-proto/api"
|
||||
|
||||
// import required packages
|
||||
_ "github.com/unistack-org/micro-proto/openapiv2"
|
||||
)
|
||||
|
@@ -6,6 +6,7 @@ package handler
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
api "github.com/unistack-org/micro/v3/api"
|
||||
codec "github.com/unistack-org/micro/v3/codec"
|
||||
)
|
||||
|
@@ -6,6 +6,7 @@ package handler
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
api "github.com/unistack-org/micro/v3/api"
|
||||
codec "github.com/unistack-org/micro/v3/codec"
|
||||
server "github.com/unistack-org/micro/v3/server"
|
||||
|
@@ -33,7 +33,7 @@ func TestBuildLabels(t *testing.T) {
|
||||
}
|
||||
|
||||
data := []testData{
|
||||
testData{
|
||||
{
|
||||
src: []string{"zerolabel", "value3", "firstlabel", "value2"},
|
||||
dst: []string{"firstlabel", "value2", "zerolabel", "value3"},
|
||||
},
|
||||
@@ -48,15 +48,15 @@ func TestBuildLabels(t *testing.T) {
|
||||
|
||||
func TestBuildName(t *testing.T) {
|
||||
data := map[string][]string{
|
||||
`my_metric{firstlabel="value2",zerolabel="value3"}`: []string{
|
||||
`my_metric{firstlabel="value2",zerolabel="value3"}`: {
|
||||
"my_metric",
|
||||
"zerolabel", "value3", "firstlabel", "value2",
|
||||
},
|
||||
`my_metric{broker="broker2",register="mdns",server="tcp"}`: []string{
|
||||
`my_metric{broker="broker2",register="mdns",server="tcp"}`: {
|
||||
"my_metric",
|
||||
"broker", "broker1", "broker", "broker2", "server", "http", "server", "tcp", "register", "mdns",
|
||||
},
|
||||
`my_metric{aaa="aaa"}`: []string{
|
||||
`my_metric{aaa="aaa"}`: {
|
||||
"my_metric",
|
||||
"aaa", "aaa",
|
||||
},
|
||||
|
@@ -27,8 +27,8 @@ type Options struct {
|
||||
Tracer tracer.Tracer
|
||||
// Tunnel used for transfer data
|
||||
Tunnel tunnel.Tunnel
|
||||
// Id of the node
|
||||
Id string
|
||||
// ID of the node
|
||||
ID string
|
||||
// Name of the network
|
||||
Name string
|
||||
// Address to bind to
|
||||
@@ -39,10 +39,10 @@ type Options struct {
|
||||
Nodes []string
|
||||
}
|
||||
|
||||
// Id sets the id of the network node
|
||||
func Id(id string) Option {
|
||||
// ID sets the id of the network node
|
||||
func ID(id string) Option {
|
||||
return func(o *Options) {
|
||||
o.Id = id
|
||||
o.ID = id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func Tracer(t tracer.Tracer) Option {
|
||||
// NewOptions returns network default options
|
||||
func NewOptions(opts ...Option) Options {
|
||||
options := Options{
|
||||
Id: id.Must(),
|
||||
ID: id.Must(),
|
||||
Name: "go.micro",
|
||||
Address: ":0",
|
||||
Logger: logger.DefaultLogger,
|
||||
|
@@ -16,12 +16,14 @@ func TestMemoryTransport(t *testing.T) {
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
cherr := make(chan error, 1)
|
||||
// accept
|
||||
go func() {
|
||||
if err := l.Accept(func(sock Socket) {
|
||||
if nerr := l.Accept(func(sock Socket) {
|
||||
for {
|
||||
var m Message
|
||||
if err := sock.Recv(&m); err != nil {
|
||||
if rerr := sock.Recv(&m); rerr != nil {
|
||||
cherr <- rerr
|
||||
return
|
||||
}
|
||||
if len(os.Getenv("INTEGRATION_TESTS")) == 0 {
|
||||
@@ -30,11 +32,12 @@ func TestMemoryTransport(t *testing.T) {
|
||||
if cerr := sock.Send(&Message{
|
||||
Body: []byte(`pong`),
|
||||
}); cerr != nil {
|
||||
cherr <- cerr
|
||||
return
|
||||
}
|
||||
}
|
||||
}); err != nil {
|
||||
t.Fatalf("Unexpected error accepting %v", err)
|
||||
}); nerr != nil {
|
||||
cherr <- err
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -45,19 +48,24 @@ func TestMemoryTransport(t *testing.T) {
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
// send <=> receive
|
||||
for i := 0; i < 3; i++ {
|
||||
if err := c.Send(&Message{
|
||||
Body: []byte(`ping`),
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
var m Message
|
||||
if err := c.Recv(&m); err != nil {
|
||||
return
|
||||
}
|
||||
if len(os.Getenv("INTEGRATION_TESTS")) == 0 {
|
||||
t.Logf("Client Received %s", string(m.Body))
|
||||
select {
|
||||
case err := <-cherr:
|
||||
t.Fatal(err)
|
||||
default:
|
||||
// send <=> receive
|
||||
for i := 0; i < 3; i++ {
|
||||
if err := c.Send(&Message{
|
||||
Body: []byte(`ping`),
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
var m Message
|
||||
if err := c.Recv(&m); err != nil {
|
||||
return
|
||||
}
|
||||
if len(os.Getenv("INTEGRATION_TESTS")) == 0 {
|
||||
t.Logf("Client Received %s", string(m.Body))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -34,9 +34,9 @@ type tunBatchSubscriber struct {
|
||||
}
|
||||
|
||||
type tunEvent struct {
|
||||
err error
|
||||
message *broker.Message
|
||||
topic string
|
||||
err error
|
||||
}
|
||||
|
||||
// used to access tunnel from options context
|
||||
@@ -82,7 +82,7 @@ func (t *tunBroker) BatchPublish(ctx context.Context, msgs []*broker.Message, op
|
||||
topic, _ := msg.Header.Get(metadata.HeaderTopic)
|
||||
c, ok := topicMap[topic]
|
||||
if !ok {
|
||||
c, err := t.tunnel.Dial(ctx, topic, tunnel.DialMode(tunnel.Multicast))
|
||||
c, err = t.tunnel.Dial(ctx, topic, tunnel.DialMode(tunnel.Multicast))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -199,7 +199,9 @@ func (t *tunBatchSubscriber) run() {
|
||||
},
|
||||
}}
|
||||
// handle the message
|
||||
go t.handler(evts)
|
||||
go func() {
|
||||
_ = t.handler(evts)
|
||||
}()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -235,13 +237,15 @@ func (t *tunSubscriber) run() {
|
||||
c.Close()
|
||||
|
||||
// handle the message
|
||||
go t.handler(&tunEvent{
|
||||
topic: t.topic,
|
||||
message: &broker.Message{
|
||||
Header: m.Header,
|
||||
Body: m.Body,
|
||||
},
|
||||
})
|
||||
go func() {
|
||||
_ = t.handler(&tunEvent{
|
||||
topic: t.topic,
|
||||
message: &broker.Message{
|
||||
Header: m.Header,
|
||||
Body: m.Body,
|
||||
},
|
||||
})
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -37,7 +37,7 @@ func (t *tunTransport) Init(opts ...transport.Option) error {
|
||||
// get the transport
|
||||
tr, ok := t.options.Context.Value(transportKey{}).(transport.Transport)
|
||||
if ok {
|
||||
tun.Init(tunnel.Transport(tr))
|
||||
_ = tun.Init(tunnel.Transport(tr))
|
||||
}
|
||||
|
||||
// set the tunnel
|
||||
|
@@ -31,7 +31,7 @@ func (p *profiler) writeHeap(f *os.File) {
|
||||
select {
|
||||
case <-t.C:
|
||||
runtime.GC()
|
||||
pprof.WriteHeapProfile(f)
|
||||
_ = pprof.WriteHeapProfile(f)
|
||||
case <-p.exit:
|
||||
return
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ func NewOptions(opts ...Option) Options {
|
||||
return options
|
||||
}
|
||||
|
||||
// nolint: golint
|
||||
// nolint: golint,revive
|
||||
// RegisterOptions holds options for register method
|
||||
type RegisterOptions struct {
|
||||
Context context.Context
|
||||
@@ -197,7 +197,7 @@ func TLSConfig(t *tls.Config) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: golint
|
||||
// nolint: golint,revive
|
||||
// RegisterAttempts specifies register atempts count
|
||||
func RegisterAttempts(t int) RegisterOption {
|
||||
return func(o *RegisterOptions) {
|
||||
@@ -205,7 +205,7 @@ func RegisterAttempts(t int) RegisterOption {
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: golint
|
||||
// nolint: golint,revive
|
||||
// RegisterTTL specifies register ttl
|
||||
func RegisterTTL(t time.Duration) RegisterOption {
|
||||
return func(o *RegisterOptions) {
|
||||
@@ -213,7 +213,7 @@ func RegisterTTL(t time.Duration) RegisterOption {
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: golint
|
||||
// nolint: golint,revive
|
||||
// RegisterContext sets the register context
|
||||
func RegisterContext(ctx context.Context) RegisterOption {
|
||||
return func(o *RegisterOptions) {
|
||||
@@ -221,7 +221,7 @@ func RegisterContext(ctx context.Context) RegisterOption {
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: golint
|
||||
// nolint: golint,revive
|
||||
// RegisterDomain secifies register domain
|
||||
func RegisterDomain(d string) RegisterOption {
|
||||
return func(o *RegisterOptions) {
|
||||
|
@@ -68,8 +68,8 @@ type Endpoint struct {
|
||||
// Option func signature
|
||||
type Option func(*Options)
|
||||
|
||||
// nolint: golint,revive
|
||||
// RegisterOption option is used to register service
|
||||
// nolint: golint
|
||||
type RegisterOption func(*RegisterOptions)
|
||||
|
||||
// WatchOption option is used to watch service changes
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/unistack-org/micro/v3/resolver"
|
||||
)
|
||||
|
||||
// nolint: golint,revive
|
||||
// HTTPResolver is a HTTP network resolver
|
||||
type HTTPResolver struct {
|
||||
// Proto if not set, defaults to http
|
||||
@@ -53,6 +54,7 @@ func (r *HTTPResolver) Resolve(name string) ([]*resolver.Record, error) {
|
||||
q.Set("name", name)
|
||||
uri.RawQuery = q.Encode()
|
||||
|
||||
// nolint: noctx
|
||||
rsp, err := http.Get(uri.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -40,8 +40,9 @@ func (d *dns) Lookup(opts ...QueryOption) ([]Route, error) {
|
||||
// check to see if we have the port provided in the service, e.g. go-micro-srv-foo:8000
|
||||
host, port, err := net.SplitHostPort(options.Service)
|
||||
if err == nil {
|
||||
var ips []string
|
||||
// lookup the service using A records
|
||||
ips, err := net.LookupHost(host)
|
||||
ips, err = net.LookupHost(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -16,15 +16,15 @@ type Options struct {
|
||||
Name string
|
||||
Gateway string
|
||||
Network string
|
||||
Id string
|
||||
ID string
|
||||
Address string
|
||||
Precache bool
|
||||
}
|
||||
|
||||
// Id sets Router Id
|
||||
func Id(id string) Option {
|
||||
// ID sets Router Id
|
||||
func ID(id string) Option {
|
||||
return func(o *Options) {
|
||||
o.Id = id
|
||||
o.ID = id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func Name(n string) Option {
|
||||
// NewOptions returns router default options
|
||||
func NewOptions(opts ...Option) Options {
|
||||
options := Options{
|
||||
Id: id.Must(),
|
||||
ID: id.Must(),
|
||||
Network: DefaultNetwork,
|
||||
Register: register.DefaultRegister,
|
||||
Logger: logger.DefaultLogger,
|
||||
|
@@ -38,8 +38,8 @@ func (t EventType) String() string {
|
||||
type Event struct {
|
||||
// Timestamp is event timestamp
|
||||
Timestamp time.Time
|
||||
// Id of the event
|
||||
Id string
|
||||
// ID of the event
|
||||
ID string
|
||||
// Route is table route
|
||||
Route Route
|
||||
// Type defines type of event
|
||||
|
@@ -13,7 +13,7 @@ func TestError(t *testing.T) {
|
||||
if !ok {
|
||||
t.Fatal("error not *errors.Error")
|
||||
}
|
||||
if merr.Id != "svc1" {
|
||||
if merr.ID != "svc1" {
|
||||
t.Fatal("id != svc1")
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,10 @@ 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"
|
||||
|
||||
import (
|
||||
|
||||
// import required packages
|
||||
_ "github.com/unistack-org/micro-proto/api"
|
||||
|
||||
// import required packages
|
||||
_ "github.com/unistack-org/micro-proto/openapiv2"
|
||||
)
|
||||
|
@@ -6,6 +6,7 @@ package health
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
api "github.com/unistack-org/micro/v3/api"
|
||||
codec "github.com/unistack-org/micro/v3/codec"
|
||||
)
|
||||
|
@@ -6,6 +6,7 @@ package health
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
api "github.com/unistack-org/micro/v3/api"
|
||||
codec "github.com/unistack-org/micro/v3/codec"
|
||||
server "github.com/unistack-org/micro/v3/server"
|
||||
|
@@ -190,7 +190,7 @@ func (n *noopServer) Register() error {
|
||||
}
|
||||
|
||||
// register the service
|
||||
if err := DefaultRegisterFunc(service, config); err != nil {
|
||||
if err = DefaultRegisterFunc(service, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@@ -20,12 +20,8 @@ type TestMessage struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
var (
|
||||
numMsg int = 8
|
||||
)
|
||||
|
||||
func (h *TestHandler) SingleSubHandler(ctx context.Context, msg *codec.Frame) error {
|
||||
//fmt.Printf("msg %s\n", msg.Data)
|
||||
// fmt.Printf("msg %s\n", msg.Data)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -262,7 +262,7 @@ func TLSConfig(t *tls.Config) Option {
|
||||
// already set. Required for Init call below.
|
||||
|
||||
// set the transport tls
|
||||
o.Transport.Init(
|
||||
_ = o.Transport.Init(
|
||||
transport.TLSConfig(t),
|
||||
)
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/unistack-org/micro/v3/broker"
|
||||
"github.com/unistack-org/micro/v3/codec"
|
||||
"github.com/unistack-org/micro/v3/errors"
|
||||
"github.com/unistack-org/micro/v3/logger"
|
||||
"github.com/unistack-org/micro/v3/metadata"
|
||||
@@ -33,13 +34,13 @@ type handler struct {
|
||||
}
|
||||
|
||||
type subscriber struct {
|
||||
opts SubscriberOptions
|
||||
typ reflect.Type
|
||||
subscriber interface{}
|
||||
rcvr reflect.Value
|
||||
topic string
|
||||
handlers []*handler
|
||||
endpoints []*register.Endpoint
|
||||
handlers []*handler
|
||||
opts SubscriberOptions
|
||||
}
|
||||
|
||||
// Is this an exported - upper case - name?
|
||||
@@ -72,7 +73,7 @@ func ValidateSubscriber(sub Subscriber) error {
|
||||
if argType.Kind() != reflect.Slice {
|
||||
return fmt.Errorf("subscriber %v dont have required signature %s", name, batchSubSig)
|
||||
}
|
||||
if strings.Compare(fmt.Sprintf("%s", argType), "[]interface{}") == 0 {
|
||||
if strings.Compare(fmt.Sprintf("%v", argType), "[]interface{}") == 0 {
|
||||
return fmt.Errorf("subscriber %v dont have required signaure %s", name, batchSubSig)
|
||||
}
|
||||
}
|
||||
@@ -244,9 +245,9 @@ func (n *noopServer) newBatchSubHandler(sb *subscriber, opts Options) broker.Bat
|
||||
}
|
||||
|
||||
reqType := handler.reqType
|
||||
|
||||
var cf codec.Codec
|
||||
for _, msg := range msgs {
|
||||
cf, err := n.newCodec(msg.ContentType())
|
||||
cf, err = n.newCodec(msg.ContentType())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -32,8 +32,8 @@ type Options struct {
|
||||
Namespace string
|
||||
// Addrs contains store address
|
||||
Addrs []string
|
||||
//Wrappers store wrapper that called before actual functions
|
||||
//Wrappers []Wrapper
|
||||
// Wrappers store wrapper that called before actual functions
|
||||
// Wrappers []Wrapper
|
||||
}
|
||||
|
||||
// NewOptions creates options struct
|
||||
@@ -339,9 +339,11 @@ func ExistsNamespace(ns string) ExistsOption {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// WrapStore adds a store Wrapper to a list of options passed into the store
|
||||
//func WrapStore(w Wrapper) Option {
|
||||
// return func(o *Options) {
|
||||
// o.Wrappers = append(o.Wrappers, w)
|
||||
// }
|
||||
//}
|
||||
func WrapStore(w Wrapper) Option {
|
||||
return func(o *Options) {
|
||||
o.Wrappers = append(o.Wrappers, w)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@@ -5,21 +5,19 @@ import (
|
||||
)
|
||||
|
||||
// LogfFunc function used for Logf method
|
||||
//type LogfFunc func(ctx context.Context, level Level, msg string, args ...interface{})
|
||||
|
||||
//type Wrapper interface {
|
||||
// Logf logs message with needed level
|
||||
//Logf(LogfFunc) LogfFunc
|
||||
//}
|
||||
// type LogfFunc func(ctx context.Context, level Level, msg string, args ...interface{})
|
||||
// type Wrapper interface {
|
||||
// Logf logs message with needed level
|
||||
// Logf(LogfFunc) LogfFunc
|
||||
// }
|
||||
|
||||
// NamespaceStore wrap store with namespace
|
||||
type NamespaceStore struct {
|
||||
s Store
|
||||
ns string
|
||||
}
|
||||
|
||||
var (
|
||||
_ Store = &NamespaceStore{}
|
||||
)
|
||||
var _ Store = &NamespaceStore{}
|
||||
|
||||
func NewNamespaceStore(s Store, ns string) Store {
|
||||
return &NamespaceStore{s: s, ns: ns}
|
||||
@@ -69,11 +67,11 @@ func (w *NamespaceStore) String() string {
|
||||
return w.s.String()
|
||||
}
|
||||
|
||||
//type NamespaceWrapper struct{}
|
||||
// type NamespaceWrapper struct{}
|
||||
|
||||
//func NewNamespaceWrapper() Wrapper {
|
||||
// func NewNamespaceWrapper() Wrapper {
|
||||
// return &NamespaceWrapper{}
|
||||
//}
|
||||
// }
|
||||
|
||||
/*
|
||||
func (w *OmitWrapper) Logf(fn LogfFunc) LogfFunc {
|
||||
|
@@ -51,7 +51,7 @@ func (m *memorySync) Leader(id string, opts ...LeaderOption) (Leader, error) {
|
||||
id: id,
|
||||
resign: func(id string) error {
|
||||
once.Do(func() {
|
||||
m.Unlock(id)
|
||||
_ = m.Unlock(id)
|
||||
})
|
||||
return nil
|
||||
},
|
||||
|
@@ -48,7 +48,7 @@ func Verify(a auth.Auth) error {
|
||||
}
|
||||
|
||||
// set the credentials and token in auth options
|
||||
a.Init(
|
||||
_ = a.Init(
|
||||
auth.ClientToken(token),
|
||||
auth.Credentials(accID, accSecret),
|
||||
)
|
||||
@@ -79,7 +79,7 @@ func Verify(a auth.Auth) error {
|
||||
}
|
||||
|
||||
// set the token
|
||||
a.Init(auth.ClientToken(tok))
|
||||
_ = a.Init(auth.ClientToken(tok))
|
||||
}
|
||||
}()
|
||||
|
||||
|
@@ -3,6 +3,7 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
@@ -80,5 +81,4 @@ func TestRoundTripper(t *testing.T) {
|
||||
if string(b) != "hello world" {
|
||||
t.Fatal("response is", string(b))
|
||||
}
|
||||
|
||||
}
|
||||
|
239
util/http/trie.go
Normal file
239
util/http/trie.go
Normal file
@@ -0,0 +1,239 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// TrieOptions contains search options
|
||||
type TrieOptions struct {
|
||||
IgnoreCase bool
|
||||
}
|
||||
|
||||
// TrieOption func signature
|
||||
type TrieOption func(*TrieOptions)
|
||||
|
||||
// IgnoreCase says that search must be case insensitive
|
||||
func IgnoreCase(b bool) TrieOption {
|
||||
return func(o *TrieOptions) {
|
||||
o.IgnoreCase = b
|
||||
}
|
||||
}
|
||||
|
||||
// Tree is a trie tree.
|
||||
type Trie struct {
|
||||
node *node
|
||||
rcache map[string]*regexp.Regexp
|
||||
rmu sync.RWMutex
|
||||
}
|
||||
|
||||
// node is a node of tree
|
||||
type node struct {
|
||||
actions map[string]interface{} // key is method, val is handler interface
|
||||
children map[string]*node // key is label of next nodes
|
||||
label string
|
||||
}
|
||||
|
||||
const (
|
||||
pathRoot string = "/"
|
||||
pathDelimiter string = "/"
|
||||
paramDelimiter string = ":"
|
||||
leftPtnDelimiter string = "{"
|
||||
rightPtnDelimiter string = "}"
|
||||
ptnWildcard string = "(.+)"
|
||||
)
|
||||
|
||||
// NewTree creates a new trie tree.
|
||||
func NewTrie() *Trie {
|
||||
return &Trie{
|
||||
node: &node{
|
||||
label: pathRoot,
|
||||
actions: make(map[string]interface{}),
|
||||
children: make(map[string]*node),
|
||||
},
|
||||
rcache: make(map[string]*regexp.Regexp),
|
||||
}
|
||||
}
|
||||
|
||||
// Insert inserts a route definition to tree.
|
||||
func (t *Trie) Insert(methods []string, path string, handler interface{}) {
|
||||
curNode := t.node
|
||||
if path == pathRoot {
|
||||
curNode.label = path
|
||||
for _, method := range methods {
|
||||
curNode.actions[method] = handler
|
||||
}
|
||||
return
|
||||
}
|
||||
ep := splitPath(path)
|
||||
for i, p := range ep {
|
||||
nextNode, ok := curNode.children[p]
|
||||
if ok {
|
||||
curNode = nextNode
|
||||
}
|
||||
// Create a new node.
|
||||
if !ok {
|
||||
curNode.children[p] = &node{
|
||||
label: p,
|
||||
actions: make(map[string]interface{}),
|
||||
children: make(map[string]*node),
|
||||
}
|
||||
curNode = curNode.children[p]
|
||||
}
|
||||
// last loop.
|
||||
// If there is already registered data, overwrite it.
|
||||
if i == len(ep)-1 {
|
||||
curNode.label = p
|
||||
for _, method := range methods {
|
||||
curNode.actions[method] = handler
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search searches a path from a tree.
|
||||
func (t *Trie) Search(method string, path string, opts ...TrieOption) (interface{}, map[string]string, bool) {
|
||||
params := make(map[string]string)
|
||||
|
||||
options := TrieOptions{}
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
curNode := t.node
|
||||
|
||||
nodeLoop:
|
||||
for _, p := range splitPath(path) {
|
||||
nextNode, ok := curNode.children[p]
|
||||
if ok {
|
||||
curNode = nextNode
|
||||
continue nodeLoop
|
||||
}
|
||||
if options.IgnoreCase {
|
||||
// additional loop for case insensitive matching
|
||||
for k, v := range curNode.children {
|
||||
if literalEqual(k, p, true) {
|
||||
curNode = v
|
||||
continue nodeLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(curNode.children) == 0 {
|
||||
if !literalEqual(curNode.label, p, options.IgnoreCase) {
|
||||
// no matching path was found
|
||||
return nil, nil, false
|
||||
}
|
||||
break
|
||||
}
|
||||
isParamMatch := false
|
||||
for c := range curNode.children {
|
||||
if string([]rune(c)[0]) == leftPtnDelimiter {
|
||||
ptn := getPattern(c)
|
||||
t.rmu.RLock()
|
||||
reg, ok := t.rcache[ptn]
|
||||
t.rmu.RUnlock()
|
||||
if !ok {
|
||||
var err error
|
||||
reg, err = regexp.Compile(ptn)
|
||||
if err != nil {
|
||||
return nil, nil, false
|
||||
}
|
||||
t.rmu.Lock()
|
||||
t.rcache[ptn] = reg
|
||||
t.rmu.Unlock()
|
||||
}
|
||||
if reg.Match([]byte(p)) {
|
||||
pn := getParamName(c)
|
||||
params[pn] = p
|
||||
curNode = curNode.children[c]
|
||||
isParamMatch = true
|
||||
break
|
||||
}
|
||||
// no matching param was found.
|
||||
return nil, nil, false
|
||||
}
|
||||
}
|
||||
if !isParamMatch {
|
||||
return nil, nil, false
|
||||
}
|
||||
}
|
||||
if path == pathRoot {
|
||||
if len(curNode.actions) == 0 {
|
||||
return nil, nil, false
|
||||
}
|
||||
}
|
||||
|
||||
handler, ok := curNode.actions[method]
|
||||
if !ok || handler == nil {
|
||||
return nil, nil, false
|
||||
}
|
||||
return handler, params, true
|
||||
}
|
||||
|
||||
// getPattern gets a pattern from a label
|
||||
// {id:[^\d+$]} -> ^\d+$
|
||||
// {id} -> (.+)
|
||||
func getPattern(label string) string {
|
||||
leftI := strings.Index(label, leftPtnDelimiter)
|
||||
rightI := strings.Index(label, paramDelimiter)
|
||||
// if label doesn't have any pattern, return wild card pattern as default.
|
||||
if leftI == -1 || rightI == -1 {
|
||||
return ptnWildcard
|
||||
}
|
||||
return label[rightI+1 : len(label)-1]
|
||||
}
|
||||
|
||||
// getParamName gets a parameter from a label
|
||||
// {id:[^\d+$]} -> id
|
||||
// {id} -> id
|
||||
func getParamName(label string) string {
|
||||
leftI := strings.Index(label, leftPtnDelimiter)
|
||||
rightI := func(l string) int {
|
||||
r := []rune(l)
|
||||
|
||||
var n int
|
||||
|
||||
loop:
|
||||
for i := 0; i < len(r); i++ {
|
||||
n = i
|
||||
switch string(r[i]) {
|
||||
case paramDelimiter:
|
||||
n = i
|
||||
break loop
|
||||
case rightPtnDelimiter:
|
||||
n = i
|
||||
break loop
|
||||
}
|
||||
|
||||
if i == len(r)-1 {
|
||||
n = i + 1
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
return n
|
||||
}(label)
|
||||
|
||||
return label[leftI+1 : rightI]
|
||||
}
|
||||
|
||||
// splitPath removes an empty value in slice.
|
||||
func splitPath(path string) []string {
|
||||
s := strings.Split(path, pathDelimiter)
|
||||
var r []string
|
||||
for _, str := range s {
|
||||
if str != "" {
|
||||
r = append(r, str)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func literalEqual(component, literal string, ignoreCase bool) bool {
|
||||
if ignoreCase {
|
||||
return strings.EqualFold(component, literal)
|
||||
}
|
||||
return component == literal
|
||||
}
|
95
util/http/trie_test.go
Normal file
95
util/http/trie_test.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTrieIgnoreCase(t *testing.T) {
|
||||
type handler struct {
|
||||
name string
|
||||
}
|
||||
tr := NewTrie()
|
||||
tr.Insert([]string{http.MethodPut}, "/v1/create/{id}", &handler{name: "test"})
|
||||
|
||||
_, _, ok := tr.Search(http.MethodPut, "/v1/CREATE/12", IgnoreCase(true))
|
||||
if !ok {
|
||||
t.Fatalf("unexpected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrieContentType(t *testing.T) {
|
||||
type handler struct {
|
||||
name string
|
||||
}
|
||||
tr := NewTrie()
|
||||
tr.Insert([]string{"application/json"}, "/v1/create/{id}", &handler{name: "test"})
|
||||
|
||||
h, _, ok := tr.Search("application/json", "/v1/create/12")
|
||||
if !ok {
|
||||
t.Fatalf("must be found error")
|
||||
}
|
||||
if h.(*handler).name != "test" {
|
||||
t.Fatalf("invalid handler %v", h)
|
||||
}
|
||||
_, _, ok = tr.Search("text/xml", "/v1/create/12")
|
||||
if ok {
|
||||
t.Fatalf("must be not found error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrieNoMatchMethod(t *testing.T) {
|
||||
tr := NewTrie()
|
||||
tr.Insert([]string{http.MethodPut}, "/v1/create/{id}", nil)
|
||||
|
||||
_, _, ok := tr.Search(http.MethodPost, "/v1/create")
|
||||
if ok {
|
||||
t.Fatalf("must be not found error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrieMatchRegexp(t *testing.T) {
|
||||
type handler struct{}
|
||||
tr := NewTrie()
|
||||
tr.Insert([]string{http.MethodPut}, "/v1/create/{category}/{id:[0-9]+}", &handler{})
|
||||
|
||||
_, params, ok := tr.Search(http.MethodPut, "/v1/create/test_cat/12345")
|
||||
switch {
|
||||
case !ok:
|
||||
t.Fatalf("route not found")
|
||||
case len(params) != 2:
|
||||
t.Fatalf("param matching error %v", params)
|
||||
case params["category"] != "test_cat":
|
||||
t.Fatalf("param matching error %v", params)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrieMatchRegexpFail(t *testing.T) {
|
||||
type handler struct{}
|
||||
tr := NewTrie()
|
||||
tr.Insert([]string{http.MethodPut}, "/v1/create/{id:[a-z]+}", &handler{})
|
||||
|
||||
_, _, ok := tr.Search(http.MethodPut, "/v1/create/12345")
|
||||
if ok {
|
||||
t.Fatalf("route must not be not found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrieMatchLongest(t *testing.T) {
|
||||
type handler struct {
|
||||
name string
|
||||
}
|
||||
tr := NewTrie()
|
||||
tr.Insert([]string{http.MethodPut}, "/v1/create", &handler{name: "first"})
|
||||
tr.Insert([]string{http.MethodPut}, "/v1/create/{id:[0-9]+}", &handler{name: "second"})
|
||||
if h, _, ok := tr.Search(http.MethodPut, "/v1/create/12345"); !ok {
|
||||
t.Fatalf("route must be found")
|
||||
} else if h.(*handler).name != "second" {
|
||||
t.Fatalf("invalid handler found: %s != %s", h.(*handler).name, "second")
|
||||
}
|
||||
if h, _, ok := tr.Search(http.MethodPut, "/v1/create"); !ok {
|
||||
t.Fatalf("route must be found")
|
||||
} else if h.(*handler).name != "first" {
|
||||
t.Fatalf("invalid handler found: %s != %s", h.(*handler).name, "first")
|
||||
}
|
||||
}
|
@@ -49,7 +49,7 @@ func (p *poolConn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *poolConn) Id() string {
|
||||
func (p *poolConn) ID() string {
|
||||
return p.id
|
||||
}
|
||||
|
||||
|
@@ -21,7 +21,7 @@ type Pool interface {
|
||||
// Conn conn pool interface
|
||||
type Conn interface {
|
||||
// unique id of connection
|
||||
Id() string
|
||||
ID() string
|
||||
// time it was created
|
||||
Created() time.Time
|
||||
// embedded connection
|
||||
|
@@ -16,7 +16,7 @@ import (
|
||||
var (
|
||||
// ErrInvalidParam is returned when invalid data is provided to the ToJSON or Unmarshal function.
|
||||
// Specifically, this will be returned when there is no equals sign present in the URL query parameter.
|
||||
ErrInvalidParam error = errors.New("qson: invalid url query param provided")
|
||||
ErrInvalidParam = errors.New("qson: invalid url query param provided")
|
||||
|
||||
bracketSplitter *regexp.Regexp
|
||||
)
|
||||
|
@@ -82,9 +82,9 @@ func getValueByName(v reflect.Value, key string) (reflect.Value, error) {
|
||||
case reflect.Struct:
|
||||
value = v.FieldByName(key)
|
||||
case reflect.Map:
|
||||
kValue := reflect.Indirect(reflect.New(v.Type().Key()))
|
||||
kValue.SetString(key)
|
||||
value = v.MapIndex(kValue)
|
||||
kvalue := reflect.Indirect(reflect.New(v.Type().Key()))
|
||||
kvalue.SetString(key)
|
||||
value = v.MapIndex(kvalue)
|
||||
}
|
||||
|
||||
if !value.IsValid() {
|
||||
|
@@ -12,16 +12,19 @@ import (
|
||||
// ErrInvalidParam specifies invalid url query params
|
||||
var ErrInvalidParam = errors.New("invalid url query param provided")
|
||||
|
||||
// var timeKind = reflect.ValueOf(time.Time{}).Kind()
|
||||
|
||||
// bracketSplitter
|
||||
var bracketSplitter = regexp.MustCompile(`\[|\]`)
|
||||
|
||||
//var timeKind = reflect.ValueOf(time.Time{}).Kind()
|
||||
|
||||
// StructField contains struct field path its value and field
|
||||
type StructField struct {
|
||||
Field reflect.StructField
|
||||
Value reflect.Value
|
||||
Path string
|
||||
Value reflect.Value
|
||||
Field reflect.StructField
|
||||
}
|
||||
|
||||
// StructFieldByTag get struct field by tag key and its value
|
||||
func StructFieldByTag(src interface{}, tkey string, tval string) (interface{}, error) {
|
||||
sv := reflect.ValueOf(src)
|
||||
if sv.Kind() == reflect.Ptr {
|
||||
@@ -66,6 +69,7 @@ func StructFieldByTag(src interface{}, tkey string, tval string) (interface{}, e
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
// StructFieldByPath get struct field by its path
|
||||
func StructFieldByPath(src interface{}, path string) (interface{}, error) {
|
||||
var err error
|
||||
for _, p := range strings.Split(path, ".") {
|
||||
@@ -77,6 +81,7 @@ func StructFieldByPath(src interface{}, path string) (interface{}, error) {
|
||||
return src, err
|
||||
}
|
||||
|
||||
// StructFieldByName get struct field by its name
|
||||
func StructFieldByName(src interface{}, tkey string) (interface{}, error) {
|
||||
sv := reflect.ValueOf(src)
|
||||
if sv.Kind() == reflect.Ptr {
|
||||
@@ -116,7 +121,7 @@ func StructFieldByName(src interface{}, tkey string) (interface{}, error) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
// StructFieldsMap returns map[string]interface{} or error
|
||||
// StructFieldsMap returns struct map[string]interface{} or error
|
||||
func StructFieldsMap(src interface{}) (map[string]interface{}, error) {
|
||||
fields, err := StructFields(src)
|
||||
if err != nil {
|
||||
@@ -149,9 +154,9 @@ func StructFields(src interface{}) ([]StructField, error) {
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
//case timeKind:
|
||||
//fmt.Printf("GGG\n")
|
||||
//fields = append(fields, StructField{Field: fld, Value: val, Path: fld.Name})
|
||||
// case timeKind:
|
||||
// fmt.Printf("GGG\n")
|
||||
// fields = append(fields, StructField{Field: fld, Value: val, Path: fld.Name})
|
||||
case reflect.Struct:
|
||||
infields, err := StructFields(val.Interface())
|
||||
if err != nil {
|
||||
@@ -207,6 +212,7 @@ func CopyFrom(a, b interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// StructURLValues get struct fields via url.Values
|
||||
func StructURLValues(src interface{}, pref string, tags []string) (url.Values, error) {
|
||||
data := url.Values{}
|
||||
|
||||
|
@@ -1,10 +1,11 @@
|
||||
package reflect
|
||||
package reflect_test
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
rfl "reflect"
|
||||
"testing"
|
||||
|
||||
rutil "github.com/unistack-org/micro/v3/util/reflect"
|
||||
)
|
||||
|
||||
func TestStructFieldsMap(t *testing.T) {
|
||||
@@ -19,7 +20,7 @@ func TestStructFieldsMap(t *testing.T) {
|
||||
}
|
||||
|
||||
val := &Str{Name: []string{"first", "second"}, XXX: "ttt", Nested: NestedStr{BBB: "ddd", CCC: 9}}
|
||||
fields, err := StructFieldsMap(val)
|
||||
fields, err := rutil.StructFieldsMap(val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -40,7 +41,7 @@ func TestStructFields(t *testing.T) {
|
||||
}
|
||||
|
||||
val := &Str{Name: []string{"first", "second"}, XXX: "ttt", Nested: NestedStr{BBB: "ddd", CCC: 9}}
|
||||
fields, err := StructFields(val)
|
||||
fields, err := rutil.StructFields(val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -67,11 +68,11 @@ func TestStructByPath(t *testing.T) {
|
||||
}
|
||||
|
||||
val := &Str{Name: []string{"first", "second"}, XXX: "ttt", Nested: NestedStr{BBB: "ddd", CCC: 9}}
|
||||
field, err := StructFieldByPath(val, "Nested.CCC")
|
||||
field, err := rutil.StructFieldByPath(val, "Nested.CCC")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if rfl.Indirect(reflect.ValueOf(field)).Int() != 9 {
|
||||
if reflect.Indirect(reflect.ValueOf(field)).Int() != 9 {
|
||||
t.Fatalf("invalid elem returned: %v", field)
|
||||
}
|
||||
}
|
||||
@@ -83,7 +84,7 @@ func TestStructByTag(t *testing.T) {
|
||||
|
||||
val := &Str{Name: []string{"first", "second"}}
|
||||
|
||||
iface, err := StructFieldByTag(val, "codec", "flatten")
|
||||
iface, err := rutil.StructFieldByTag(val, "codec", "flatten")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -102,7 +103,7 @@ func TestStructByName(t *testing.T) {
|
||||
|
||||
val := &Str{Name: []string{"first", "second"}}
|
||||
|
||||
iface, err := StructFieldByName(val, "Name")
|
||||
iface, err := rutil.StructFieldByName(val, "Name")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -122,7 +123,7 @@ func TestStructURLValues(t *testing.T) {
|
||||
}
|
||||
|
||||
val := &Str{Name: "test_name", Args: []int{1, 2, 3}, Str: &Str{Name: "nested_name"}}
|
||||
data, err := StructURLValues(val, "", []string{"json"})
|
||||
data, err := rutil.StructURLValues(val, "", []string{"json"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -138,7 +139,7 @@ func TestURLSliceVars(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mp, err := URLMap(u.RawQuery)
|
||||
mp, err := rutil.URLMap(u.RawQuery)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -164,7 +165,7 @@ func TestURLVars(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mp, err := URLMap(u.RawQuery)
|
||||
mp, err := rutil.URLMap(u.RawQuery)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -184,14 +185,14 @@ func TestIsZero(t *testing.T) {
|
||||
}
|
||||
testStr1.Nested.NestedName = "nested_name"
|
||||
|
||||
if ok := IsZero(testStr1); ok {
|
||||
if ok := rutil.IsZero(testStr1); ok {
|
||||
t.Fatalf("zero ret on non zero struct: %#+v", testStr1)
|
||||
}
|
||||
|
||||
testStr1.Name = ""
|
||||
testStr1.Value = ""
|
||||
testStr1.Nested.NestedName = ""
|
||||
if ok := IsZero(testStr1); !ok {
|
||||
if ok := rutil.IsZero(testStr1); !ok {
|
||||
t.Fatalf("non zero ret on zero struct: %#+v", testStr1)
|
||||
}
|
||||
|
||||
@@ -206,12 +207,12 @@ func TestIsZero(t *testing.T) {
|
||||
Name: "test_name",
|
||||
Nested: &testStr3{Nested: "nested_name"},
|
||||
}
|
||||
if ok := IsZero(vtest); ok {
|
||||
if ok := rutil.IsZero(vtest); ok {
|
||||
t.Fatalf("zero ret on non zero struct: %#+v", vtest)
|
||||
}
|
||||
vtest.Nested = nil
|
||||
vtest.Name = ""
|
||||
if ok := IsZero(vtest); !ok {
|
||||
if ok := rutil.IsZero(vtest); !ok {
|
||||
t.Fatalf("non zero ret on zero struct: %#+v", vtest)
|
||||
}
|
||||
|
||||
|
@@ -28,8 +28,8 @@ type Stream struct {
|
||||
Entries chan *Entry
|
||||
// Stop channel
|
||||
Stop chan bool
|
||||
// Id of the stream
|
||||
Id string
|
||||
// ID of the stream
|
||||
ID string
|
||||
}
|
||||
|
||||
// Put adds a new value to ring buffer
|
||||
@@ -53,7 +53,7 @@ func (b *Buffer) Put(v interface{}) {
|
||||
for _, stream := range b.streams {
|
||||
select {
|
||||
case <-stream.Stop:
|
||||
delete(b.streams, stream.Id)
|
||||
delete(b.streams, stream.ID)
|
||||
close(stream.Entries)
|
||||
case stream.Entries <- entry:
|
||||
}
|
||||
@@ -116,7 +116,7 @@ func (b *Buffer) Stream() (<-chan *Entry, chan bool) {
|
||||
stop := make(chan bool)
|
||||
|
||||
b.streams[id] = &Stream{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Entries: entries,
|
||||
Stop: stop,
|
||||
}
|
||||
|
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2015, Gengo, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Gengo, Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@@ -1,119 +0,0 @@
|
||||
package router
|
||||
|
||||
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/compile.go
|
||||
|
||||
const (
|
||||
opcodeVersion = 1
|
||||
)
|
||||
|
||||
// Template is a compiled representation of path templates.
|
||||
type Template struct {
|
||||
// Version is the version number of the format.
|
||||
Version int
|
||||
// OpCodes is a sequence of operations.
|
||||
OpCodes []int
|
||||
// Pool is a constant pool
|
||||
Pool []string
|
||||
// Verb is a VERB part in the template.
|
||||
Verb string
|
||||
// Fields is a list of field paths bound in this template.
|
||||
Fields []string
|
||||
// Original template (example: /v1/a_bit_of_everything)
|
||||
Template string
|
||||
}
|
||||
|
||||
// Compiler compiles utilities representation of path templates into marshallable operations.
|
||||
// They can be unmarshalled by runtime.NewPattern.
|
||||
type Compiler interface {
|
||||
Compile() Template
|
||||
}
|
||||
|
||||
type op struct {
|
||||
// code is the opcode of the operation
|
||||
code OpCode
|
||||
|
||||
// str is a string operand of the code.
|
||||
// num is ignored if str is not empty.
|
||||
str string
|
||||
|
||||
// num is a numeric operand of the code.
|
||||
num int
|
||||
}
|
||||
|
||||
func (w wildcard) compile() []op {
|
||||
return []op{
|
||||
{code: OpPush},
|
||||
}
|
||||
}
|
||||
|
||||
func (w deepWildcard) compile() []op {
|
||||
return []op{
|
||||
{code: OpPushM},
|
||||
}
|
||||
}
|
||||
|
||||
func (l literal) compile() []op {
|
||||
return []op{
|
||||
{
|
||||
code: OpLitPush,
|
||||
str: string(l),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v variable) compile() []op {
|
||||
ops := make([]op, 0, len(v.segments))
|
||||
for _, s := range v.segments {
|
||||
ops = append(ops, s.compile()...)
|
||||
}
|
||||
ops = append(ops, op{
|
||||
code: OpConcatN,
|
||||
num: len(v.segments),
|
||||
}, op{
|
||||
code: OpCapture,
|
||||
str: v.path,
|
||||
})
|
||||
|
||||
return ops
|
||||
}
|
||||
|
||||
func (t template) Compile() Template {
|
||||
rawOps := make([]op, 0, len(t.segments))
|
||||
for _, s := range t.segments {
|
||||
rawOps = append(rawOps, s.compile()...)
|
||||
}
|
||||
|
||||
var (
|
||||
ops []int
|
||||
pool []string
|
||||
fields []string
|
||||
)
|
||||
consts := make(map[string]int)
|
||||
for _, op := range rawOps {
|
||||
ops = append(ops, int(op.code))
|
||||
if op.str == "" {
|
||||
ops = append(ops, op.num)
|
||||
} else {
|
||||
// eof segment literal represents the "/" path pattern
|
||||
if op.str == eof {
|
||||
op.str = ""
|
||||
}
|
||||
if _, ok := consts[op.str]; !ok {
|
||||
consts[op.str] = len(pool)
|
||||
pool = append(pool, op.str)
|
||||
}
|
||||
ops = append(ops, consts[op.str])
|
||||
}
|
||||
if op.code == OpCapture {
|
||||
fields = append(fields, op.str)
|
||||
}
|
||||
}
|
||||
return Template{
|
||||
Version: opcodeVersion,
|
||||
OpCodes: ops,
|
||||
Pool: pool,
|
||||
Verb: t.verb,
|
||||
Fields: fields,
|
||||
Template: t.template,
|
||||
}
|
||||
}
|
@@ -1,129 +0,0 @@
|
||||
package router
|
||||
|
||||
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/compile_test.go
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
operandFiller = 0
|
||||
)
|
||||
|
||||
func TestCompile(t *testing.T) {
|
||||
for _, spec := range []struct {
|
||||
segs []segment
|
||||
verb string
|
||||
|
||||
ops []int
|
||||
pool []string
|
||||
fields []string
|
||||
}{
|
||||
{},
|
||||
{
|
||||
segs: []segment{
|
||||
literal(eof),
|
||||
},
|
||||
ops: []int{int(OpLitPush), 0},
|
||||
pool: []string{""},
|
||||
},
|
||||
{
|
||||
segs: []segment{
|
||||
wildcard{},
|
||||
},
|
||||
ops: []int{int(OpPush), operandFiller},
|
||||
},
|
||||
{
|
||||
segs: []segment{
|
||||
deepWildcard{},
|
||||
},
|
||||
ops: []int{int(OpPushM), operandFiller},
|
||||
},
|
||||
{
|
||||
segs: []segment{
|
||||
literal("v1"),
|
||||
},
|
||||
ops: []int{int(OpLitPush), 0},
|
||||
pool: []string{"v1"},
|
||||
},
|
||||
{
|
||||
segs: []segment{
|
||||
literal("v1"),
|
||||
},
|
||||
verb: "LOCK",
|
||||
ops: []int{int(OpLitPush), 0},
|
||||
pool: []string{"v1"},
|
||||
},
|
||||
{
|
||||
segs: []segment{
|
||||
variable{
|
||||
path: "name.nested",
|
||||
segments: []segment{
|
||||
wildcard{},
|
||||
},
|
||||
},
|
||||
},
|
||||
ops: []int{
|
||||
int(OpPush), operandFiller,
|
||||
int(OpConcatN), 1,
|
||||
int(OpCapture), 0,
|
||||
},
|
||||
pool: []string{"name.nested"},
|
||||
fields: []string{"name.nested"},
|
||||
},
|
||||
{
|
||||
segs: []segment{
|
||||
literal("obj"),
|
||||
variable{
|
||||
path: "name.nested",
|
||||
segments: []segment{
|
||||
literal("a"),
|
||||
wildcard{},
|
||||
literal("b"),
|
||||
},
|
||||
},
|
||||
variable{
|
||||
path: "obj",
|
||||
segments: []segment{
|
||||
deepWildcard{},
|
||||
},
|
||||
},
|
||||
},
|
||||
ops: []int{
|
||||
int(OpLitPush), 0,
|
||||
int(OpLitPush), 1,
|
||||
int(OpPush), operandFiller,
|
||||
int(OpLitPush), 2,
|
||||
int(OpConcatN), 3,
|
||||
int(OpCapture), 3,
|
||||
int(OpPushM), operandFiller,
|
||||
int(OpConcatN), 1,
|
||||
int(OpCapture), 0,
|
||||
},
|
||||
pool: []string{"obj", "a", "b", "name.nested"},
|
||||
fields: []string{"name.nested", "obj"},
|
||||
},
|
||||
} {
|
||||
tmpl := template{
|
||||
segments: spec.segs,
|
||||
verb: spec.verb,
|
||||
}
|
||||
compiled := tmpl.Compile()
|
||||
if got, want := compiled.Version, opcodeVersion; got != want {
|
||||
t.Errorf("tmpl.Compile().Version = %d; want %d; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
|
||||
}
|
||||
if got, want := compiled.OpCodes, spec.ops; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("tmpl.Compile().OpCodes = %v; want %v; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
|
||||
}
|
||||
if got, want := compiled.Pool, spec.pool; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("tmpl.Compile().Pool = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
|
||||
}
|
||||
if got, want := compiled.Verb, spec.verb; got != want {
|
||||
t.Errorf("tmpl.Compile().Verb = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
|
||||
}
|
||||
if got, want := compiled.Fields, spec.fields; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("tmpl.Compile().Fields = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,371 +0,0 @@
|
||||
package router
|
||||
|
||||
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/parse.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// InvalidTemplateError indicates that the path template is not valid.
|
||||
type InvalidTemplateError struct {
|
||||
tmpl string
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e InvalidTemplateError) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.msg, e.tmpl)
|
||||
}
|
||||
|
||||
// Parse parses the string representation of path template
|
||||
func Parse(tmpl string) (Compiler, error) {
|
||||
if !strings.HasPrefix(tmpl, "/") {
|
||||
return template{}, InvalidTemplateError{tmpl: tmpl, msg: "no leading /"}
|
||||
}
|
||||
tokens, verb := tokenize(tmpl[1:])
|
||||
|
||||
p := parser{tokens: tokens}
|
||||
segs, err := p.topLevelSegments()
|
||||
if err != nil {
|
||||
return template{}, InvalidTemplateError{tmpl: tmpl, msg: err.Error()}
|
||||
}
|
||||
|
||||
return template{
|
||||
segments: segs,
|
||||
verb: verb,
|
||||
template: tmpl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func tokenize(path string) (tokens []string, verb string) {
|
||||
if path == "" {
|
||||
return []string{eof}, ""
|
||||
}
|
||||
|
||||
const (
|
||||
init = iota
|
||||
field
|
||||
nested
|
||||
)
|
||||
st := init
|
||||
for path != "" {
|
||||
var idx int
|
||||
switch st {
|
||||
case init:
|
||||
idx = strings.IndexAny(path, "/{")
|
||||
case field:
|
||||
idx = strings.IndexAny(path, ".=}")
|
||||
case nested:
|
||||
idx = strings.IndexAny(path, "/}")
|
||||
}
|
||||
if idx < 0 {
|
||||
tokens = append(tokens, path)
|
||||
break
|
||||
}
|
||||
switch r := path[idx]; r {
|
||||
case '/', '.':
|
||||
case '{':
|
||||
st = field
|
||||
case '=':
|
||||
st = nested
|
||||
case '}':
|
||||
st = init
|
||||
}
|
||||
if idx == 0 {
|
||||
tokens = append(tokens, path[idx:idx+1])
|
||||
} else {
|
||||
tokens = append(tokens, path[:idx], path[idx:idx+1])
|
||||
}
|
||||
path = path[idx+1:]
|
||||
}
|
||||
|
||||
l := len(tokens)
|
||||
// See
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/pull/1947#issuecomment-774523693 ;
|
||||
// although normal and backwards-compat logic here is to use the last index
|
||||
// of a colon, if the final segment is a variable followed by a colon, the
|
||||
// part following the colon must be a verb. Hence if the previous token is
|
||||
// an end var marker, we switch the index we're looking for to Index instead
|
||||
// of LastIndex, so that we correctly grab the remaining part of the path as
|
||||
// the verb.
|
||||
var penultimateTokenIsEndVar bool
|
||||
switch l {
|
||||
case 0, 1:
|
||||
// Not enough to be variable so skip this logic and don't result in an
|
||||
// invalid index
|
||||
default:
|
||||
penultimateTokenIsEndVar = tokens[l-2] == "}"
|
||||
}
|
||||
t := tokens[l-1]
|
||||
var idx int
|
||||
if penultimateTokenIsEndVar {
|
||||
idx = strings.Index(t, ":")
|
||||
} else {
|
||||
idx = strings.LastIndex(t, ":")
|
||||
}
|
||||
if idx == 0 {
|
||||
tokens, verb = tokens[:l-1], t[1:]
|
||||
} else if idx > 0 {
|
||||
tokens[l-1], verb = t[:idx], t[idx+1:]
|
||||
}
|
||||
tokens = append(tokens, eof)
|
||||
return tokens, verb
|
||||
}
|
||||
|
||||
// parser is a parser of the template syntax defined in github.com/googleapis/googleapis/google/api/http.proto.
|
||||
type parser struct {
|
||||
tokens []string
|
||||
accepted []string
|
||||
}
|
||||
|
||||
// topLevelSegments is the target of this parser.
|
||||
func (p *parser) topLevelSegments() ([]segment, error) {
|
||||
if _, err := p.accept(typeEOF); err == nil {
|
||||
p.tokens = p.tokens[:0]
|
||||
return []segment{literal(eof)}, nil
|
||||
}
|
||||
segs, err := p.segments()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.accept(typeEOF); err != nil {
|
||||
return nil, fmt.Errorf("unexpected token %q after segments %q", p.tokens[0], strings.Join(p.accepted, ""))
|
||||
}
|
||||
return segs, nil
|
||||
}
|
||||
|
||||
func (p *parser) segments() ([]segment, error) {
|
||||
s, err := p.segment()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
segs := []segment{s}
|
||||
for {
|
||||
if _, err := p.accept("/"); err != nil {
|
||||
return segs, nil
|
||||
}
|
||||
s, err := p.segment()
|
||||
if err != nil {
|
||||
return segs, err
|
||||
}
|
||||
segs = append(segs, s)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) segment() (segment, error) {
|
||||
if _, err := p.accept("*"); err == nil {
|
||||
return wildcard{}, nil
|
||||
}
|
||||
if _, err := p.accept("**"); err == nil {
|
||||
return deepWildcard{}, nil
|
||||
}
|
||||
if l, err := p.literal(); err == nil {
|
||||
return l, nil
|
||||
}
|
||||
|
||||
v, err := p.variable()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("segment neither wildcards, literal or variable: %v", err)
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (p *parser) literal() (segment, error) {
|
||||
lit, err := p.accept(typeLiteral)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return literal(lit), nil
|
||||
}
|
||||
|
||||
func (p *parser) variable() (segment, error) {
|
||||
if _, err := p.accept("{"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path, err := p.fieldPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var segs []segment
|
||||
if _, err := p.accept("="); err == nil {
|
||||
segs, err = p.segments()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid segment in variable %q: %v", path, err)
|
||||
}
|
||||
} else {
|
||||
segs = []segment{wildcard{}}
|
||||
}
|
||||
|
||||
if _, err := p.accept("}"); err != nil {
|
||||
return nil, fmt.Errorf("unterminated variable segment: %s", path)
|
||||
}
|
||||
return variable{
|
||||
path: path,
|
||||
segments: segs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *parser) fieldPath() (string, error) {
|
||||
c, err := p.accept(typeIdent)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
components := []string{c}
|
||||
for {
|
||||
if _, err = p.accept("."); err != nil {
|
||||
return strings.Join(components, "."), nil
|
||||
}
|
||||
c, err := p.accept(typeIdent)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid field path component: %v", err)
|
||||
}
|
||||
components = append(components, c)
|
||||
}
|
||||
}
|
||||
|
||||
// A termType is a type of terminal symbols.
|
||||
type termType string
|
||||
|
||||
// These constants define some of valid values of termType.
|
||||
// They improve readability of parse functions.
|
||||
//
|
||||
// You can also use "/", "*", "**", "." or "=" as valid values.
|
||||
const (
|
||||
typeIdent = termType("ident")
|
||||
typeLiteral = termType("literal")
|
||||
typeEOF = termType("$")
|
||||
)
|
||||
|
||||
const (
|
||||
// eof is the terminal symbol which always appears at the end of token sequence.
|
||||
eof = "\u0000"
|
||||
)
|
||||
|
||||
// accept tries to accept a token in "p".
|
||||
// This function consumes a token and returns it if it matches to the specified "term".
|
||||
// If it doesn't match, the function does not consume any tokens and return an error.
|
||||
func (p *parser) accept(term termType) (string, error) {
|
||||
t := p.tokens[0]
|
||||
switch term {
|
||||
case "/", "*", "**", ".", "=", "{", "}":
|
||||
if t != string(term) && t != "/" {
|
||||
return "", fmt.Errorf("expected %q but got %q", term, t)
|
||||
}
|
||||
case typeEOF:
|
||||
if t != eof {
|
||||
return "", fmt.Errorf("expected EOF but got %q", t)
|
||||
}
|
||||
case typeIdent:
|
||||
if err := expectIdent(t); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case typeLiteral:
|
||||
if err := expectPChars(t); err != nil {
|
||||
return "", err
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("unknown termType %q", term)
|
||||
}
|
||||
p.tokens = p.tokens[1:]
|
||||
p.accepted = append(p.accepted, t)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// expectPChars determines if "t" consists of only pchars defined in RFC3986.
|
||||
//
|
||||
// https://www.ietf.org/rfc/rfc3986.txt, P.49
|
||||
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
// / "*" / "+" / "," / ";" / "="
|
||||
// pct-encoded = "%" HEXDIG HEXDIG
|
||||
//nolint:gocyclo
|
||||
func expectPChars(t string) error {
|
||||
const (
|
||||
init = iota
|
||||
pct1
|
||||
pct2
|
||||
)
|
||||
st := init
|
||||
for _, r := range t {
|
||||
if st != init {
|
||||
if !isHexDigit(r) {
|
||||
return fmt.Errorf("invalid hexdigit: %c(%U)", r, r)
|
||||
}
|
||||
switch st {
|
||||
case pct1:
|
||||
st = pct2
|
||||
case pct2:
|
||||
st = init
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// unreserved
|
||||
switch {
|
||||
case 'A' <= r && r <= 'Z':
|
||||
continue
|
||||
case 'a' <= r && r <= 'z':
|
||||
continue
|
||||
case '0' <= r && r <= '9':
|
||||
continue
|
||||
}
|
||||
switch r {
|
||||
case '-', '.', '_', '~':
|
||||
// unreserved
|
||||
case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=':
|
||||
// sub-delims
|
||||
case ':', '@':
|
||||
// rest of pchar
|
||||
case '%':
|
||||
// pct-encoded
|
||||
st = pct1
|
||||
default:
|
||||
return fmt.Errorf("invalid character in path segment: %q(%U)", r, r)
|
||||
}
|
||||
}
|
||||
if st != init {
|
||||
return fmt.Errorf("invalid percent-encoding in %q", t)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// expectIdent determines if "ident" is a valid identifier in .proto schema ([[:alpha:]_][[:alphanum:]_]*).
|
||||
func expectIdent(ident string) error {
|
||||
if ident == "" {
|
||||
return fmt.Errorf("empty identifier")
|
||||
}
|
||||
for pos, r := range ident {
|
||||
switch {
|
||||
case '0' <= r && r <= '9':
|
||||
if pos == 0 {
|
||||
return fmt.Errorf("identifier starting with digit: %s", ident)
|
||||
}
|
||||
continue
|
||||
case 'A' <= r && r <= 'Z':
|
||||
continue
|
||||
case 'a' <= r && r <= 'z':
|
||||
continue
|
||||
case r == '_':
|
||||
continue
|
||||
default:
|
||||
return fmt.Errorf("invalid character %q(%U) in identifier: %s", r, r, ident)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isHexDigit(r rune) bool {
|
||||
switch {
|
||||
case '0' <= r && r <= '9':
|
||||
return true
|
||||
case 'A' <= r && r <= 'F':
|
||||
return true
|
||||
case 'a' <= r && r <= 'f':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
@@ -1,360 +0,0 @@
|
||||
package router
|
||||
|
||||
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/parse_test.go
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/unistack-org/micro/v3/logger"
|
||||
)
|
||||
|
||||
func TestTokenize(t *testing.T) {
|
||||
for _, spec := range []struct {
|
||||
src string
|
||||
tokens []string
|
||||
verb string
|
||||
}{
|
||||
{
|
||||
src: "",
|
||||
tokens: []string{eof},
|
||||
},
|
||||
{
|
||||
src: "v1",
|
||||
tokens: []string{"v1", eof},
|
||||
},
|
||||
{
|
||||
src: "v1/b",
|
||||
tokens: []string{"v1", "/", "b", eof},
|
||||
},
|
||||
{
|
||||
src: "v1/endpoint/*",
|
||||
tokens: []string{"v1", "/", "endpoint", "/", "*", eof},
|
||||
},
|
||||
{
|
||||
src: "v1/endpoint/**",
|
||||
tokens: []string{"v1", "/", "endpoint", "/", "**", eof},
|
||||
},
|
||||
{
|
||||
src: "v1/b/{bucket_name=*}",
|
||||
tokens: []string{
|
||||
"v1", "/",
|
||||
"b", "/",
|
||||
"{", "bucket_name", "=", "*", "}",
|
||||
eof,
|
||||
},
|
||||
},
|
||||
{
|
||||
src: "v1/b/{bucket_name=buckets/*}",
|
||||
tokens: []string{
|
||||
"v1", "/",
|
||||
"b", "/",
|
||||
"{", "bucket_name", "=", "buckets", "/", "*", "}",
|
||||
eof,
|
||||
},
|
||||
},
|
||||
{
|
||||
src: "v1/b/{bucket_name=buckets/*}/o",
|
||||
tokens: []string{
|
||||
"v1", "/",
|
||||
"b", "/",
|
||||
"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
|
||||
"o",
|
||||
eof,
|
||||
},
|
||||
},
|
||||
{
|
||||
src: "v1/b/{bucket_name=buckets/*}/o/{name}",
|
||||
tokens: []string{
|
||||
"v1", "/",
|
||||
"b", "/",
|
||||
"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
|
||||
"o", "/", "{", "name", "}",
|
||||
eof,
|
||||
},
|
||||
},
|
||||
{
|
||||
src: "v1/a=b&c=d;e=f:g/endpoint.rdf",
|
||||
tokens: []string{
|
||||
"v1", "/",
|
||||
"a=b&c=d;e=f:g", "/",
|
||||
"endpoint.rdf",
|
||||
eof,
|
||||
},
|
||||
},
|
||||
{
|
||||
src: "v1/a/{endpoint}:a",
|
||||
tokens: []string{
|
||||
"v1", "/",
|
||||
"a", "/",
|
||||
"{", "endpoint", "}",
|
||||
eof,
|
||||
},
|
||||
verb: "a",
|
||||
},
|
||||
{
|
||||
src: "v1/a/{endpoint}:b:c",
|
||||
tokens: []string{
|
||||
"v1", "/",
|
||||
"a", "/",
|
||||
"{", "endpoint", "}",
|
||||
eof,
|
||||
},
|
||||
verb: "b:c",
|
||||
},
|
||||
} {
|
||||
tokens, verb := tokenize(spec.src)
|
||||
if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("tokenize(%q) = %q, _; want %q, _", spec.src, got, want)
|
||||
}
|
||||
|
||||
switch {
|
||||
case spec.verb != "":
|
||||
if got, want := verb, spec.verb; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("tokenize(%q) = %q, _; want %q, _", spec.src, got, want)
|
||||
}
|
||||
|
||||
default:
|
||||
if got, want := verb, ""; got != want {
|
||||
t.Errorf("tokenize(%q) = _, %q; want _, %q", spec.src, got, want)
|
||||
}
|
||||
|
||||
src := fmt.Sprintf("%s:%s", spec.src, "LOCK")
|
||||
tokens, verb = tokenize(src)
|
||||
if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("tokenize(%q) = %q, _; want %q, _", src, got, want)
|
||||
}
|
||||
if got, want := verb, "LOCK"; got != want {
|
||||
t.Errorf("tokenize(%q) = _, %q; want _, %q", src, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSegments(t *testing.T) {
|
||||
for _, spec := range []struct {
|
||||
tokens []string
|
||||
want []segment
|
||||
}{
|
||||
{
|
||||
tokens: []string{eof},
|
||||
want: []segment{
|
||||
literal(eof),
|
||||
},
|
||||
},
|
||||
{
|
||||
// Note: this case will never arise as tokenize() will never return such a sequence of tokens
|
||||
// and even if it does it will be treated as [eof]
|
||||
tokens: []string{eof, "v1", eof},
|
||||
want: []segment{
|
||||
literal(eof),
|
||||
},
|
||||
},
|
||||
{
|
||||
tokens: []string{"v1", eof},
|
||||
want: []segment{
|
||||
literal("v1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
tokens: []string{"/", eof},
|
||||
want: []segment{
|
||||
wildcard{},
|
||||
},
|
||||
},
|
||||
{
|
||||
tokens: []string{"-._~!$&'()*+,;=:@", eof},
|
||||
want: []segment{
|
||||
literal("-._~!$&'()*+,;=:@"),
|
||||
},
|
||||
},
|
||||
{
|
||||
tokens: []string{"%e7%ac%ac%e4%b8%80%e7%89%88", eof},
|
||||
want: []segment{
|
||||
literal("%e7%ac%ac%e4%b8%80%e7%89%88"),
|
||||
},
|
||||
},
|
||||
{
|
||||
tokens: []string{"v1", "/", "*", eof},
|
||||
want: []segment{
|
||||
literal("v1"),
|
||||
wildcard{},
|
||||
},
|
||||
},
|
||||
{
|
||||
tokens: []string{"v1", "/", "**", eof},
|
||||
want: []segment{
|
||||
literal("v1"),
|
||||
deepWildcard{},
|
||||
},
|
||||
},
|
||||
{
|
||||
tokens: []string{"{", "name", "}", eof},
|
||||
want: []segment{
|
||||
variable{
|
||||
path: "name",
|
||||
segments: []segment{
|
||||
wildcard{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
tokens: []string{"{", "name", "=", "*", "}", eof},
|
||||
want: []segment{
|
||||
variable{
|
||||
path: "name",
|
||||
segments: []segment{
|
||||
wildcard{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
tokens: []string{"{", "field", ".", "nested", ".", "nested2", "=", "*", "}", eof},
|
||||
want: []segment{
|
||||
variable{
|
||||
path: "field.nested.nested2",
|
||||
segments: []segment{
|
||||
wildcard{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
tokens: []string{"{", "name", "=", "a", "/", "b", "/", "*", "}", eof},
|
||||
want: []segment{
|
||||
variable{
|
||||
path: "name",
|
||||
segments: []segment{
|
||||
literal("a"),
|
||||
literal("b"),
|
||||
wildcard{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
tokens: []string{
|
||||
"v1", "/",
|
||||
"{",
|
||||
"name", ".", "nested", ".", "nested2",
|
||||
"=",
|
||||
"a", "/", "b", "/", "*",
|
||||
"}", "/",
|
||||
"o", "/",
|
||||
"{",
|
||||
"another_name",
|
||||
"=",
|
||||
"a", "/", "b", "/", "*", "/", "c",
|
||||
"}", "/",
|
||||
"**",
|
||||
eof,
|
||||
},
|
||||
want: []segment{
|
||||
literal("v1"),
|
||||
variable{
|
||||
path: "name.nested.nested2",
|
||||
segments: []segment{
|
||||
literal("a"),
|
||||
literal("b"),
|
||||
wildcard{},
|
||||
},
|
||||
},
|
||||
literal("o"),
|
||||
variable{
|
||||
path: "another_name",
|
||||
segments: []segment{
|
||||
literal("a"),
|
||||
literal("b"),
|
||||
wildcard{},
|
||||
literal("c"),
|
||||
},
|
||||
},
|
||||
deepWildcard{},
|
||||
},
|
||||
},
|
||||
} {
|
||||
p := parser{tokens: spec.tokens}
|
||||
segs, err := p.topLevelSegments()
|
||||
if err != nil {
|
||||
t.Errorf("parser{%q}.segments() failed with %v; want success", spec.tokens, err)
|
||||
continue
|
||||
}
|
||||
if got, want := segs, spec.want; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("parser{%q}.segments() = %#v; want %#v", spec.tokens, got, want)
|
||||
}
|
||||
if got := p.tokens; len(got) > 0 {
|
||||
t.Errorf("p.tokens = %q; want []; spec.tokens=%q", got, spec.tokens)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSegmentsWithErrors(t *testing.T) {
|
||||
for _, spec := range []struct {
|
||||
tokens []string
|
||||
}{
|
||||
{
|
||||
// double slash
|
||||
tokens: []string{"//", eof},
|
||||
},
|
||||
{
|
||||
// invalid literal
|
||||
tokens: []string{"a?b", eof},
|
||||
},
|
||||
{
|
||||
// invalid percent-encoding
|
||||
tokens: []string{"%", eof},
|
||||
},
|
||||
{
|
||||
// invalid percent-encoding
|
||||
tokens: []string{"%2", eof},
|
||||
},
|
||||
{
|
||||
// invalid percent-encoding
|
||||
tokens: []string{"a%2z", eof},
|
||||
},
|
||||
{
|
||||
// unterminated variable
|
||||
tokens: []string{"{", "name", eof},
|
||||
},
|
||||
{
|
||||
// unterminated variable
|
||||
tokens: []string{"{", "name", "=", eof},
|
||||
},
|
||||
{
|
||||
// unterminated variable
|
||||
tokens: []string{"{", "name", "=", "*", eof},
|
||||
},
|
||||
{
|
||||
// empty component in field path
|
||||
tokens: []string{"{", "name", ".", "}", eof},
|
||||
},
|
||||
{
|
||||
// empty component in field path
|
||||
tokens: []string{"{", "name", ".", ".", "nested", "}", eof},
|
||||
},
|
||||
{
|
||||
// invalid character in identifier
|
||||
tokens: []string{"{", "field-name", "}", eof},
|
||||
},
|
||||
{
|
||||
// no slash between segments
|
||||
tokens: []string{"v1", "endpoint", eof},
|
||||
},
|
||||
{
|
||||
// no slash between segments
|
||||
tokens: []string{"v1", "{", "name", "}", eof},
|
||||
},
|
||||
} {
|
||||
p := parser{tokens: spec.tokens}
|
||||
segs, err := p.topLevelSegments()
|
||||
if err == nil {
|
||||
t.Errorf("parser{%q}.segments() succeeded; want InvalidTemplateError; accepted %#v", spec.tokens, segs)
|
||||
continue
|
||||
}
|
||||
logger.Info(context.TODO(), err.Error())
|
||||
}
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
package router
|
||||
|
||||
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/utilities/pattern.go
|
||||
|
||||
// An OpCode is a opcode of compiled path patterns.
|
||||
type OpCode int
|
||||
|
||||
// These constants are the valid values of OpCode.
|
||||
const (
|
||||
// OpNop does nothing
|
||||
OpNop = OpCode(iota)
|
||||
// OpPush pushes a component to stack
|
||||
OpPush
|
||||
// OpLitPush pushes a component to stack if it matches to the literal
|
||||
OpLitPush
|
||||
// OpPushM concatenates the remaining components and pushes it to stack
|
||||
OpPushM
|
||||
// OpConcatN pops N items from stack, concatenates them and pushes it back to stack
|
||||
OpConcatN
|
||||
// OpCapture pops an item and binds it to the variable
|
||||
OpCapture
|
||||
// OpEnd is the least positive invalid opcode.
|
||||
OpEnd
|
||||
)
|
@@ -1,32 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/unistack-org/micro/v3/register"
|
||||
"github.com/unistack-org/micro/v3/router"
|
||||
)
|
||||
|
||||
type apiRouter struct {
|
||||
router.Router
|
||||
routes []router.Route
|
||||
}
|
||||
|
||||
func (r *apiRouter) Lookup(...router.QueryOption) ([]router.Route, error) {
|
||||
return r.routes, nil
|
||||
}
|
||||
|
||||
func (r *apiRouter) String() string {
|
||||
return "api"
|
||||
}
|
||||
|
||||
// New router is a hack for API routing
|
||||
func New(srvs []*register.Service) router.Router {
|
||||
var routes []router.Route
|
||||
|
||||
for _, srv := range srvs {
|
||||
for _, n := range srv.Nodes {
|
||||
routes = append(routes, router.Route{Address: n.Address, Metadata: n.Metadata})
|
||||
}
|
||||
}
|
||||
|
||||
return &apiRouter{routes: routes}
|
||||
}
|
@@ -1,259 +0,0 @@
|
||||
package router
|
||||
|
||||
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/runtime/pattern.go
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/unistack-org/micro/v3/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotMatch indicates that the given HTTP request path does not match to the pattern.
|
||||
ErrNotMatch = errors.New("not match to the path pattern")
|
||||
// ErrInvalidPattern indicates that the given definition of Pattern is not valid.
|
||||
ErrInvalidPattern = errors.New("invalid pattern")
|
||||
)
|
||||
|
||||
type rop struct {
|
||||
code OpCode
|
||||
operand int
|
||||
}
|
||||
|
||||
// Pattern is a template pattern of http request paths defined in
|
||||
// https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
|
||||
type Pattern struct {
|
||||
// ops is a list of operations
|
||||
ops []rop
|
||||
// pool is a constant pool indexed by the operands or vars
|
||||
pool []string
|
||||
// vars is a list of variables names to be bound by this pattern
|
||||
vars []string
|
||||
// stacksize is the max depth of the stack
|
||||
stacksize int
|
||||
// tailLen is the length of the fixed-size segments after a deep wildcard
|
||||
tailLen int
|
||||
// verb is the VERB part of the path pattern. It is empty if the pattern does not have VERB part.
|
||||
verb string
|
||||
}
|
||||
|
||||
// NewPattern returns a new Pattern from the given definition values.
|
||||
// "ops" is a sequence of op codes. "pool" is a constant pool.
|
||||
// "verb" is the verb part of the pattern. It is empty if the pattern does not have the part.
|
||||
// "version" must be 1 for now.
|
||||
// It returns an error if the given definition is invalid.
|
||||
func NewPattern(version int, ops []int, pool []string, verb string) (Pattern, error) {
|
||||
if version != 1 {
|
||||
if logger.V(logger.TraceLevel) {
|
||||
logger.Trace(context.TODO(), "unsupported version: %d", version)
|
||||
}
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
|
||||
l := len(ops)
|
||||
if l%2 != 0 {
|
||||
if logger.V(logger.TraceLevel) {
|
||||
logger.Trace(context.TODO(), "odd number of ops codes: %d", l)
|
||||
}
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
|
||||
var (
|
||||
typedOps []rop
|
||||
stack, maxstack int
|
||||
tailLen int
|
||||
pushMSeen bool
|
||||
vars []string
|
||||
)
|
||||
for i := 0; i < l; i += 2 {
|
||||
op := rop{code: OpCode(ops[i]), operand: ops[i+1]}
|
||||
switch op.code {
|
||||
case OpNop:
|
||||
continue
|
||||
case OpPush:
|
||||
if pushMSeen {
|
||||
tailLen++
|
||||
}
|
||||
stack++
|
||||
case OpPushM:
|
||||
if pushMSeen {
|
||||
if logger.V(logger.TraceLevel) {
|
||||
logger.Trace(context.TODO(), "pushM appears twice")
|
||||
}
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
pushMSeen = true
|
||||
stack++
|
||||
case OpLitPush:
|
||||
if op.operand < 0 || len(pool) <= op.operand {
|
||||
if logger.V(logger.TraceLevel) {
|
||||
logger.Trace(context.TODO(), "negative literal index: %d", op.operand)
|
||||
}
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
if pushMSeen {
|
||||
tailLen++
|
||||
}
|
||||
stack++
|
||||
case OpConcatN:
|
||||
if op.operand <= 0 {
|
||||
if logger.V(logger.TraceLevel) {
|
||||
logger.Trace(context.TODO(), "negative concat size: %d", op.operand)
|
||||
}
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
stack -= op.operand
|
||||
if stack < 0 {
|
||||
if logger.V(logger.TraceLevel) {
|
||||
logger.Trace(context.TODO(), "stack underflow")
|
||||
}
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
stack++
|
||||
case OpCapture:
|
||||
if op.operand < 0 || len(pool) <= op.operand {
|
||||
if logger.V(logger.TraceLevel) {
|
||||
logger.Trace(context.TODO(), "variable name index out of bound: %d", op.operand)
|
||||
}
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
v := pool[op.operand]
|
||||
op.operand = len(vars)
|
||||
vars = append(vars, v)
|
||||
stack--
|
||||
if stack < 0 {
|
||||
if logger.V(logger.TraceLevel) {
|
||||
logger.Trace(context.TODO(), "stack underflow")
|
||||
}
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
default:
|
||||
if logger.V(logger.TraceLevel) {
|
||||
logger.Trace(context.TODO(), "invalid opcode: %d", op.code)
|
||||
}
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
|
||||
if maxstack < stack {
|
||||
maxstack = stack
|
||||
}
|
||||
typedOps = append(typedOps, op)
|
||||
}
|
||||
return Pattern{
|
||||
ops: typedOps,
|
||||
pool: pool,
|
||||
vars: vars,
|
||||
stacksize: maxstack,
|
||||
tailLen: tailLen,
|
||||
verb: verb,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MustPattern is a helper function which makes it easier to call NewPattern in variable initialization.
|
||||
func MustPattern(p Pattern, err error) Pattern {
|
||||
if err != nil {
|
||||
if logger.V(logger.FatalLevel) {
|
||||
logger.Fatal(context.TODO(), "Pattern initialization failed: %v", err)
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Match examines components if it matches to the Pattern.
|
||||
// If it matches, the function returns a mapping from field paths to their captured values.
|
||||
// If otherwise, the function returns an error.
|
||||
//nolint:gocyclo
|
||||
func (p Pattern) Match(components []string, verb string) (map[string]string, error) {
|
||||
if p.verb != verb {
|
||||
if p.verb != "" {
|
||||
return nil, ErrNotMatch
|
||||
}
|
||||
if len(components) == 0 {
|
||||
components = []string{":" + verb}
|
||||
} else {
|
||||
components = append([]string{}, components...)
|
||||
components[len(components)-1] += ":" + verb
|
||||
}
|
||||
}
|
||||
|
||||
var pos int
|
||||
stack := make([]string, 0, p.stacksize)
|
||||
captured := make([]string, len(p.vars))
|
||||
l := len(components)
|
||||
for _, op := range p.ops {
|
||||
switch op.code {
|
||||
case OpNop:
|
||||
continue
|
||||
case OpPush, OpLitPush:
|
||||
if pos >= l {
|
||||
return nil, ErrNotMatch
|
||||
}
|
||||
c := components[pos]
|
||||
if op.code == OpLitPush {
|
||||
if lit := p.pool[op.operand]; c != lit {
|
||||
return nil, ErrNotMatch
|
||||
}
|
||||
}
|
||||
stack = append(stack, c)
|
||||
pos++
|
||||
case OpPushM:
|
||||
end := len(components)
|
||||
if end < pos+p.tailLen {
|
||||
return nil, ErrNotMatch
|
||||
}
|
||||
end -= p.tailLen
|
||||
stack = append(stack, strings.Join(components[pos:end], "/"))
|
||||
pos = end
|
||||
case OpConcatN:
|
||||
n := op.operand
|
||||
l := len(stack) - n
|
||||
stack = append(stack[:l], strings.Join(stack[l:], "/"))
|
||||
case OpCapture:
|
||||
n := len(stack) - 1
|
||||
captured[op.operand] = stack[n]
|
||||
stack = stack[:n]
|
||||
}
|
||||
}
|
||||
if pos < l {
|
||||
return nil, ErrNotMatch
|
||||
}
|
||||
bindings := make(map[string]string, len(captured))
|
||||
for i, val := range captured {
|
||||
bindings[p.vars[i]] = val
|
||||
}
|
||||
return bindings, nil
|
||||
}
|
||||
|
||||
// Verb returns the verb part of the Pattern.
|
||||
func (p Pattern) Verb() string { return p.verb }
|
||||
|
||||
func (p Pattern) String() string {
|
||||
var stack []string
|
||||
for _, op := range p.ops {
|
||||
switch op.code {
|
||||
case OpNop:
|
||||
continue
|
||||
case OpPush:
|
||||
stack = append(stack, "*")
|
||||
case OpLitPush:
|
||||
stack = append(stack, p.pool[op.operand])
|
||||
case OpPushM:
|
||||
stack = append(stack, "**")
|
||||
case OpConcatN:
|
||||
n := op.operand
|
||||
l := len(stack) - n
|
||||
stack = append(stack[:l], strings.Join(stack[l:], "/"))
|
||||
case OpCapture:
|
||||
n := len(stack) - 1
|
||||
stack[n] = fmt.Sprintf("{%s=%s}", p.vars[op.operand], stack[n])
|
||||
}
|
||||
}
|
||||
segs := strings.Join(stack, "/")
|
||||
if p.verb != "" {
|
||||
return fmt.Sprintf("/%s:%s", segs, p.verb)
|
||||
}
|
||||
return "/" + segs
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
package router
|
||||
|
||||
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/types.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type template struct {
|
||||
segments []segment
|
||||
verb string
|
||||
template string
|
||||
}
|
||||
|
||||
type segment interface {
|
||||
fmt.Stringer
|
||||
compile() (ops []op)
|
||||
}
|
||||
|
||||
type wildcard struct{}
|
||||
|
||||
type deepWildcard struct{}
|
||||
|
||||
type literal string
|
||||
|
||||
type variable struct {
|
||||
path string
|
||||
segments []segment
|
||||
}
|
||||
|
||||
func (wildcard) String() string {
|
||||
return "*"
|
||||
}
|
||||
|
||||
func (deepWildcard) String() string {
|
||||
return "**"
|
||||
}
|
||||
|
||||
func (l literal) String() string {
|
||||
return string(l)
|
||||
}
|
||||
|
||||
func (v variable) String() string {
|
||||
segs := make([]string, 0, len(v.segments))
|
||||
for _, s := range v.segments {
|
||||
segs = append(segs, s.String())
|
||||
}
|
||||
return fmt.Sprintf("{%s=%s}", v.path, strings.Join(segs, "/"))
|
||||
}
|
||||
|
||||
func (t template) String() string {
|
||||
segs := make([]string, 0, len(t.segments))
|
||||
for _, s := range t.segments {
|
||||
segs = append(segs, s.String())
|
||||
}
|
||||
str := strings.Join(segs, "/")
|
||||
if t.verb != "" {
|
||||
str = fmt.Sprintf("%s:%s", str, t.verb)
|
||||
}
|
||||
return "/" + str
|
||||
}
|
@@ -1,93 +0,0 @@
|
||||
package router
|
||||
|
||||
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/types_test.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTemplateStringer(t *testing.T) {
|
||||
for _, spec := range []struct {
|
||||
segs []segment
|
||||
want string
|
||||
}{
|
||||
{
|
||||
segs: []segment{
|
||||
literal("v1"),
|
||||
},
|
||||
want: "/v1",
|
||||
},
|
||||
{
|
||||
segs: []segment{
|
||||
wildcard{},
|
||||
},
|
||||
want: "/*",
|
||||
},
|
||||
{
|
||||
segs: []segment{
|
||||
deepWildcard{},
|
||||
},
|
||||
want: "/**",
|
||||
},
|
||||
{
|
||||
segs: []segment{
|
||||
variable{
|
||||
path: "name",
|
||||
segments: []segment{
|
||||
literal("a"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: "/{name=a}",
|
||||
},
|
||||
{
|
||||
segs: []segment{
|
||||
variable{
|
||||
path: "name",
|
||||
segments: []segment{
|
||||
literal("a"),
|
||||
wildcard{},
|
||||
literal("b"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: "/{name=a/*/b}",
|
||||
},
|
||||
{
|
||||
segs: []segment{
|
||||
literal("v1"),
|
||||
variable{
|
||||
path: "name",
|
||||
segments: []segment{
|
||||
literal("a"),
|
||||
wildcard{},
|
||||
literal("b"),
|
||||
},
|
||||
},
|
||||
literal("c"),
|
||||
variable{
|
||||
path: "field.nested",
|
||||
segments: []segment{
|
||||
wildcard{},
|
||||
literal("d"),
|
||||
},
|
||||
},
|
||||
wildcard{},
|
||||
literal("e"),
|
||||
deepWildcard{},
|
||||
},
|
||||
want: "/v1/{name=a/*/b}/c/{field.nested=*/d}/*/e/**",
|
||||
},
|
||||
} {
|
||||
tmpl := template{segments: spec.segs}
|
||||
if got, want := tmpl.String(), spec.want; got != want {
|
||||
t.Errorf("%#v.String() = %q; want %q", tmpl, got, want)
|
||||
}
|
||||
|
||||
tmpl.verb = "LOCK"
|
||||
if got, want := tmpl.String(), fmt.Sprintf("%s:LOCK", spec.want); got != want {
|
||||
t.Errorf("%#v.String() = %q; want %q", tmpl, got, want)
|
||||
}
|
||||
}
|
||||
}
|
@@ -29,6 +29,7 @@ func Stores(stores ...store.Store) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: golint,revive
|
||||
// SyncInterval sets the duration between syncs from L0 to L1
|
||||
func SyncInterval(d time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
@@ -36,6 +37,7 @@ func SyncInterval(d time.Duration) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: golint,revive
|
||||
// SyncMultiplier sets the multiplication factor for time to wait each sync layer
|
||||
func SyncMultiplier(i int64) Option {
|
||||
return func(o *Options) {
|
||||
|
@@ -45,10 +45,10 @@ func (b *Basic) Generate(acc *auth.Account, opts ...token.GenerateOption) (*toke
|
||||
|
||||
// write to the store
|
||||
key, err := id.New()
|
||||
if err !=nil {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
err = b.store.Write(context.Background(), fmt.Sprintf("%v%v", StorePrefix, key), bytes, store.WriteTTL(options.Expiry))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -13,7 +13,7 @@ import (
|
||||
// authClaims to be encoded in the JWT
|
||||
type authClaims struct {
|
||||
Metadata metadata.Metadata `json:"metadata"`
|
||||
jwt.StandardClaims
|
||||
jwt.RegisteredClaims
|
||||
Type string `json:"type"`
|
||||
Scopes []string `json:"scopes"`
|
||||
}
|
||||
@@ -50,10 +50,10 @@ func (j *JWT) Generate(acc *auth.Account, opts ...token.GenerateOption) (*token.
|
||||
// generate the JWT
|
||||
expiry := time.Now().Add(options.Expiry)
|
||||
t := jwt.NewWithClaims(jwt.SigningMethodRS256, authClaims{
|
||||
Type: acc.Type, Scopes: acc.Scopes, Metadata: acc.Metadata, StandardClaims: jwt.StandardClaims{
|
||||
Type: acc.Type, Scopes: acc.Scopes, Metadata: acc.Metadata, RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: acc.ID,
|
||||
Issuer: acc.Issuer,
|
||||
ExpiresAt: expiry.Unix(),
|
||||
ExpiresAt: jwt.NewNumericDate(expiry),
|
||||
},
|
||||
})
|
||||
tok, err := t.SignedString(key)
|
||||
|
Reference in New Issue
Block a user