Further consolidate the libraries

This commit is contained in:
Asim Aslam
2019-06-03 18:44:43 +01:00
parent fe060b2d0b
commit b42b6fa0fc
110 changed files with 11934 additions and 83 deletions

38
api/resolver/grpc/grpc.go Normal file
View File

@@ -0,0 +1,38 @@
// Package grpc resolves a grpc service like /greeter.Say/Hello to greeter service
package grpc
import (
"errors"
"net/http"
"strings"
"github.com/micro/go-micro/api/resolver"
)
type Resolver struct{}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
// /foo.Bar/Service
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
}
// [foo.Bar, Service]
parts := strings.Split(req.URL.Path[1:], "/")
// [foo, Bar]
name := strings.Split(parts[0], ".")
// foo
return &resolver.Endpoint{
Name: strings.Join(name[:len(name)-1], "."),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
func (r *Resolver) String() string {
return "grpc"
}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{}
}

27
api/resolver/host/host.go Normal file
View File

@@ -0,0 +1,27 @@
// Package host resolves using http host
package host
import (
"net/http"
"github.com/micro/go-micro/api/resolver"
)
type Resolver struct{}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
return &resolver.Endpoint{
Name: req.Host,
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
func (r *Resolver) String() string {
return "host"
}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{}
}

View File

@@ -0,0 +1,45 @@
// Package micro provides a micro rpc resolver which prefixes a namespace
package micro
import (
"net/http"
"github.com/micro/go-micro/api/resolver"
)
// default resolver for legacy purposes
// it uses proxy routing to resolve names
// /foo becomes namespace.foo
// /v1/foo becomes namespace.v1.foo
type Resolver struct {
Options resolver.Options
}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
var name, method string
switch r.Options.Handler {
// internal handlers
case "meta", "api", "rpc", "micro":
name, method = apiRoute(req.URL.Path)
default:
method = req.Method
name = proxyRoute(req.URL.Path)
}
return &resolver.Endpoint{
Name: name,
Method: method,
}, nil
}
func (r *Resolver) String() string {
return "micro"
}
// NewResolver creates a new micro resolver
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{
Options: resolver.NewOptions(opts...),
}
}

View File

@@ -0,0 +1,90 @@
package micro
import (
"path"
"regexp"
"strings"
)
var (
proxyRe = regexp.MustCompile("^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$")
versionRe = regexp.MustCompilePOSIX("^v[0-9]+$")
)
// Translates /foo/bar/zool into api service go.micro.api.foo method Bar.Zool
// Translates /foo/bar into api service go.micro.api.foo method Foo.Bar
func apiRoute(p string) (string, string) {
p = path.Clean(p)
p = strings.TrimPrefix(p, "/")
parts := strings.Split(p, "/")
// If we've got two or less parts
// Use first part as service
// Use all parts as method
if len(parts) <= 2 {
name := parts[0]
return name, methodName(parts)
}
// Treat /v[0-9]+ as versioning where we have 3 parts
// /v1/foo/bar => service: v1.foo method: Foo.bar
if len(parts) == 3 && versionRe.Match([]byte(parts[0])) {
name := strings.Join(parts[:len(parts)-1], ".")
return name, methodName(parts[len(parts)-2:])
}
// Service is everything minus last two parts
// Method is the last two parts
name := strings.Join(parts[:len(parts)-2], ".")
return name, methodName(parts[len(parts)-2:])
}
func proxyRoute(p string) string {
parts := strings.Split(p, "/")
if len(parts) < 2 {
return ""
}
var service string
var alias string
// /[service]/methods
if len(parts) > 2 {
// /v1/[service]
if versionRe.MatchString(parts[1]) {
service = parts[1] + "." + parts[2]
alias = parts[2]
} else {
service = parts[1]
alias = parts[1]
}
// /[service]
} else {
service = parts[1]
alias = parts[1]
}
// check service name is valid
if !proxyRe.MatchString(alias) {
return ""
}
return service
}
func methodName(parts []string) string {
for i, part := range parts {
parts[i] = toCamel(part)
}
return strings.Join(parts, ".")
}
func toCamel(s string) string {
words := strings.Split(s, "-")
var out string
for _, word := range words {
out += strings.Title(word)
}
return out
}

View File

