gkBoot

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 14, 2024 License: MIT Imports: 28 Imported by: 2

README

gkBoot

Build Status
Go

Objective

The purpose of gkBoot is to organize, compartmentalize and wire a microservice with the least amount of boilerplate while providing devs an easier time in building a microservice.

The inspiration for this is mostly Spring Boot. I liked the look of the decoupled architecture and the adherence to a domain-oriented design pattern.

In gkBoot, I tried to capture much of that while still maintaining that which makes Go great. Hopefully you like it too.

Installation

Note: Please use go 1.18 for installation

go get github.com/yomiji/gkBoot@v1.0.0

Use

Note: Please check out tests for advanced or detailed use cases

Users of gkBoot are recommended to follow a pattern when creating their microservics. Generally speaking, everything is centered around the service wiring (which sit in main.go) and the service files (which sit in the service directory):

├── go.mod
├── go.sum
├── main.go
├── services
│   └── greeting
│       └── greetings.go
└── tests
    └── greeting
        └── greetings_test.go

In services/greeting/greetings.go:

package greeting
import (
    "context"
    "fmt"
    "strconv"
    "github.com/yomiji/gkBoot"
    "github.com/yomiji/gkBoot/request"
)

type Request struct {
	// the "header!" tag value indicates that the value is required to be in
	// the header with the alias indicating that it should be named Secret-Value
	SecretValue string `header:"Secret-Value" required:"true" json:"-"`
	// the "query" tag value indicates that the value of the object is found in
	// the request query params
	FirstName   string `query:"firstName" json:"firstName"`
	// the "path" tag value indicates that the value of the object is found in
	// the url request path
	Age         int    `path:"age" json:"age"`
}

func (r Request) Info() request.HttpRouteInfo {
	return request.HttpRouteInfo{
		Name:        "DemoRequest",
		Method:      request.GET,
		Path:        "/{age}/greetings",
		Description: "A typical greeting.",
	}
}

type Service struct {
	gkBoot.BasicService
}

type Response struct {
	Greeting string
}

func (s Service) Execute(ctx context.Context, request interface{}) (response interface{}, err error) {
	reqObj := request.(*Request)
	
	var age string
	
	if reqObj.Age == 0 {
		age = "old"
	} else {
		age = strconv.Itoa(reqObj.Age)
	}
	
	greeting := fmt.Sprintf("Hello, %s! You're %s!\n", reqObj.FirstName, age)
	
	return Response{Greeting:greeting}, nil
}

In main.go:

package main
import (
	"greeting"
    "github.com/yomiji/gkBoot"
)

func main() {
    // start an http service on localhost port 8080
    gkBoot.StartServer([]gkBoot.ServiceRequest{
        {
            Request: new(greeting.Request),
            Service: new(greeting.Service),
        },
    })
}

In a client:

package main
import (
	"greeting"
	"github.com/yomiji/gkBoot"
)

func main() {
    Request := &greeting.Request {
        SecretValue: "Hello!",
        FirstName: "Simon!",
        Age: 21,
    }
	
    Response := &greeting.Response{}
    
    gkBoot.DoRequest("http://localhost:8080", Request, Response)
    
    //Response contains "Hello, Simon! You're 21!"
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	HTTP2GlobalCA = []*tls.Config{nil}
)

Functions

func APIValidationWrapper

func APIValidationWrapper(srv service.Service) service.Service

func DoGeneratedRequest added in v1.0.0

func DoGeneratedRequest[ResponseType any](
	r *http.Request, responseObj *ResponseType, tlsConfig ...*tls.Config,
) error

func DoRequest added in v1.0.0

func DoRequest[RequestType request.HttpRequest, ResponseType any](
	baseUrl string,
	clientRequest RequestType,
	responseObj *ResponseType,
	tlsConfig ...*tls.Config,
) error

func GenerateClientRequest added in v1.0.0

func GenerateClientRequest(baseUrl string, serviceRequest request.HttpRequest) (*http.Request, error)

func GenerateRequestDecoder

func GenerateRequestDecoder(obj request.HttpRequest) (kitDefaults.DecodeRequestFunc, error)

GenerateRequestDecoder

When used in go-kit, generates a json decoder function that translates http requests to go concrete objects.

This reads 'request' tags in a concrete object to perform transformations from the relative http request parts to the given field in the result object. The 'request' tag may be applied to a field of type bool, string, int, int8, int16, int32, int64, float32, float64, complex64, complex128 or a slice of any of these (the relative part of the request must list slice values separated by comma).

The 'request' tag itself may be structured thus:

	type ConcreteObject struct {
	  Value   string           `request:"header"`                     // find in headers with name "Value"
	  MyInt   int              `request:"header" alias:"New-Integer"` // "New-Integer" in headers not "MyInt"
   MyBool  bool             `request:"query" json:"myBool"`        // "myBool" in the query params of request
	  MyFloat float32          `request:"query!"`                     // query param must be present as "MyFloat"
	  Body    CustomBodyStruct `request:"form"`                       // json request body as an object (json.Unmarshal)
	}

Note that this function will look for the corresponding field values using the following naming hierarchy:

alias -> json -> field name (exported)

This function will skip over unexported fields

The resulting decoder function always returns a pointer to a new instantiation of the 'obj' argument.

The obj argument may be a reference or a value type

func GenerateSpecification

