2019-06-03 18:44:43 +01:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
|
2021-01-10 03:55:04 +03:00
|
|
|
"github.com/unistack-org/micro/v3/metadata"
|
2021-01-29 13:17:32 +03:00
|
|
|
"github.com/unistack-org/micro/v3/register"
|
2020-08-19 17:47:17 +03:00
|
|
|
"github.com/unistack-org/micro/v3/server"
|
2019-06-03 18:44:43 +01:00
|
|
|
)
|
|
|
|
|
2021-02-14 16:16:01 +03:00
|
|
|
// Api interface
|
2020-03-30 11:04:59 +03:00
|
|
|
type Api interface {
|
2020-04-01 12:07:50 +01:00
|
|
|
// Initialise options
|
|
|
|
Init(...Option) error
|
|
|
|
// Get the options
|
|
|
|
Options() Options
|
2020-03-30 11:04:59 +03:00
|
|
|
// Register a http handler
|
|
|
|
Register(*Endpoint) error
|
|
|
|
// Register a route
|
|
|
|
Deregister(*Endpoint) error
|
2020-07-16 23:33:11 +08:00
|
|
|
// Implementation of api
|
2020-03-30 11:04:59 +03:00
|
|
|
String() string
|
|
|
|
}
|
|
|
|
|
2021-02-14 16:16:01 +03:00
|
|
|
// Options holds the options
|
2020-04-07 17:38:27 +03:00
|
|
|
type Options struct{}
|
2020-04-01 12:07:50 +01:00
|
|
|
|
2021-02-14 16:16:01 +03:00
|
|
|
// Option func signature
|
2020-04-01 12:07:50 +01:00
|
|
|
type Option func(*Options) error
|
|
|
|
|
2019-06-03 18:44:43 +01:00
|
|
|
// Endpoint is a mapping between an RPC method and HTTP endpoint
|
|
|
|
type Endpoint struct {
|
2021-02-14 16:16:01 +03:00
|
|
|
// Name Greeter.Hello
|
2019-06-03 18:44:43 +01:00
|
|
|
Name string
|
|
|
|
// Description e.g what's this endpoint for
|
|
|
|
Description string
|
2021-02-14 16:16:01 +03:00
|
|
|
// Handler e.g rpc, proxy
|
2019-06-03 18:44:43 +01:00
|
|
|
Handler string
|
2021-02-14 16:16:01 +03:00
|
|
|
// Host e.g example.com
|
2019-06-03 18:44:43 +01:00
|
|
|
Host []string
|
2021-02-14 16:16:01 +03:00
|
|
|
// Method e.g GET, POST
|
2019-06-03 18:44:43 +01:00
|
|
|
Method []string
|
2021-02-14 16:16:01 +03:00
|
|
|
// Path e.g /greeter. Expect POSIX regex
|
2019-06-03 18:44:43 +01:00
|
|
|
Path []string
|
2020-04-07 17:38:27 +03:00
|
|
|
// Body destination
|
|
|
|
// "*" or "" - top level message value
|
|
|
|
// "string" - inner message value
|
|
|
|
Body string
|
2020-03-30 11:04:59 +03:00
|
|
|
// Stream flag
|
|
|
|
Stream bool
|
2019-06-03 18:44:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Service represents an API service
|
|
|
|
type Service struct {
|
|
|
|
// Name of service
|
|
|
|
Name string
|
2021-02-14 16:16:01 +03:00
|
|
|
// Endpoint for this service
|
2019-06-03 18:44:43 +01:00
|
|
|
Endpoint *Endpoint
|
2021-02-14 16:16:01 +03:00
|
|
|
// Services that provides service
|
2021-01-29 13:17:32 +03:00
|
|
|
Services []*register.Service
|
2019-06-03 18:44:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Encode encodes an endpoint to endpoint metadata
|
|
|
|
func Encode(e *Endpoint) map[string]string {
|
|
|
|
if e == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-01-06 22:22:36 +00:00
|
|
|
// endpoint map
|
|
|
|
ep := make(map[string]string)
|
|
|
|
|
|
|
|
// set vals only if they exist
|
|
|
|
set := func(k, v string) {
|
|
|
|
if len(v) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ep[k] = v
|
2019-06-03 18:44:43 +01:00
|
|
|
}
|
2020-01-06 22:22:36 +00:00
|
|
|
|
|
|
|
set("endpoint", e.Name)
|
|
|
|
set("description", e.Description)
|
|
|
|
set("handler", e.Handler)
|
|
|
|
set("method", strings.Join(e.Method, ","))
|
|
|
|
set("path", strings.Join(e.Path, ","))
|
|
|
|
set("host", strings.Join(e.Host, ","))
|
2021-02-02 19:35:16 +03:00
|
|
|
set("body", e.Body)
|
2020-01-06 22:22:36 +00:00
|
|
|
|
|
|
|
return ep
|
2019-06-03 18:44:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Decode decodes endpoint metadata into an endpoint
|
2021-01-10 03:55:04 +03:00
|
|
|
func Decode(e metadata.Metadata) *Endpoint {
|
2019-06-03 18:44:43 +01:00
|
|
|
if e == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-01-10 03:55:04 +03:00
|
|
|
ep := &Endpoint{}
|
|
|
|
ep.Name, _ = e.Get("endpoint")
|
|
|
|
ep.Description, _ = e.Get("description")
|
|
|
|
epmethod, _ := e.Get("method")
|
|
|
|
ep.Method = []string{epmethod}
|
|
|
|
eppath, _ := e.Get("path")
|
|
|
|
ep.Path = []string{eppath}
|
|
|
|
ephost, _ := e.Get("host")
|
|
|
|
ep.Host = []string{ephost}
|
|
|
|
ep.Handler, _ = e.Get("handler")
|
2021-02-02 19:35:16 +03:00
|
|
|
ep.Body, _ = e.Get("body")
|
2021-01-10 03:55:04 +03:00
|
|
|
|
|
|
|
return ep
|
2019-06-03 18:44:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Validate validates an endpoint to guarantee it won't blow up when being served
|
|
|
|
func Validate(e *Endpoint) error {
|
|
|
|
if e == nil {
|
|
|
|
return errors.New("endpoint is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(e.Name) == 0 {
|
|
|
|
return errors.New("name required")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, p := range e.Path {
|
2020-04-29 15:23:10 +03:00
|
|
|
ps := p[0]
|
|
|
|
pe := p[len(p)-1]
|
|
|
|
|
|
|
|
if ps == '^' && pe == '$' {
|
|
|
|
_, err := regexp.CompilePOSIX(p)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if ps == '^' && pe != '$' {
|
|
|
|
return errors.New("invalid path")
|
|
|
|
} else if ps != '^' && pe == '$' {
|
|
|
|
return errors.New("invalid path")
|
2019-06-03 18:44:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(e.Handler) == 0 {
|
|
|
|
return errors.New("invalid handler")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Design ideas
|
|
|
|
|
|
|
|
// Gateway is an api gateway interface
|
|
|
|
type Gateway interface {
|
|
|
|
// Register a http handler
|
|
|
|
Handle(pattern string, http.Handler)
|
|
|
|
// Register a route
|
|
|
|
RegisterRoute(r Route)
|
|
|
|
// Init initialises the command line.
|
|
|
|
// It also parses further options.
|
|
|
|
Init(...Option) error
|
|
|
|
// Run the gateway
|
|
|
|
Run() error
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewGateway returns a new api gateway
|
|
|
|
func NewGateway() Gateway {
|
|
|
|
return newGateway()
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
// WithEndpoint returns a server.HandlerOption with endpoint metadata set
|
|
|
|
//
|
|
|
|
// Usage:
|
|
|
|
//
|
|
|
|
// proto.RegisterHandler(service.Server(), new(Handler), api.WithEndpoint(
|
|
|
|
// &api.Endpoint{
|
|
|
|
// Name: "Greeter.Hello",
|
|
|
|
// Path: []string{"/greeter"},
|
|
|
|
// },
|
|
|
|
// ))
|
|
|
|
func WithEndpoint(e *Endpoint) server.HandlerOption {
|
|
|
|
return server.EndpointMetadata(e.Name, Encode(e))
|
|
|
|
}
|