github.com/btccom/go-micro/v2@v2.9.3/api/api.go (about)

     1  package api
     2  
     3  import (
     4  	"errors"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/btccom/go-micro/v2/registry"
     9  	"github.com/btccom/go-micro/v2/server"
    10  )
    11  
    12  type Api interface {
    13  	// Initialise options
    14  	Init(...Option) error
    15  	// Get the options
    16  	Options() Options
    17  	// Register a http handler
    18  	Register(*Endpoint) error
    19  	// Register a route
    20  	Deregister(*Endpoint) error
    21  	// Implemenation of api
    22  	String() string
    23  }
    24  
    25  type Options struct{}
    26  
    27  type Option func(*Options) error
    28  
    29  // Endpoint is a mapping between an RPC method and HTTP endpoint
    30  type Endpoint struct {
    31  	// RPC Method e.g. Greeter.Hello
    32  	Name string
    33  	// Description e.g what's this endpoint for
    34  	Description string
    35  	// API Handler e.g rpc, proxy
    36  	Handler string
    37  	// HTTP Host e.g example.com
    38  	Host []string
    39  	// HTTP Methods e.g GET, POST
    40  	Method []string
    41  	// HTTP Path e.g /greeter. Expect POSIX regex
    42  	Path []string
    43  	// Body destination
    44  	// "*" or "" - top level message value
    45  	// "string" - inner message value
    46  	Body string
    47  	// Stream flag
    48  	Stream bool
    49  }
    50  
    51  // Service represents an API service
    52  type Service struct {
    53  	// Name of service
    54  	Name string
    55  	// The endpoint for this service
    56  	Endpoint *Endpoint
    57  	// Versions of this service
    58  	Services []*registry.Service
    59  }
    60  
    61  func strip(s string) string {
    62  	return strings.TrimSpace(s)
    63  }
    64  
    65  func slice(s string) []string {
    66  	var sl []string
    67  
    68  	for _, p := range strings.Split(s, ",") {
    69  		if str := strip(p); len(str) > 0 {
    70  			sl = append(sl, strip(p))
    71  		}
    72  	}
    73  
    74  	return sl
    75  }
    76  
    77  // Encode encodes an endpoint to endpoint metadata
    78  func Encode(e *Endpoint) map[string]string {
    79  	if e == nil {
    80  		return nil
    81  	}
    82  
    83  	// endpoint map
    84  	ep := make(map[string]string)
    85  
    86  	// set vals only if they exist
    87  	set := func(k, v string) {
    88  		if len(v) == 0 {
    89  			return
    90  		}
    91  		ep[k] = v
    92  	}
    93  
    94  	set("endpoint", e.Name)
    95  	set("description", e.Description)
    96  	set("handler", e.Handler)
    97  	set("method", strings.Join(e.Method, ","))
    98  	set("path", strings.Join(e.Path, ","))
    99  	set("host", strings.Join(e.Host, ","))
   100  
   101  	return ep
   102  }
   103  
   104  // Decode decodes endpoint metadata into an endpoint
   105  func Decode(e map[string]string) *Endpoint {
   106  	if e == nil {
   107  		return nil
   108  	}
   109  
   110  	return &Endpoint{
   111  		Name:        e["endpoint"],
   112  		Description: e["description"],
   113  		Method:      slice(e["method"]),
   114  		Path:        slice(e["path"]),
   115  		Host:        slice(e["host"]),
   116  		Handler:     e["handler"],
   117  	}
   118  }
   119  
   120  // Validate validates an endpoint to guarantee it won't blow up when being served
   121  func Validate(e *Endpoint) error {
   122  	if e == nil {
   123  		return errors.New("endpoint is nil")
   124  	}
   125  
   126  	if len(e.Name) == 0 {
   127  		return errors.New("name required")
   128  	}
   129  
   130  	for _, p := range e.Path {
   131  		ps := p[0]
   132  		pe := p[len(p)-1]
   133  
   134  		if ps == '^' && pe == '$' {
   135  			_, err := regexp.CompilePOSIX(p)
   136  			if err != nil {
   137  				return err
   138  			}
   139  		} else if ps == '^' && pe != '$' {
   140  			return errors.New("invalid path")
   141  		} else if ps != '^' && pe == '$' {
   142  			return errors.New("invalid path")
   143  		}
   144  	}
   145  
   146  	if len(e.Handler) == 0 {
   147  		return errors.New("invalid handler")
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  /*
   154  Design ideas
   155  
   156  // Gateway is an api gateway interface
   157  type Gateway interface {
   158  	// Register a http handler
   159  	Handle(pattern string, http.Handler)
   160  	// Register a route
   161  	RegisterRoute(r Route)
   162  	// Init initialises the command line.
   163  	// It also parses further options.
   164  	Init(...Option) error
   165  	// Run the gateway
   166  	Run() error
   167  }
   168  
   169  // NewGateway returns a new api gateway
   170  func NewGateway() Gateway {
   171  	return newGateway()
   172  }
   173  */
   174  
   175  // WithEndpoint returns a server.HandlerOption with endpoint metadata set
   176  //
   177  // Usage:
   178  //
   179  // 	proto.RegisterHandler(service.Server(), new(Handler), api.WithEndpoint(
   180  //		&api.Endpoint{
   181  //			Name: "Greeter.Hello",
   182  //			Path: []string{"/greeter"},
   183  //		},
   184  //	))
   185  func WithEndpoint(e *Endpoint) server.HandlerOption {
   186  	return server.EndpointMetadata(e.Name, Encode(e))
   187  }