gorsk

package module
v0.0.0-...-6f8991e Latest Latest
Warning

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

Go to latest
Published: Mar 4, 2023 License: MIT Imports: 4 Imported by: 0

README

GORSK - GO(lang) Restful Starter Kit

Build Status codecov Go Report Card Maintainability

Gorsk V2 is released - read more about it

Gorsk is a Golang starter kit for developing RESTful services. It is designed to help you kickstart your project, skipping the 'setting-up part' and jumping straight to writing business logic.

Previously Gorsk was built using Gin. Gorsk using Gin is available HERE.

Gorsk follows SOLID principles, with package design being inspired by several package designs, including Ben Johnson's Standard Package Layout, Go Standard Package Layout with my own ideas applied to both. The idea for building this project and its readme structure was inspired by this.

This starter kit currently provides:

  • Fully featured RESTful endpoints for authentication, changing password and CRUD operations on the user entity
  • JWT authentication and session
  • Application configuration via config file (yaml)
  • RBAC (role-based access control)
  • Structured logging
  • Great performance
  • Request marshaling and data validation
  • API Docs using SwaggerUI
  • Mocking using stdlib
  • Complete test coverage
  • Containerized database query tests

The following dependencies are used in this project (generated using Glice):

|-------------------------------------|--------------------------------------------|--------------|
|             DEPENDENCY              |                  REPOURL                   |   LICENSE    |
|-------------------------------------|--------------------------------------------|--------------|
| github.com/labstack/echo            | https://github.com/labstack/echo           | MIT          |
| github.com/go-pg/pg                 | https://github.com/go-pg/pg                | bsd-2-clause |
| github.com/dgrijalva/jwt-go         | https://github.com/dgrijalva/jwt-go        | MIT          |
| github.com/rs/zerolog               | https://github.com/rs/zerolog              | MIT          |
| golang.org/x/crypto/bcrypt          | https://github.com/golang/crypto           |              |
| gopkg.in/yaml.v2                    | https://github.com/go-yaml/yaml            |              |
| gopkg.in/go-playground/validator.v8 | https://github.com/go-playground/validator | MIT          |
| github.com/lib/pq                   | https://github.com/lib/pq                  | Other        |
| github.com/nbutton23/zxcvbn-go      | https://github.com/nbutton23/zxcvbn-go     | MIT          |
| github.com/fortytw2/dockertest      | https://github.com/fortytw2/dockertest     | MIT          |
| github.com/stretchr/testify         | https://github.com/stretchr/testify        | Other        |
|-------------------------------------|--------------------------------------------|--------------|
  1. Echo - HTTP 'framework'.
  2. Go-Pg - PostgreSQL ORM
  3. JWT-Go - JWT Authentication
  4. Zerolog - Structured logging
  5. Bcrypt - Password hashing
  6. Yaml - Unmarshalling YAML config file
  7. Validator - Request validation.
  8. lib/pq - PostgreSQL driver
  9. zxcvbn-go - Password strength checker
  10. DockerTest - Testing database queries
  11. Testify/Assert - Asserting test results

Most of these can easily be replaced with your own choices since their usage is abstracted and localized.

Getting started

Using Gorsk requires having Go 1.7 or above. Once you downloaded Gorsk (either using Git or go get) you need to configure the following:

  1. To use Gorsk as a starting point of a real project whose package name is something like github.com/author/project, move the directory $GOPATH/github.com/ribice/gorsk to $GOPATH/github.com/author/project. We use task as a task runner. Run task relocate to rename the packages, e.g. task relocate TARGET=github.com/author/project

  2. Change the configuration file according to your needs, or create a new one.

  3. Set the ("ENVIRONMENT_NAME") environment variable, either using terminal or os.Setenv("ENVIRONMENT_NAME","dev").

  4. Set the JWT secret env var ("JWT_SECRET")

  5. In cmd/migration/main.go set up psn variable and then run it (go run main.go). It will create all tables, and necessery data, with a new account username/password admin/admin.

  6. Run the app using:

go run cmd/api/main.go

The application runs as an HTTP server at port 8080. It provides the following RESTful endpoints:

  • POST /login: accepts username/passwords and returns jwt token and refresh token
  • GET /refresh/:token: refreshes sessions and returns jwt token
  • GET /me: returns info about currently logged in user
  • GET /swaggerui/ (with trailing slash): launches swaggerui in browser
  • GET /v1/users: returns list of users
  • GET /v1/users/:id: returns single user
  • POST /v1/users: creates a new user
  • PATCH /v1/password/:id: changes password for a user
  • DELETE /v1/users/:id: deletes a user

