Skip to content

Commit

Permalink
[CHANGED] Use interface instead of struct for micro.Request
Browse files Browse the repository at this point in the history
  • Loading branch information
piotrpio committed Dec 29, 2022
1 parent 07b2b8f commit 9c1dfba
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 221 deletions.
20 changes: 7 additions & 13 deletions micro/example_package_test.go
Expand Up @@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package micro
package micro_test

import (
"fmt"
Expand All @@ -20,21 +20,19 @@ import (
"time"

"github.com/nats-io/nats.go"
"github.com/nats-io/nats.go/micro"
)

func Example() {
s := RunServerOnPort(-1)
defer s.Shutdown()

nc, err := nats.Connect(s.ClientURL())
nc, err := nats.Connect("127.0.0.1:4222")
if err != nil {
log.Fatal(err)
}
defer nc.Close()

// Service handler is a function which takes Service.Request as argument.
// req.Respond or req.Error should be used to respond to the request.
incrementHandler := func(req *Request) {
incrementHandler := func(req micro.Request) {
val, err := strconv.Atoi(string(req.Data()))
if err != nil {
req.Error("400", "request data should be a number", nil)
Expand All @@ -45,11 +43,11 @@ func Example() {
req.Respond([]byte(strconv.Itoa(responseData)))
}

config := Config{
config := micro.Config{
Name: "IncrementService",
Version: "0.1.0",
Description: "Increment numbers",
Endpoint: Endpoint{
Endpoint: micro.Endpoint{
// service handler
Handler: incrementHandler,
// a unique subject serving as a service endpoint
Expand All @@ -59,7 +57,7 @@ func Example() {
// Multiple instances of the servcice with the same name can be created.
// Requests to a service with the same name will be load-balanced.
for i := 0; i < 5; i++ {
svc, err := AddService(nc, config)
svc, err := micro.AddService(nc, config)
if err != nil {
log.Fatal(err)
}
Expand All @@ -76,8 +74,4 @@ func Example() {
log.Fatal(err)
}
fmt.Println(responseVal)

//
// Output: 4
//
}
69 changes: 35 additions & 34 deletions micro/example_test.go
Expand Up @@ -11,14 +11,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package micro
package micro_test

import (
"fmt"
"log"
"reflect"

"github.com/nats-io/nats.go"
"github.com/nats-io/nats.go/micro"
)

func ExampleAddService() {
Expand All @@ -28,33 +29,33 @@ func ExampleAddService() {
}
defer nc.Close()

echoHandler := func(req *Request) {
echoHandler := func(req micro.Request) {
req.Respond(req.Data())
}

config := Config{
config := micro.Config{
Name: "EchoService",
Version: "v1.0.0",
Description: "Send back what you receive",
Endpoint: Endpoint{
Endpoint: micro.Endpoint{
Subject: "echo",
Handler: echoHandler,
},

// DoneHandler can be set to customize behavior on stopping a service.
DoneHandler: func(srv Service) {
DoneHandler: func(srv micro.Service) {
info := srv.Info()
fmt.Printf("stopped service %q with ID %q\n", info.Name, info.ID)
},

// ErrorHandler can be used to customize behavior on service execution error.
ErrorHandler: func(srv Service, err *NATSError) {
ErrorHandler: func(srv micro.Service, err *micro.NATSError) {
info := srv.Info()
fmt.Printf("Service %q returned an error on subject %q: %s", info.Name, err.Subject, err.Description)
},
}

srv, err := AddService(nc, config)
srv, err := micro.AddService(nc, config)
if err != nil {
log.Fatal(err)
}
Expand All @@ -68,15 +69,15 @@ func ExampleService_Info() {
}
defer nc.Close()

config := Config{
config := micro.Config{
Name: "EchoService",
Endpoint: Endpoint{
Endpoint: micro.Endpoint{
Subject: "echo",
Handler: func(*Request) {},
Handler: func(micro.Request) {},
},
}

srv, _ := AddService(nc, config)
srv, _ := micro.AddService(nc, config)

// service info
info := srv.Info()
Expand All @@ -95,16 +96,16 @@ func ExampleService_Stats() {
}
defer nc.Close()

config := Config{
config := micro.Config{
Name: "EchoService",
Version: "0.1.0",
Endpoint: Endpoint{
Endpoint: micro.Endpoint{
Subject: "echo",
Handler: func(*Request) {},
Handler: func(micro.Request) {},
},
}

srv, _ := AddService(nc, config)
srv, _ := micro.AddService(nc, config)

// stats of a service instance
stats := srv.Stats()
Expand All @@ -121,16 +122,16 @@ func ExampleService_Stop() {
}
defer nc.Close()

config := Config{
config := micro.Config{
Name: "EchoService",
Version: "0.1.0",
Endpoint: Endpoint{
Endpoint: micro.Endpoint{
Subject: "echo",
Handler: func(*Request) {},
Handler: func(micro.Request) {},
},
}

srv, _ := AddService(nc, config)
srv, _ := micro.AddService(nc, config)

// stop a service
err = srv.Stop()
Expand All @@ -152,16 +153,16 @@ func ExampleService_Stopped() {
}
defer nc.Close()

config := Config{
config := micro.Config{
Name: "EchoService",
Version: "0.1.0",
Endpoint: Endpoint{
Endpoint: micro.Endpoint{
Subject: "echo",
Handler: func(*Request) {},
Handler: func(micro.Request) {},
},
}

srv, _ := AddService(nc, config)
srv, _ := micro.AddService(nc, config)

// stop a service
err = srv.Stop()
Expand All @@ -181,21 +182,21 @@ func ExampleService_Reset() {
}
defer nc.Close()

config := Config{
config := micro.Config{
Name: "EchoService",
Version: "0.1.0",
Endpoint: Endpoint{
Endpoint: micro.Endpoint{
Subject: "echo",
Handler: func(*Request) {},
Handler: func(micro.Request) {},
},
}

srv, _ := AddService(nc, config)
srv, _ := micro.AddService(nc, config)

// reset endpoint stats on this service
srv.Reset()

empty := Stats{
empty := micro.Stats{
ServiceIdentity: srv.Info().ServiceIdentity,
}
if !reflect.DeepEqual(srv.Stats(), empty) {
Expand All @@ -206,15 +207,15 @@ func ExampleService_Reset() {
func ExampleControlSubject() {

// subject used to get PING from all services
subjectPINGAll, _ := ControlSubject(PingVerb, "", "")
subjectPINGAll, _ := micro.ControlSubject(micro.PingVerb, "", "")
fmt.Println(subjectPINGAll)

// subject used to get PING from services with provided name
subjectPINGName, _ := ControlSubject(PingVerb, "CoolService", "")
subjectPINGName, _ := micro.ControlSubject(micro.PingVerb, "CoolService", "")
fmt.Println(subjectPINGName)

// subject used to get PING from a service with provided name and ID
subjectPINGInstance, _ := ControlSubject(PingVerb, "CoolService", "123")
subjectPINGInstance, _ := micro.ControlSubject(micro.PingVerb, "CoolService", "123")
fmt.Println(subjectPINGInstance)

// Output:
Expand All @@ -224,7 +225,7 @@ func ExampleControlSubject() {
}

func ExampleRequest_Respond() {
handler := func(req *Request) {
handler := func(req micro.Request) {
// respond to the request
if err := req.Respond(req.Data()); err != nil {
log.Fatal(err)
Expand All @@ -240,7 +241,7 @@ func ExampleRequest_RespondJSON() {
Y int `json:"y"`
}

handler := func(req *Request) {
handler := func(req micro.Request) {
resp := Point{5, 10}
// respond to the request
// response will be serialized to {"x":5,"y":10}
Expand All @@ -253,7 +254,7 @@ func ExampleRequest_RespondJSON() {
}

func ExampleRequest_Error() {
handler := func(req *Request) {
handler := func(req micro.Request) {
// respond with an error
// Error sets Nats-Service-Error and Nats-Service-Error-Code headers in the response
if err := req.Error("400", "bad request", []byte(`{"error": "value should be a number"}`)); err != nil {
Expand Down
57 changes: 47 additions & 10 deletions micro/request.go
Expand Up @@ -25,16 +25,41 @@ type (
// Request represents service request available in the service handler.
// It exposes methods to respond to the request, as well as
// getting the request data and headers.
Request struct {
msg *nats.Msg
respondError error
Request interface {
// Respond sends the response for the request.
// Additional headers can be passed using [WithHeaders] option.
Respond([]byte, ...RespondOpt) error

// RespondJSON marshals the given response value and responds to the request.
// Additional headers can be passed using [WithHeaders] option.
RespondJSON(interface{}, ...RespondOpt) error

// Error prepares and publishes error response from a handler.
// A response error should be set containing an error code and description.
// Optionally, data can be set as response payload.
Error(code, description string, data []byte, opts ...RespondOpt) error

// Data returns request data.
Data() []byte

// Headers returns request headers.
Headers() Headers

// Subject returns underlying NATS message subject.
Subject() string
}

// RequestHandler is a function used as a Handler for a service.
RequestHandler func(*Request)
RequestHandler func(Request)

// Headers is a wrapper around [*nats.Header]
Headers nats.Header

// request is a default implementation of Request interface
request struct {
msg *nats.Msg
respondError error
}
)

var (
Expand All @@ -43,10 +68,12 @@ var (
ErrArgRequired = errors.New("argument required")
)

// RespondOpt is a
// RespondOpt is a function used to configure [Request.Respond] and [Request.RespondJSON] methods.
type RespondOpt func(*nats.Msg)

func (r *Request) Respond(response []byte, opts ...RespondOpt) error {
// Respond sends the response for the request.
// Additional headers can be passed using [WithHeaders] option.
func (r *request) Respond(response []byte, opts ...RespondOpt) error {
respMsg := &nats.Msg{
Data: response,
}
Expand All @@ -62,7 +89,9 @@ func (r *Request) Respond(response []byte, opts ...RespondOpt) error {
return nil
}

func (r *Request) RespondJSON(response interface{}, opts ...RespondOpt) error {
// RespondJSON marshals the given response value and responds to the request.
// Additional headers can be passed using [WithHeaders] option.
func (r *request) RespondJSON(response interface{}, opts ...RespondOpt) error {
resp, err := json.Marshal(response)
if err != nil {
return ErrMarshalResponse
Expand All @@ -73,7 +102,7 @@ func (r *Request) RespondJSON(response interface{}, opts ...RespondOpt) error {
// Error prepares and publishes error response from a handler.
// A response error should be set containing an error code and description.
// Optionally, data can be set as response payload.
func (r *Request) Error(code, description string, data []byte, opts ...RespondOpt) error {
func (r *request) Error(code, description string, data []byte, opts ...RespondOpt) error {
if code == "" {
return fmt.Errorf("%w: error code", ErrArgRequired)
}
Expand All @@ -98,6 +127,7 @@ func (r *Request) Error(code, description string, data []byte, opts ...RespondOp
return nil
}

// WithHeaders can be used to configure response with custom headers.
func WithHeaders(headers Headers) RespondOpt {
return func(m *nats.Msg) {
if m.Header == nil {
Expand All @@ -111,14 +141,21 @@ func WithHeaders(headers Headers) RespondOpt {
}
}

func (r *Request) Data() []byte {
// Data returns request data.
func (r *request) Data() []byte {
return r.msg.Data
}

func (r *Request) Headers() Headers {
// Headers returns request headers.
func (r *request) Headers() Headers {
return Headers(r.msg.Header)
}

// Subject returns underlying NATS message subject.
func (r *request) Subject() string {
return r.msg.Subject
}

// Get gets the first value associated with the given key.
// It is case-sensitive.
func (h Headers) Get(key string) string {
Expand Down

0 comments on commit 9c1dfba

Please sign in to comment.