procroute

package module
Version: v1.1.1 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2021 License: Apache-2.0 Imports: 9 Imported by: 0

README

procroute

unit-tests codecov GitHub go.mod Go version GitHub issues GitHub release (latest SemVer) Godoc reference

Procroute serves the purpose of simplifying the creation of web-based applications. The main goal was to create a framework that implements all the necessary http functions so that the end user can concentrate on the business logic.

Getting started

The main two entrypoints of the application are the following: procroute.NewRouteMachine and procroute.NewRouteSet.

Great it seems simple, but how do i start?

  1. Implement the procroute.Loggable interface. You can find an example in the logger.go file.
  2. Implement the procroute.Parser interface. You can find an example in the parser.go file.
  3. Implement one or more of the route interfaces. You can find an example in the model.go file.
  4. Define a routing machine and routes. You can find an example in the main.go file.
Define a logger

The following example implements the Loggable interface, that must be implemented to run the routemachine.

type ExampleLogger struct{}

func (e *ExampleLogger) buildLogerEntry(prefix, format string, v ...interface{}) {
    t := time.Now().Format(time.RFC3339)
    fmt.Printf(t+"\t"+prefix+"\t"+format+"\n", v...)
}

func (e *ExampleLogger) Trace(format string, v ...interface{}) {
    e.buildLogerEntry("TRACE", format, v...)
}

func (e *ExampleLogger) Debug(format string, v ...interface{}) {
    e.buildLogerEntry("DEBUG", format, v...)
}

func (e *ExampleLogger) Info(format string, v ...interface{}) {
    e.buildLogerEntry("INFO", format, v...)
}

func (e *ExampleLogger) Warn(format string, v ...interface{}) {
    e.buildLogerEntry("WARN", format, v...)
}

func (e *ExampleLogger) Error(format string, v ...interface{}) {
    e.buildLogerEntry("ERROR", format, v...)
}

func (e *ExampleLogger) Fatal(format string, v ...interface{}) {
    e.buildLogerEntry("FATAL", format, v...)
}
Define a parser

The following example implements the Parser interface, that must be implemented to create a routeset.

type JsonParser struct{}

func (j *JsonParser) Unmarshal(data []byte, v interface{}) error {
    return json.Unmarshal(data, v)
}

func (j *JsonParser) Marshal(v interface{}) ([]byte, error) {
    return json.Marshal(v)
}

func (j *JsonParser) MimeType() string {
    return "application/json"
}
Get endpoint

The following example implements the GetRoute interface that is used to publish an HTTP GET endpoint.

type MyModel struct {
    ID uint `json:"id"`
    Name string `json:"id"`
}

type Example struct {}

func (e *Example) Get(requestData interface{}) (interface{}, *HttpError) {
    // implement your business logic and return a value

    return &MyModel{
        ID: 12,
        Name: "example",
    }, nil
}

func main() {
    rm := procroute.NewRouteMachine("0.0.0.0", 8080, "/api", &ExampleLogger{})
    rm.AddRouteSet(procroute.NewRouteSet("/example", &JsonParser{}).AddRoutes(&Example{}))

    if err := rm.Start(); err != nil {
        panic(err)
    }

    select {}
}
Define a custom route path, inject the Loggable and UrlParams interface
type MyModel struct {
    ID uint `json:"id"`
    Name string `json:"id"`
}

type Example struct {
    urlParams map[string]string
    loggable procroute.Loggable
}

func (e *Example) Get(requestData interface{}) (interface{}, *HttpError) {
    // implement your business logic and return a value
    e.loggable.Info("id contains the value: %s", e.urlParams["id"])

    // the returned value is parsed into the defined format and available at the get endpoint
    return &MyModel{
        ID: 12,
        Name: "example",
    }, nil
}

func (e *Example) GetRoutePath() string {
    return "/{id}"
}

func (e *Example) SetUrlParams(args map[string]string) {
    e.urlParams = args
}

func (e *Example) WithLogger(loggable procroute.Loggable) {
    e.loggable = loggable
}