You can log in as admin to the application by sending a post request to localhost:8080/login with username admin and password admin in JSON body.

Implementing CRUD of another table

Let's say you have a table named 'cars' that handles employee's cars. To implement CRUD on this table you need:

  1. Inside pkg/utl/model create a new file named car.go. Inside put your entity (struct), and methods on the struct if you need them.

  2. Create a new car folder in the (micro)service where your service will be located, most probably inside api. Inside create a file/service named car.go and test file for it (car/car.go and car/car_test.go). You can test your code without writing a single query by mocking the database logic inside /mock/mockdb folder. If you have complex queries interfering with other entities, you can create in this folder other files such as car_users.go or car_templates.go for example.

  3. Inside car folder, create folders named platform, transport and logging.

  4. Code for interacting with a platform like database (postgresql) should be placed under car/platform/pgsql. (pkg/api/car/platform/pgsql/car.go)

  5. In pkg/api/car/transport create a new file named http.go. This is where your handlers are located. Under the same location create http_test.go to test your API.

  6. In logging directory create a file named car.go and copy the logic from another service. This serves as request/response logging.

  7. In pkg/api/api.go wire up all the logic, by instantiating car service, passing it to the logging and transport service afterwards.

Implementing other platforms

Similarly to implementing APIs relying only on a database, you can implement other platforms by:

  1. In the service package, in car.go add interface that corresponds to the platform, for example, Indexer or Reporter.

  2. Rest of the procedure is same, except that in /platform you would create a new folder for your platform, for example, elastic.

  3. Once the new platform logic is implemented, create an instance of it in main.go (for example elastic.Client) and pass it as an argument to car service (pkg/api/car/car.go).

Running database queries in transaction

To use a transaction, before interacting with db create a new transaction:

err := s.db.RunInTransaction(func (tx *pg.Tx) error{
    // Application service here
})

Instead of passing database client as s.db , inside this function pass it as tx. Handle the error accordingly.

Project Structure

  1. Root directory contains things not related to code directly, e.g. docker-compose, CI/CD, readme, bash scripts etc. It should also contain vendor folder, Gopkg.toml and Gopkg.lock if dep is being used.

  2. Cmd package contains code for starting applications (main packages). The directory name for each application should match the name of the executable you want to have. Gorsk is structured as a monolith application but can be easily restructured to contain multiple microservices. An application may produce multiple binaries, therefore Gorsk uses the Go convention of placing main package as a subdirectory of the cmd package. As an example, scheduler application's binary would be located under cmd/cron. It also loads the necessery configuration and passes it to the service initializers.

  3. Rest of the code is located under /pkg. The pkg directory contains utl and 'microservice' directories.

  4. Microservice directories, like api (naming corresponds to cmd/ folder naming) contains multiple folders for each domain it interacts with, for example: user, car, appointment etc.

  5. Domain directories, like user, contain all application/business logic and two additional directories: platform and transport.

  6. Platform folder contains various packages that provide support for things like databases, authentication or even marshaling. Most of the packages located under platform are decoupled by using interfaces. Every platform has its own package, for example, postgres, elastic, redis, memcache etc.

  7. Transport package contains HTTP handlers. The package receives the requests, marshals, validates then passes it to the corresponding service.

  8. Utl directory contains helper packages and models. Packages such as mock, middleware, configuration, server are located here.

License

gorsk is licensed under the MIT license. Check the LICENSE file for details.

Author

Emir Ribic

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrGeneric is used for testing purposes and for errors handled later in the callstack
	ErrGeneric = errors.New("generic error")

	// ErrBadRequest (400) is returned for bad request (validation)
	ErrBadRequest = echo.NewHTTPError(400)

	// ErrUnauthorized (401) is returned when user is not authorized
	ErrUnauthorized = echo.ErrUnauthorized
)

Functions

This section is empty.

Types

type AccessRole

type AccessRole int

AccessRole represents access role type

