mirror of
https://github.com/kevin-DL/m3o-go.git
synced 2026-01-11 18:44:26 +00:00
First
This commit is contained in:
97
README.md
Normal file
97
README.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Go Micro Client
|
||||
|
||||
By default the client connects to api.micro.mu/client
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/micro/clients/go/client"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
var (
|
||||
token, _ = os.Getenv("TOKEN")
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := client.NewClient(nil)
|
||||
|
||||
// set your api token
|
||||
c.SetToken(token)
|
||||
|
||||
req := &Request{
|
||||
Name: "John",
|
||||
}
|
||||
var rsp Response
|
||||
|
||||
if err := c.Call("go.micro.srv.greeter", "Say.Hello", req, &rsp); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(rsp)
|
||||
}
|
||||
```
|
||||
|
||||
If you want to access your local micro:
|
||||
|
||||
```go
|
||||
c := client.NewClient(client.Options{Local: true})
|
||||
```
|
||||
|
||||
You can also set the api address explicitly:
|
||||
|
||||
```go
|
||||
c := client.NewClient(client.Options{Address: "https://api.yourdomain.com/client"})
|
||||
```
|
||||
|
||||
## Streaming
|
||||
|
||||
The client supports streaming
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/micro/clients/go/client"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Count string `json:"count"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Count string `json:"count"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
c := client.NewClient(&client.Options{Local: true})
|
||||
|
||||
stream, err := c.Stream("go.micro.srv.stream", "Streamer.ServerStream", Request{Count: "10"})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
var rsp Response
|
||||
if err := stream.Recv(&rsp); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println("got", rsp.Count)
|
||||
}
|
||||
}
|
||||
```
|
||||
207
client/client.go
Normal file
207
client/client.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
// local address for api
|
||||
localAddress = "http://localhost:8080/"
|
||||
// public address for api
|
||||
liveAddress = "https://api.micro.mu/"
|
||||
)
|
||||
|
||||
// Options of the Client
|
||||
type Options struct {
|
||||
// Token for authentication
|
||||
Token string
|
||||
// Address of the micro platform.
|
||||
// By default it connects to live. Change it or use the local flag
|
||||
// to connect to your local installation.
|
||||
Address string
|
||||
// Helper flag to help users connect to the default local address
|
||||
Local bool
|
||||
}
|
||||
|
||||
// Request is the request of the generic `api-client` call
|
||||
type Request struct {
|
||||
// eg. "go.micro.srv.greeter"
|
||||
Service string `json:"service"`
|
||||
// eg. "Say.Hello"
|
||||
Endpoint string `json:"endpoint"`
|
||||
// json and then base64 encoded body
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
// Response is the response of the generic `api-client` call.
|
||||
type Response struct {
|
||||
// json and base64 encoded response body
|
||||
Body string `json:"body"`
|
||||
// error fields. Error json example
|
||||
// {"id":"go.micro.client","code":500,"detail":"malformed method name: \"\"","status":"Internal Server Error"}
|
||||
Code int `json:"code"`
|
||||
ID string `json:"id"`
|
||||
Detail string `json:"detail"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// Client enables generic calls to micro
|
||||
type Client struct {
|
||||
options Options
|
||||
}
|
||||
|
||||
type Stream struct {
|
||||
conn *websocket.Conn
|
||||
service, endpoint string
|
||||
}
|
||||
|
||||
// NewClient returns a generic micro client that connects to live by default
|
||||
func NewClient(options *Options) *Client {
|
||||
ret := new(Client)
|
||||
if options != nil {
|
||||
ret.options = *options
|
||||
} else {
|
||||
ret.options = Options{
|
||||
Address: liveAddress,
|
||||
}
|
||||
}
|
||||
if options != nil && options.Local {
|
||||
ret.options.Address = localAddress
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// SetToken sets the api auth token
|
||||
func (client *Client) SetToken(t string) {
|
||||
client.options.Token = t
|
||||
}
|
||||
|
||||
// Call enables you to access any endpoint of any service on Micro
|
||||
func (client *Client) Call(service, endpoint string, request, response interface{}) error {
|
||||
// example curl: curl -XPOST -d '{"service": "go.micro.srv.greeter", "endpoint": "Say.Hello"}'
|
||||
// -H 'Content-Type: application/json' http://localhost:8080/client {"body":"eyJtc2ciOiJIZWxsbyAifQ=="}
|
||||
uri, err := url.Parse(client.options.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: make optional
|
||||
uri.Path = "/client"
|
||||
|
||||
b, err := marshalRequest(service, endpoint, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", uri.String(), bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set the token if it exists
|
||||
if len(client.options.Token) > 0 {
|
||||
req.Header.Set("Authorization", "Bearer " + client.options.Token)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
httpClient := &http.Client{}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return unmarshalResponse(body, response)
|
||||
}
|
||||
|
||||
// Stream enables the ability to stream via websockets
|
||||
func (client *Client) Stream(service, endpoint string, request interface{}) (*Stream, error) {
|
||||
b, err := marshalRequest(service, endpoint, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uri, err := url.Parse(client.options.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: make optional
|
||||
uri.Path = "/client/stream"
|
||||
|
||||
// replace http with websocket
|
||||
uri.Scheme = strings.Replace(uri.Scheme, "http", "ws", 1)
|
||||
|
||||
// create the headers
|
||||
header := make(http.Header)
|
||||
// set the token if it exists
|
||||
if len(client.options.Token) > 0 {
|
||||
header.Set("Authorization", "Bearer " + client.options.Token)
|
||||
}
|
||||
header.Set("Content-Type", "application/json")
|
||||
|
||||
// dial the connection
|
||||
conn, _, err := websocket.DefaultDialer.Dial(uri.String(), header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// send the first request
|
||||
if err := conn.WriteMessage(websocket.TextMessage, b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Stream{conn, service, endpoint}, nil
|
||||
}
|
||||
|
||||
func (s *Stream) Recv(v interface{}) error {
|
||||
// read response
|
||||
_, message, err := s.conn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return unmarshalResponse(message, v)
|
||||
}
|
||||
|
||||
func (s *Stream) Send(v interface{}) error {
|
||||
b, err := marshalRequest(s.service, s.endpoint, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.conn.WriteMessage(websocket.TextMessage, b)
|
||||
}
|
||||
|
||||
func marshalRequest(service, endpoint string, v interface{}) ([]byte, error) {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(&Request{
|
||||
Service: service,
|
||||
Endpoint: endpoint,
|
||||
Body: base64.StdEncoding.EncodeToString(b),
|
||||
})
|
||||
}
|
||||
|
||||
func unmarshalResponse(body []byte, v interface{}) error {
|
||||
rsp := new(Response)
|
||||
if err := json.Unmarshal(body, rsp); err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := base64.StdEncoding.DecodeString(rsp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(b, v)
|
||||
}
|
||||
22
client/client_test.go
Normal file
22
client/client_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package client
|
||||
|
||||
import "testing"
|
||||
|
||||
type req struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type rsp struct {
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
func TestBasicCall(t *testing.T) {
|
||||
response := rsp{}
|
||||
if err := NewClient(nil).Call("go.micro.srv.greeter", "Say.Hello", req{Name: "John"}, &response); err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if response.Msg != "Hello John" {
|
||||
t.Logf("Message is not as expected: %v", response.Msg)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
5
client/go.mod
Normal file
5
client/go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module github.com/micro/clients/go/client
|
||||
|
||||
go 1.13
|
||||
|
||||
require github.com/gorilla/websocket v1.4.1
|
||||
2
client/go.sum
Normal file
2
client/go.sum
Normal file
@@ -0,0 +1,2 @@
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
Reference in New Issue
Block a user