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 }