func GenerateSpecification(requests []ServiceRequest, optionalReflector *openapi3.Reflector) (
	openapi3.Reflector,
	error,
)

func MakeHandler

func MakeHandler(serviceRequests []ServiceRequest, option ...config.GkBootOption) (http.Handler, *config.BootConfig)

func NewServiceBuilder

func NewServiceBuilder(srv service.Service, option ...config.GkBootOption) *serviceBuilder

NewServiceBuilder

This will wire up a service-only object that can re-use logging wrappers established elsewhere while maintaining the REST Request-Service established pattern. The associated Mixin chains must be called to identify which functionality is wired in.

Unavailable config options (using the following will not do anything):

config.WithServiceDecorator
config.WithHttpServerOpts
config.WithHttpPort
config.WithRootPath
config.WithStrictAPI

func Start

func Start(serviceRequests []ServiceRequest, option ...config.GkBootOption) (*http.Server, <-chan struct{})

Start

Starts the http server for GkBoot. Returns the running http.Server and a blocking function that waits until a signal (syscall.SIGINT, syscall.SIGTERM, syscall.SIGALRM) is sent.

func StartServer

func StartServer(serviceRequests []ServiceRequest, option ...config.GkBootOption)

StartServer

Convenience method.

If the service and blocker of Start are unnecessary, this conveniently does all of that for us.

func StartServerWithHandler

func StartServerWithHandler(serviceRequests []ServiceRequest, option ...config.GkBootOption)

func StartWithHandler

func StartWithHandler(serviceRequests []ServiceRequest, option ...config.GkBootOption) (*http.Server, <-chan struct{})

Types

type BasicService

type BasicService struct {
	service.UsingConfig
}

BasicService

This is the typical service with no DB attached, with an associated Configuration set by WithCustomConfig

It is recommended to use config.WithCustomConfig on gkBoot.Start followed by implementing member Execute function of your struct

func (BasicService) Execute

func (b BasicService) Execute(ctx context.Context, request interface{}) (response interface{}, err error)

type BasicServiceWithDB

type BasicServiceWithDB struct {
	service.UsingConfig
	service.UsingDB
}

BasicServiceWithDB

This is the typical service with custom config and DB

It is recommended to use config.WithCustomConfig and config.WithDatabase on gkBoot.Start followed by implementing member Execute function of your struct

func (BasicServiceWithDB) Execute

func (b BasicServiceWithDB) Execute(ctx context.Context, request interface{}) (response interface{}, err error)

type HttpDecoder

type HttpDecoder interface {
	Decode(ctx context.Context, httpRequest *http.Request) (request interface{}, err error)
}

HttpDecoder

Objects that implement this interface will pass the defined function to the decoder part of the go-kit route definition

type JSONBody added in v0.3.0

type JSONBody struct{}

JSONBody

When embedded into a request, flags the request as a JSON body to allow for automatic decoding.

type Requester added in v1.3.0

type Requester interface {
	Request(ctx context.Context) (*http.Request, error)
}

Requester is an interface that defines the Request method for making HTTP requests.

The Request method takes a context.Context object as a parameter and returns an *http.Request object and an error. The ctx parameter is used to pass any optional cancellation signals or deadlines to the Request method.

The Request method is responsible for creating and returning an *http.Request object that represents the HTTP request to be made. If there is an error while creating the request, the error should be returned.

Example Usage:

type MyRequester struct {}

func (r *MyRequester) Request(ctx context.Context) (*http.Request, error) {
    // Implement the logic to create and return the *http.Request object
}

func main() {
    requester := &MyRequester{}
    request, err := requester.Request(context.Background())
    if err != nil {
        // Handle error
    }
    // Use the *http.Request object for making the HTTP request
}

type ServiceRequest

type ServiceRequest struct {
	Request request.HttpRequest
	Service service.Service
}

ServiceRequest

The request and service provided in this structure are submitted to the Start functions. The requests, when called from the http client, will route to the associated service to execute the business logic of the Execute method.

type SkipClientValidation added in v1.0.0

type SkipClientValidation interface {
	SkipClientValidation()
}

SkipClientValidation is an interface that can be implemented by a request object to skip client validation during the generation of an *http.Request object. If a request object implements this interface, the validation step will be bypassed and the request object will not be validated before generating the HTTP request.

Example Usage:

type MyRequest struct {
    // request fields
}

func (r *MyRequest) Info() request.HttpRouteInfo {
    // return HttpRouteInfo
}

func (r *MyRequest) Validate() error {
    // return validation error
}

func (r *MyRequest) SkipClientValidation() {
    // implement the interface to skip client validation
}

func main() {
    request := &MyRequest{}

    // Generate *http.Request object
    httpRequest, err := GenerateClientRequest(baseUrl, request)
    if err != nil {
        // Handle error
    }
    // Use the *http.Request object for making the HTTP request
}

type UsingSkipClientValidation added in v1.2.7

type UsingSkipClientValidation struct{}

UsingSkipClientValidation is a type that can be used to indicate that client validation should be skipped during the generation of an *http.Request object. If a request object contains a field of type UsingSkipClientValidation and is used as an argument for generating an *http.Request object, the validation step will be bypassed and the request object will not be validated before generating the HTTP request.

func (UsingSkipClientValidation) SkipClientValidation added in v1.2.7

func (u UsingSkipClientValidation) SkipClientValidation()

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL