package main
import (
"context"
"encoding/json"
"errors"
"log"
"strings"
"flag"
"net/http"
"github.com/go-kit/kit/endpoint"
natstransport "github.com/go-kit/kit/transport/nats"
httptransport "github.com/go-kit/kit/transport/http"
"github.com/nats-io/nats.go"
)
// StringService provides operations on strings.
type StringService interface {
Uppercase(context.Context, string) (string, error)
Count(context.Context, string) int
}
// stringService is a concrete implementation of StringService
type stringService struct{}
func (stringService) Uppercase(_ context.Context, s string) (string, error) {
if s == "" {
return "", ErrEmpty
}
return strings.ToUpper(s), nil
}
func (stringService) Count(_ context.Context, s string) int {
return len(s)
}
// ErrEmpty is returned when an input string is empty.
var ErrEmpty = errors.New("empty string")
// For each method, we define request and response structs
type uppercaseRequest struct {
S string `json:"s"`
}
type uppercaseResponse struct {
V string `json:"v"`
Err string `json:"err,omitempty"` // errors don't define JSON marshaling
}
type countRequest struct {
S string `json:"s"`
}
type countResponse struct {
V int `json:"v"`
}
// Endpoints are a primary abstraction in go-kit. An endpoint represents a single RPC (method in our service interface)
func makeUppercaseHTTPEndpoint(nc *nats.Conn) endpoint.Endpoint {
return natstransport.NewPublisher(
nc,
"stringsvc.uppercase",
natstransport.EncodeJSONRequest,
decodeUppercaseResponse,
).Endpoint()
}
func makeCountHTTPEndpoint(nc *nats.Conn) endpoint.Endpoint {
return natstransport.NewPublisher(
nc,
"stringsvc.count",
natstransport.EncodeJSONRequest,
decodeCountResponse,
).Endpoint()
}
func makeUppercaseEndpoint(svc StringService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(uppercaseRequest)
v, err := svc.Uppercase(ctx, req.S)
if err != nil {
return uppercaseResponse{v, err.Error()}, nil
}
return uppercaseResponse{v, ""}, nil
}
}
func makeCountEndpoint(svc StringService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(countRequest)
v := svc.Count(ctx, req.S)
return countResponse{v}, nil
}
}
// Transports expose the service to the network. In this fourth example we utilize JSON over NATS and HTTP.
func main() {
svc := stringService{}
natsURL := flag.String("nats-url", nats.DefaultURL, "URL for connection to NATS")
flag.Parse()
nc, err := nats.Connect(*natsURL)
if err != nil {
log.Fatal(err)
}
defer nc.Close()
uppercaseHTTPHandler := httptransport.NewServer(
makeUppercaseHTTPEndpoint(nc),
decodeUppercaseHTTPRequest,
httptransport.EncodeJSONResponse,
)
countHTTPHandler := httptransport.NewServer(
makeCountHTTPEndpoint(nc),
decodeCountHTTPRequest,
httptransport.EncodeJSONResponse,
)
uppercaseHandler := natstransport.NewSubscriber(
makeUppercaseEndpoint(svc),
decodeUppercaseRequest,
natstransport.EncodeJSONResponse,
)
countHandler := natstransport.NewSubscriber(
makeCountEndpoint(svc),
decodeCountRequest,
natstransport.EncodeJSONResponse,
)
uSub, err := nc.QueueSubscribe("stringsvc.uppercase", "stringsvc", uppercaseHandler.ServeMsg(nc))
if err != nil {
log.Fatal(err)
}
defer uSub.Unsubscribe()
cSub, err := nc.QueueSubscribe("stringsvc.count", "stringsvc", countHandler.ServeMsg(nc))
if err != nil {
log.Fatal(err)
}
defer cSub.Unsubscribe()
http.Handle("/uppercase", uppercaseHTTPHandler)
http.Handle("/count", countHTTPHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func decodeUppercaseHTTPRequest(_ context.Context, r *http.Request) (interface{}, error) {
var request uppercaseRequest
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
return nil, err
}
return request, nil
}
func decodeCountHTTPRequest(_ context.Context, r *http.Request) (interface{}, error) {
var request countRequest
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
return nil, err
}
return request, nil
}
func decodeUppercaseResponse(_ context.Context, msg *nats.Msg) (interface{}, error) {
var response uppercaseResponse
if err := json.Unmarshal(msg.Data, &response); err != nil {
return nil, err
}
return response, nil
}
func decodeCountResponse(_ context.Context, msg *nats.Msg) (interface{}, error) {
var response countResponse
if err := json.Unmarshal(msg.Data, &response); err != nil {
return nil, err
}
return response, nil
}
func decodeUppercaseRequest(_ context.Context, msg *nats.Msg) (interface{}, error) {
var request uppercaseRequest
if err := json.Unmarshal(msg.Data, &request); err != nil {
return nil, err
}
return request, nil
}
func decodeCountRequest(_ context.Context, msg *nats.Msg) (interface{}, error) {
var request countRequest
if err := json.Unmarshal(msg.Data, &request); err != nil {
return nil, err
}
return request, nil
}