func main() {
    rm := procroute.NewRouteMachine("0.0.0.0", 8080, "/api", &ExampleLogger{})
    rm.AddRouteSet(procroute.NewRouteSet("/example", &JsonParser{}).AddRoutes(&Example{}))

    if err := rm.Start(); err != nil {
        panic(err)
    }

    select {}
}

Defining a middleware

The following code snippet provides you a logger middleware that prints the method and endpoint path to the console.

type MyExampleMiddleware struct {
    logger procroute.Loggable
}

func (m *MyExampleMiddleware) Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        m.logger.Debug("method: %s path: %s", r.Method, r.URL)
        next.ServeHTTP(w, r)
    })
}

func (m *MyExampleMiddleware) WithLogger(lggbl procroute.Loggable) {
    m.logger = lggbl
}

func main() {
    rm := procroute.NewRouteMachine("0.0.0.0", 8080, "/api", &ExampleLogger{})
    rm.AddMiddleware(&MyExampleMiddleware{})

    if err := rm.Start(); err != nil {
        panic(err)
    }

    select {}
}

Starting the example

Since this repository contains a fully functional example, clone the repository, navigate to the examples folder, and run:

go run .

The output generated by the example should look something like this:

2021-06-06T01:39:22+02:00       DEBUG   compiling routes
2021-06-06T01:39:22+02:00       INFO    registered get route at: /api/example/{id}
2021-06-06T01:39:22+02:00       INFO    registered get all route at: /api/example
2021-06-06T01:39:22+02:00       INFO    registered post route at: /api/example
2021-06-06T01:39:22+02:00       INFO    registered update route at: /api/example
2021-06-06T01:39:22+02:00       INFO    registered delete route at: /api/example/{id}
2021-06-06T01:39:22+02:00       INFO    server started on: 0.0.0.0:8080

This indicates that the sample application is running at 0.0.0.0:8080. You can confirm this by running a curl request against the endpoint get all:

curl -i -X GET http://localhost:8080/api/example

The answer should be the same as the following text snipped:

HTTP/1.1 200 OK
Content-Type: application/json
Date: xxx, xxx Jun 2021 xx:xx:xx GMT
Content-Length: 358

[
    {
        "name": "test1",
        "url": "test1",
        "id": 1,
        "createdAt": "2021-06-06T01:51:11.0862583+02:00",
        "updatedAt": "2021-06-06T01:51:11.086261+02:00",
        "deletedAt": "2021-06-06T01:51:11.0862635+02:00"
    }, 
    {
        "name": "test2",
        "url": "test2",
        "id": 2,
        "createdAt": "2021-06-06T01:51:11.0862661+02:00",
        "updatedAt": "2021-06-06T01:51:11.0862687+02:00",
        "deletedAt": "2021-06-06T01:51:11.0862713+02:00"
    }
]

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrAddressNotSet             = errors.New("address not set")
	ErrRouteSetNotPresent        = errors.New("missing routesets")
	ErrNilRouteSetIsNotAllowed   = errors.New("empty route set is not supported")
	ErrNilMiddlewareIsNotAllowed = errors.New("nil middleware is not supported")
)
View Source
var (
	ErrGetRouteIsNil    = errors.New("get route is nil")
	ErrGetAllRouteIsNil = errors.New("get all route is nil")
	ErrPostRouteIsNil   = errors.New("post route is nil")
	ErrUpdateRouteIsNil = errors.New("update route is nil")
	ErrDeleteRouteIsNil = errors.New("delete route is nil")
)
View Source
var (
	ErrHttpResponseWriterNotSet = errors.New("http response writer is not set")
)

Functions

This section is empty.

Types

type DeleteRoute

type DeleteRoute interface {
	// Delete represents the method that contains the business logic for deleting a resource.
	//
	// Example
	//  type Model struct {
	//  	Name string `json:"name,omitempty"`
	//  	URL  string `json:"url,omitempty"`
	//  }
	//
	//  type MyType struct {
	//  	Model
	//  }
	//
	//  func (m *MyType) Delete(requestData interface{}) *HttpError {
	//      // do something
	//      fmt.Printf("%+v\n", m.Model)
	//  	return nil
	//  }
	Delete(requestData interface{}) *HttpError
}