const (
	// SuperAdminRole has all permissions
	SuperAdminRole AccessRole = 100

	// AdminRole has admin specific permissions
	AdminRole AccessRole = 110

	// CompanyAdminRole can edit company specific things
	CompanyAdminRole AccessRole = 120

	// LocationAdminRole can edit location specific things
	LocationAdminRole AccessRole = 130

	// UserRole is a standard user
	UserRole AccessRole = 200
)

type AuthToken

type AuthToken struct {
	Token        string `json:"token"`
	RefreshToken string `json:"refresh_token"`
}

AuthToken holds authentication token details with refresh token

type AuthUser

type AuthUser struct {
	ID         int
	CompanyID  int
	LocationID int
	Username   string
	Email      string
	Role       AccessRole
}

AuthUser represents data stored in JWT token for user

type Base

type Base struct {
	ID        int       `json:"id"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	DeletedAt time.Time `json:"deleted_at,omitempty" pg:",soft_delete"`
}

Base contains common fields for all tables

func (*Base) BeforeInsert

func (b *Base) BeforeInsert(ctx context.Context) (context.Context, error)

BeforeInsert hooks into insert operations, setting createdAt and updatedAt to current time

func (*Base) BeforeUpdate

func (b *Base) BeforeUpdate(ctx context.Context) (context.Context, error)

BeforeUpdate hooks into update operations, setting updatedAt to current time

type Company

type Company struct {
	Base
	Name      string     `json:"name"`
	Active    bool       `json:"active"`
	Locations []Location `json:"locations,omitempty"`
	Owner     User       `json:"owner"`
}

Company represents company model

type ListQuery

type ListQuery struct {
	Query string
	ID    int
}

ListQuery holds company/location data used for list db queries

type Location

type Location struct {
	Base
	Name    string `json:"name"`
	Active  bool   `json:"active"`
	Address string `json:"address"`

	CompanyID int `json:"company_id"`
}

Location represents company location model

type Logger

type Logger interface {
	// source, msg, error, params
	Log(echo.Context, string, string, error, map[string]interface{})
}

Logger represents logging interface

type Pagination

type Pagination struct {
	Limit  int `json:"limit,omitempty"`
	Offset int `json:"offset,omitempty"`
}

Pagination data

type PaginationReq

type PaginationReq struct {
	Limit int `query:"limit"`
	Page  int `query:"page" validate:"min=0"`
}

PaginationReq holds pagination http fields and tags

func (PaginationReq) Transform

func (p PaginationReq) Transform() Pagination

Transform checks and converts http pagination into database pagination model

type RBACService

type RBACService interface {
	User(echo.Context) AuthUser
	EnforceRole(echo.Context, AccessRole) error
	EnforceUser(echo.Context, int) error
	EnforceCompany(echo.Context, int) error
	EnforceLocation(echo.Context, int) error
	AccountCreate(echo.Context, AccessRole, int, int) error
	IsLowerRole(echo.Context, AccessRole) error
}

RBACService represents role-based access control service interface

type RefreshToken

type RefreshToken struct {
	Token string `json:"token"`
}

RefreshToken holds authentication token details

type Role

type Role struct {
	ID          AccessRole `json:"id"`
	AccessLevel AccessRole `json:"access_level"`
	Name        string     `json:"name"`
}

Role model

type User

type User struct {
	Base
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	Username  string `json:"username"`
	Password  string `json:"-"`
	Email     string `json:"email"`

	Mobile  string `json:"mobile,omitempty"`
	Phone   string `json:"phone,omitempty"`
	Address string `json:"address,omitempty"`

	Active bool `json:"active"`

	LastLogin          time.Time `json:"last_login,omitempty"`
	LastPasswordChange time.Time `json:"last_password_change,omitempty"`

	Token string `json:"-"`

	Role *Role `json:"role,omitempty"`

	RoleID     AccessRole `json:"-"`
	CompanyID  int        `json:"company_id"`
	LocationID int        `json:"location_id"`
}

User represents user domain model

func (*User) ChangePassword

func (u *User) ChangePassword(hash string)

ChangePassword updates user's password related fields

func (*User) UpdateLastLogin

func (u *User) UpdateLastLogin(token string)

UpdateLastLogin updates last login field

Directories

Path Synopsis
cmd
api
pkg
api
GORSK - Go(lang) restful starter kit
GORSK - Go(lang) restful starter kit
api/user
Package user contains user application services
Package user contains user application services

Jump to

Keyboard shortcuts

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