@@ -0,0 +1,130 @@
package micro
import (
"testing"
)
func TestApiRoute(t *testing.T) {
testData := []struct {
path string
service string
method string
}{
{
"/foo/bar",
"foo",
"Foo.Bar",
},
{
"/foo/foo/bar",
"foo",
"Foo.Bar",
},
{
"/foo/bar/baz",
"foo",
"Bar.Baz",
},
{
"/foo/bar/baz-xyz",
"foo",
"Bar.BazXyz",
},
{
"/foo/bar/baz/cat",
"foo.bar",
"Baz.Cat",
},
{
"/foo/bar/baz/cat/car",
"foo.bar.baz",
"Cat.Car",
},
{
"/foo/fooBar/bazCat",
"foo",
"FooBar.BazCat",
},
{
"/v1/foo/bar",
"v1.foo",
"Foo.Bar",
},
{
"/v1/foo/bar/baz",
"v1.foo",
"Bar.Baz",
},
{
"/v1/foo/bar/baz/cat",
"v1.foo.bar",
"Baz.Cat",
},
}
for _, d := range testData {
s, m := apiRoute(d.path)
if d.service != s {
t.Fatalf("Expected service: %s for path: %s got: %s %s", d.service, d.path, s, m)
}
if d.method != m {
t.Fatalf("Expected service: %s for path: %s got: %s", d.method, d.path, m)
}
}
}
func TestProxyRoute(t *testing.T) {
testData := []struct {
path string
service string
}{
// no namespace
{
"/f",
"f",
},
{
"/f",
"f",
},
{
"/f-b",
"f-b",
},
{
"/foo/bar",
"foo",
},
{
"/foo-bar",
"foo-bar",
},
{
"/foo-bar-baz",
"foo-bar-baz",
},
{
"/foo/bar/bar",
"foo",
},
{
"/v1/foo/bar",
"v1.foo",
},
{
"/v1/foo/bar/baz",
"v1.foo",
},
{
"/v1/foo/bar/baz/cat",
"v1.foo",
},
}
for _, d := range testData {
s := proxyRoute(d.path)
if d.service != s {
t.Fatalf("Expected service: %s for path: %s got: %s", d.service, d.path, s)
}
}
}

24
api/resolver/options.go Normal file
View File

@@ -0,0 +1,24 @@
package resolver
// NewOptions returns new initialised options
func NewOptions(opts ...Option) Options {
var options Options
for _, o := range opts {
o(&options)
}
return options
}
// WithHandler sets the handler being used
func WithHandler(h string) Option {
return func(o *Options) {
o.Handler = h
}
}
// WithNamespace sets the namespace being used
func WithNamespace(n string) Option {
return func(o *Options) {
o.Namespace = n
}
}

33
api/resolver/path/path.go Normal file
View File

@@ -0,0 +1,33 @@
// Package path resolves using http path
package path
import (
"errors"
"net/http"
"strings"
"github.com/micro/go-micro/api/resolver"
)
type Resolver struct{}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
}
parts := strings.Split(req.URL.Path[1:], "/")
return &resolver.Endpoint{
Name: parts[0],
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
func (r *Resolver) String() string {
return "path"
}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{}
}

31
api/resolver/resolver.go Normal file
View File

@@ -0,0 +1,31 @@
// Package resolver resolves a http request to an endpoint
package resolver
import (
"net/http"
)
// Resolver resolves requests to endpoints
type Resolver interface {
Resolve(r *http.Request) (*Endpoint, error)
String() string
}
// Endpoint is the endpoint for a http request
type Endpoint struct {
// e.g greeter
Name string
// HTTP Host e.g example.com
Host string
// HTTP Methods e.g GET, POST
Method string
// HTTP Path e.g /greeter.
Path string
}
type Options struct {
Handler string
Namespace string
}
type Option func(o *Options)

View File

@@ -0,0 +1,59 @@
// Package vpath resolves using http path and recognised versioned urls
package vpath
import (
"errors"
"net/http"
"regexp"
"strings"
"github.com/micro/go-micro/api/resolver"
)
type Resolver struct{}
var (
re = regexp.MustCompile("^v[0-9]+$")
)
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
}
parts := strings.Split(req.URL.Path[1:], "/")
if len(parts) == 1 {
return &resolver.Endpoint{
Name: parts[0],
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
// /v1/foo
if re.MatchString(parts[0]) {
return &resolver.Endpoint{
Name: parts[1],
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
return &resolver.Endpoint{
Name: parts[0],
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
func (r *Resolver) String() string {
return "path"
}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{}
}