DeleteRoute provides the interface that must be implemented to create a Delete endpoint.

type DeleteRouteRoutePath

type DeleteRouteRoutePath interface {
	// DeleteRoutePath represents an optional method that can be set to define a custom path for the delete route.
	//
	// Example:
	//  type MyType struct {}
	//
	//  func (m *MyType) DeleteRoutePath() string {
	//  	return "/delete"
	//  }
	DeleteRoutePath() string
}

DeleteRouteRoutePath defines an optional child interface that is used to customize route path.

type GetAllRoute

type GetAllRoute interface {
	// GetAll represents the method that contains the business logic for receiving all resources.
	//
	// Example
	//  type Model struct {
	//  	Name string `json:"name,omitempty"`
	//  	URL  string `json:"url,omitempty"`
	//  }
	//
	//  type MyType struct {
	//  	Model
	//  }
	//
	//  func (m *MyType) GetAll(requestData interface{}) ([]interface{}, *HttpError) {
	//      // do something
	//  	return []interface{}{
	//          Model{
	//				Name: "example",
	//				URL: "example.url",
	//          },
	//      }, nil
	//  }
	GetAll(requestData interface{}) ([]interface{}, *HttpError)
}

GetAllRoute provides the interface that must be implemented to create a Get All endpoint.

type GetAllRoutePath

type GetAllRoutePath interface {
	// GetAllRoutePath represents an optional method that can be set to define a custom path for the get all route.
	//
	// Example:
	//  type MyType struct {}
	//
	//  func (m *MyType) GetAllRoutePath() string {
	//  	return "/get-all"
	//  }
	GetAllRoutePath() string
}

GetAllRoutePath defines an optional child interface that is used to customize route path.

type GetRoute

type GetRoute interface {
	// Get represents the method that contains the business logic for receiving a resource.
	//
	// Example
	//  type Model struct {
	//  	Name string `json:"name,omitempty"`
	//  	URL  string `json:"url,omitempty"`
	//  }
	//
	//  type MyType struct {
	//  	Model
	//  }
	//
	//  func (m *MyType) Get(requestData interface{}) (interface{}, *HttpError) {
	//      // do something
	//  	return Model{
	//			Name: "example",
	//			URL: "example.url",
	//      }, nil
	//  }
	Get(requestData interface{}) (interface{}, *HttpError)
}

GetRoute provides the interface that must be implemented to create a Get endpoint.

type GetRoutePath

type GetRoutePath interface {
	// GetRoutePath represents an optional method that can be set to define a custom path for the get route.
	//
	// Example:
	//  type MyType struct {}
	//
	//  func (m *MyType) GetRoutePath() string {
	//  	return "/get-all"
	//  }
	GetRoutePath() string
}

GetRoutePath defines an optional child interface that is used to customize route path.

type HttpError

type HttpError struct {
	Status    int
	ErrorCode string
	Message   string
}

HttpError represents the datatype used as error response

type Loggable

type Loggable interface {
	Trace(format string, v ...interface{})
	Debug(format string, v ...interface{})
	Info(format string, v ...interface{})
	Warn(format string, v ...interface{})
	Error(format string, v ...interface{})
	Fatal(format string, v ...interface{})
}

Loggable defines the interface to pass a logger to the RouteMachine. The logger can be used later by implementing the WithLogger interface.

type Middleware

type Middleware interface {
	// Middleware represents the function that must be implemented to assign a new Middleware to the RouteMachine.
	// In most cases the implementation is similar to:
	//
	// Example:
	//  func (m *MyType) Middleware(http.Handler) http.Handler {
	//      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	//          // do something
	//          next.ServeHTTP(w, r)
	//      })
	//  }
	Middleware(http.Handler) http.Handler
}

Middleware defines an interface that is used to inject a middleware

type Parser

type Parser interface {
	// Unmarshal parses the encoded data and stores the result in the value pointed to by v. If v is nil or not a pointer, Unmarshal returns an error.
	//
	// Example:
	//  func (m *JsonParser) Unmarshal(data []byte, v interface{}) error {
	//  	return json.Unmarshal(data, v)
	//  }
	Unmarshal(data []byte, v interface{}) error
	// Marshal returns the encoded data as byte slice.
	//
	// Exaple:
	//  func (m *JsonParser) Marshal(v interface{}) ([]byte, error) {
	//  	return json.Marshal(v)
	//  }
	Marshal(v interface{}) ([]byte, error)
	// MimeType returns the associated mime type in string representation.
	// A list of available MIME types can be found at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
	//
	// Example:
	//  func (m *JsonParser) MimeType() string {
	//  	return "application/json"
	//  }
	MimeType() string
}

Parser provides the interface that must be implemented to marshal and unmarshal the data sent during http request and http responses.

type PostRoute

type PostRoute interface {
	// Post represents the method that contains the business logic for creating a resource.
	//
	// Example
	//  type Model struct {
	//  	Name string `json:"name,omitempty"`
	//  	URL  string `json:"url,omitempty"`
	//  }
	//
	//  type MyType struct {
	//  	Model
	//  }
	//
	//  func (m *MyType) Post(requestData interface{}) *HttpError {
	//      // do something
	//      fmt.Printf("%+v\n", requestData)
	//  	return nil
	//  }
	Post(requestData interface{}) *HttpError
}

PostRoute provides the interface that must be implemented to create a Post endpoint.

type PostRouteRoutePath

type PostRouteRoutePath interface {
	// PostRoutePath represents an optional method that can be set to define a custom path for the post route.
	//
	// Example:
	//  type MyType struct {}
	//
	//  func (m *MyType) PostRoutePath() string {
	//  	return "/post"
	//  }
	PostRoutePath() string
}

PostRouteRoutePath defines an optional child interface that is used to customize route path.

type QueryParams added in v1.1.0

type QueryParams interface {
	// SetQueryParams represents a method to pass query params.
	//
	// Example:
	//  type MyType struct {
	//  	queryParams url.Values
	//  }
	//
	//  func (m *MyType) SetQueryParams(args url.Values) {
	//  	m.queryParams = args
	//  }
	SetQueryParams(args url.Values)
}

type RawRoute added in v1.0.2

type RawRoute interface {
	// Raw represents the method that does nothing for you.
	// Any logic must be handled by the user itself.
	// This type can be used when a functionality is missing by this framework.
	Raw(w http.ResponseWriter, r *http.Request)
	// HttpMethods must returns a slice of http methods procroute should use to register the route.
	// Example:
	//  type MyType struct {}
	//
	//  func (m *MyType) HttpMethods() string {
	//  	return []string{"GET","OPTIONS"}
	//  }
	HttpMethods() []string
}

RawRoute provides the interface that must be implemented to create a Raw endpoint.

type RawRouteRoutePath added in v1.0.2

type RawRouteRoutePath interface {
	// RawRoutePath represents an optional method that can be set to define a custom path for the raw route.
	//
	// Example:
	//  type MyType struct {}
	//
	//  func (m *MyType) RawRoutePath() string {
	//  	return "/raw"
	//  }
	RawRoutePath() string
}

RawRouteRoutePath defines an optional child interface that is used to customize route path.

type RouteMachine

type RouteMachine struct {
	// contains filtered or unexported fields
}

RouteMachine represents the manager type used to create and operate endpoints.

func NewRouteMachine

func NewRouteMachine(addr string, port uint16, basePath string, loggable Loggable) *RouteMachine

NewRouteMachine is a constructor that creates a route machine based on the settings passed as parameters. If the port or loggable is not set correctly, you will get errors during execution.

func (*RouteMachine) AddMiddleware

func (rm *RouteMachine) AddMiddleware(next Middleware) error

AddMiddleware injects a middleware just before an endpoint is touched.

func (*RouteMachine) AddRouteSet

func (rm *RouteMachine) AddRouteSet(routeSet *RouteSet) error

AddRouteSet provides a method to register a new RouteSet within the route machine

func (*RouteMachine) SetIdleTimeout

func (rm *RouteMachine) SetIdleTimeout(timeout time.Duration) *RouteMachine

SetIdleTimeout provides a method that changes the idle timeout within the http server

func (*RouteMachine) SetReadHeaderTimeout

func (rm *RouteMachine) SetReadHeaderTimeout(timeout time.Duration) *RouteMachine

SetReadHeaderTimeout provides a method that changes the read header timeout within the http server

func (*RouteMachine) SetReadTimeout

func (rm *RouteMachine) SetReadTimeout(timeout time.Duration) *RouteMachine

SetReadTimeout provides a method that changes the read timeout within the http server

func (*RouteMachine) Start

func (rm *RouteMachine) Start() error

Start provides a method that starts a go routine with the http server

Possible errors:

- ErrAddressNotSet
- ErrRouteSetNotPresent
- socket related errors

func (*RouteMachine) Stop

func (rm *RouteMachine) Stop() error

Stop delegates the stop signal to http.server.Shutdown

type RouteSet

type RouteSet struct {
	// contains filtered or unexported fields
}

RouteSet defines a structure that is used to create an endpoint set based on the base path

func NewRouteSet

func NewRouteSet(basePath string, parser Parser) *RouteSet

NewRouteSet defines a new route set that is used to genereate http endpoints

func (*RouteSet) AddRoutes

func (rs *RouteSet) AddRoutes(routes ...interface{}) *RouteSet

AddRoutes provides a method that adds routes to the route set. When calling AddRoutes, ensure that your types do not overwrite each other.

Bad example:

 type Model struct {
 	Name string `json:"name,omitempty"`
 	URL  string `json:"url,omitempty"`
 }

 type MyType struct {
 	Model
 }

 func (m *MyType) Type() interface{} {
 	return &m.Model
 }

 func (m *MyType) Get() (interface{}, *HttpError) {
     // do something
 	return Model{
			Name: "example",
			URL: "example.url",
     }, nil
 }

 type MyType2 struct {
 	Model
 }

 func (m *MyType2) Type() interface{} {
 	return &m.Model
 }

 func (m *MyType2) Get() (interface{}, *HttpError) {
     // do something
 	return Model{
			Name: "example",
			URL: "example.url",
     }, nil
 }

 rs.AddRoutes(&MyType{}, &MyType2{})

type UpdateRoute

type UpdateRoute interface {
	// Update represents the method that contains the business logic for updating a resource.
	//
	// Example
	//  type Model struct {
	//  	Name string `json:"name,omitempty"`
	//  	URL  string `json:"url,omitempty"`
	//  }
	//
	//  type MyType struct {
	//  	Model
	//  }
	//
	//  func (m *MyType) Update(requestData interface{}) *HttpError {
	//      // do something
	//      fmt.Printf("%+v\n", m.Model)
	//  	return nil
	//  }
	Update(requestData interface{}) *HttpError
}

UpdateRoute provides the interface that must be implemented to create an Update endpoint.

type UpdateRouteRoutePath

type UpdateRouteRoutePath interface {
	// UpdateRoutePath represents an optional method that can be set to define a custom path for the update route.
	//
	// Example:
	//  type MyType struct {}
	//
	//  func (m *MyType) UpdateRoutePath() string {
	//  	return "/update"
	//  }
	UpdateRoutePath() string
}

UpdateRouteRoutePath defines an optional child interface that is used to customize route path.

type UrlParams

type UrlParams interface {
	// SetUrlParam represents a method to pass url params that can be used later to identify resources.
	//
	// Example:
	//  type MyType struct {
	//  	urlParams map[string]string
	//  }
	//
	//  func (m *MyType) SetUrlParams(args map[string]string) {
	//  	m.urlParams = args
	//  }
	SetUrlParams(args map[string]string)
}

UrlParams represents an interface that must be implemented if the route works with url parameters.

type WithLogger

type WithLogger interface {
	// WithLogger represents the function that must be implemented to inject the Loggable object into the concrete implementation.
	// In most cases the implementation is similar to:
	//
	// Example:
	//  func (m *MyType) WithLogger(lggbl Loggable) {
	//     m.logger = lggbl
	//  }
	WithLogger(Loggable)
}

WithLogger provides an optional interface that is used to attach the logger to the route or middleware

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
t or T : Toggle theme light dark auto
y or Y : Canonical URL