gormpher

package module
v0.0.0-...-2131790 Latest Latest
Warning

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

Go to latest
Published: Apr 10, 2024 License: BSD-3-Clause Imports: 22 Imported by: 2

README

Gormpher - Generate restful APIs by defining struct model, based on Gin and Gorm

Quick Start

go get github.com/restsend/gormpher

reference to example

package main

import (
	"errors"
	"flag"
	"math/rand"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/restsend/gormpher"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

type User struct {
	ID        uint       `json:"id" gorm:"primarykey"`
	CreatedAt time.Time  `json:"createdAt"`
	UpdatedAt time.Time  `json:"updatedAt"`
	Name      string     `json:"name"`
	Age       int        `json:"age"`
	Enabled   bool       `json:"enabled"`
	LastLogin *time.Time `json:"lastLogin"`
}

type Product struct {
	UUID      string    `json:"id" gorm:"primarykey"`
	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`
	GroupID   int       `json:"-"`
	Group     Group     `json:"group" gorm:"foreignKey:GroupID;references:ID"` // association
	Name      string    `json:"name"`
	Enabled   bool      `json:"enabled"`
}

type Group struct {
	ID   uint   `json:"id" gorm:"primarykey"`
	Name string `json:"name"`
}

func main() {
	var dsn string
	var addr string

	flag.StringVar(&dsn, "n", "", "DB DSN")
	flag.StringVar(&addr, "a", ":8890", "Api Server Addr")
	flag.Parse()

	db, _ := gorm.Open(sqlite.Open(dsn), nil)
	db.AutoMigrate(Product{}, User{})

	r := gin.Default()

	objs := GetWebObjects(db)
	// visit API: http://localhost:8890/api
	gormpher.RegisterObjects(r, objs)
	// visit Admin: http://localhost:8890/admin/v1
	gormpher.RegisterObjectsWithAdmin(r.Group("admin"), objs)

	r.Run(addr)
}

func GetWebObjects(db *gorm.DB) []gormpher.WebObject {
	return []gormpher.WebObject{
		// Basic Demo
		// Check API File: user.http
		// PUT 		http://localhost:8890/user
		// GET 		http://localhost:8890/user/:key
		// PATCH	http://localhost:8890/user/:key
		// POST 	http://localhost:8890/user
		// DELETE http://localhost:8890/user/:key
		// DELETE http://localhost:8890/user
		{
			Name:         "user",
			Model:        &User{},
			SearchFields: []string{"Name", "Enabled"},
			EditFields:   []string{"Name", "Age", "Enabled", "LastLogin"},
			FilterFields: []string{"Name", "CreatedAt", "Age", "Enabled"},
			OrderFields:  []string{"CreatedAt", "Age", "Enabled"},
			GetDB:        func(ctx *gin.Context, isCreate bool) *gorm.DB { return db },
		},
		// Advanced Demo
		// Check API File: product.http
		// PUT 		http://localhost:8890/product
		// GET 		http://localhost:8890/product/:key
		// PATCH	http://localhost:8890/product/:key
		// POST 	http://localhost:8890/product
		// DELETE http://localhost:8890/product/:key
		// DELETE http://localhost:8890/product
		{
			Name:         "product",
			Model:        &Product{},
			SearchFields: []string{"Name"},
			EditFields:   []string{"Name", "Enabled", "Model"},
			FilterFields: []string{"Name", "CreatedAt", "Enabled"},
			OrderFields:  []string{"CreatedAt"},
			GetDB:        func(c *gin.Context, isCreate bool) *gorm.DB { return db },
			BeforeCreate: func(ctx *gin.Context, vptr any, vals map[string]any) error {
				p := (vptr).(*Product)
				p.UUID = MockUUID(8)

				// create group
				group := Group{Name: "group" + MockUUID(4)}
				if err := db.Create(&group).Error; err != nil {
					return err
				}

				p.GroupID = int(group.ID)
				return nil
			},
			BeforeDelete: func(ctx *gin.Context, vptr any) error {
				p := (vptr).(*Product)
				if p.Enabled {
					return errors.New("product is enabled, can not delete")
				}
				return nil
			},
			// Custom Query View
			// GET http://localhost:8890/product/all_enabled
			Views: []gormpher.QueryView{
				{
					Name:   "all_enabled",
					Method: "GET",
					Prepare: func(db *gorm.DB, c *gin.Context, pagination bool) (*gorm.DB, *gormpher.QueryForm, error) {
						// SELECT (id, name) FROM products WHERE enabled = true
						queryForm := &gormpher.QueryForm{
							Limit: -1,
							Filters: []gormpher.Filter{
								{Name: "enabled", Op: "=", Value: true}, // JSON format
							},
							ViewFields: []string{"UUID", "Name"},
						}
						return db, queryForm, nil
					},
				},
			},
		},
	}
}

func MockUUID(n int) string {
	source := []rune("0123456789abcdefghijklmnopqrstuvwxyz")
	b := make([]rune, n)
	for i := range b {
		b[i] = source[rand.Intn(len(source))]
	}
	return string(b)
}

Run the project and visit http://localhost:8890/admin , you can see a web interface for administrator.

image

Features

Generate CRUD Restful API
Optional Admin Web UI
Generic Gorm Function

Documentation

Index

Constants

View Source
const (
	DefaultQueryLimit = 50
	MaxQueryLimit     = 150
)
View Source
const (
	GET    = 1 << 1
	CREATE = 1 << 2
	EDIT   = 1 << 3
	DELETE = 1 << 4
	QUERY  = 1 << 5
	BATCH  = 1 << 6
)

Request method

Variables

This section is empty.

Functions

func Count

func Count[T any](db *gorm.DB, where ...any) (int, error)

func Delete

func Delete[T any](db *gorm.DB, val *T, where ...any) error

Delete

func DeleteByID

func DeleteByID[T any, E ~int | ~string](db *gorm.DB, id E, where ...any) error

func DeleteByMap

func DeleteByMap[T any](db *gorm.DB, m map[string]any, where ...any) error

func ExecuteEdit

func ExecuteEdit[T any, V Key](db *gorm.DB, key V, vals map[string]any) (*T, error)

func ExecuteGet

func ExecuteGet[T any, V Key](db *gorm.DB, key V) (*T, error)

func ExecuteQuery

func ExecuteQuery[T any](db *gorm.DB, form QueryForm, pagination bool) (items []T, count int, err error)

QueryForm: database column format key

func FilterEqualScope

func FilterEqualScope(filters map[string]any) func(db *gorm.DB) *gorm.DB

{"name": "mockname", "age": 10 } => name = 'mockname' AND age = 10

func FilterScope

func FilterScope(filters []Filter) func(db *gorm.DB) *gorm.DB

[{"name": "name", op: "=", "value": "mockname" }, {"name": "age", "op": "<", "value": 20 }] => name = 'mockname' AND age < 20

func Get

func Get[T any](db *gorm.DB, val *T, where ...any) (*T, error)

Get

func GetByID

func GetByID[T any, E ~int | ~string](db *gorm.DB, id E, where ...any) (*T, error)

func GetByMap

func GetByMap[T any](db *gorm.DB, m map[string]any, where ...any) (*T, error)

func GetColumnNameByField

func GetColumnNameByField[T any](name string) string

func GetFieldNameByJsonTag

func GetFieldNameByJsonTag[T any](jsonTag string) string

func GetPkColumnName

func GetPkColumnName[T any]() string

func GetPkJsonName

func GetPkJsonName[T any]() string

func GetTableName

func GetTableName[T any](db *gorm.DB) string

func HandleCreate

func HandleCreate[T any](c *gin.Context, db *gorm.DB, onCreate onCreateFunc[T])

func HandleDelete

func HandleDelete[T any](c *gin.Context, db *gorm.DB, onDelete onDeleteFunc[T])

func HandleEdit

func HandleEdit[T any](c *gin.Context, db *gorm.DB, editables []string, onUpdate onUpdateFunc[T])

func HandleGet

func HandleGet[T any](c *gin.Context, db *gorm.DB, onRender onRenderFunc[T])

func HandleQuery

func HandleQuery[T any](c *gin.Context, db *gorm.DB, ctx *QueryOption)

TODO: add onRender hook QueryForm: json format key

func KeywordScope

func KeywordScope(keys map[string]string) func(db *gorm.DB) *gorm.DB

{"name": "mockname", "nick": "mocknick" } => name LIKE '%mockname%' OR nick LIKE '%mocknick%'

func List

func List[T any](db *gorm.DB, ctx *ListContext) ([]T, int, error)

func ListFilter

func ListFilter[T any](db *gorm.DB, filter []Filter, where ...any) ([]T, int, error)

func ListKeyword

func ListKeyword[T any](db *gorm.DB, keys map[string]string, where ...any) ([]T, int, error)

func ListModel

func ListModel[T, R any](db *gorm.DB, ctx *ListContext) ([]R, int, error)

func ListOrder

func ListOrder[T any](db *gorm.DB, order string, where ...any) ([]T, int, error)

func ListPage

func ListPage[T any](db *gorm.DB, page int, pageSize int, where ...any) ([]T, int, error)

func ListPageKeyword

func ListPageKeyword[T any](db *gorm.DB, page, pageSize int, keys map[string]string, where ...any) ([]T, int, error)

func ListPageKeywordFilterOrder

func ListPageKeywordFilterOrder[T any](db *gorm.DB, page, pageSize int, keys map[string]string, filters []Filter, order string, where ...any) ([]T, int, error)

func ListPageKeywordFilterOrderModel

func ListPageKeywordFilterOrderModel[T, R any](db *gorm.DB, page, pageSize int, keys map[string]string, filters []Filter, order string, where ...any) ([]R, int, error)

func ListPageKeywordOrder

func ListPageKeywordOrder[T any](db *gorm.DB, page, pageSize int, keys map[string]string, order string, where ...any) ([]T, int, error)

func ListPageOrder

func ListPageOrder[T any](db *gorm.DB, page, pageSize int, order string, where ...any) ([]T, int, error)

func ListPos

func ListPos[T any](db *gorm.DB, pos, limit int, where ...any) ([]T, int, error)

Query List

func ListPosFilter

func ListPosFilter[T any](db *gorm.DB, pos, limit int, filter []Filter, where ...any) ([]T, int, error)

func ListPosKeyword

func ListPosKeyword[T any](db *gorm.DB, pos, limit int, keys map[string]string, where ...any) ([]T, int, error)

func ListPosKeywordFilter

func ListPosKeywordFilter[T any](db *gorm.DB, pos, limit int, keys map[string]string, filter []Filter, where ...any) ([]T, int, error)

func ListPosKeywordFilterOrder

func ListPosKeywordFilterOrder[T any](db *gorm.DB, pos, limit int, keys map[string]string, filters []Filter, order string, where ...any) ([]T, int, error)

func ListPosKeywordFilterOrderModel

func ListPosKeywordFilterOrderModel[T, R any](db *gorm.DB, pos, limit int, keys map[string]string, filters []Filter, order string, where ...any) ([]R, int, error)

List Model

func ListPosKeywordOrder

func ListPosKeywordOrder[T any](db *gorm.DB, pos, limit int, keys map[string]string, order string, where ...any) ([]T, int, error)

func ListPosOrder

func ListPosOrder[T any](db *gorm.DB, pos, limit int, order string, where ...any) ([]T, int, error)

func New

func New[T any](db *gorm.DB, val *T) (*T, error)

func PageScope

func PageScope(page, pageSize int) func(db *gorm.DB) *gorm.DB

func RegisterAdminHandler

func RegisterAdminHandler(r *gin.RouterGroup, m *AdminManager)

RegisterAdmin resolve adminManager & register admin handler

func RegisterObject

func RegisterObject(r gin.IRoutes, obj *WebObject) error

func RegisterObjects

func RegisterObjects(r gin.IRoutes, objs []WebObject)

func RegisterObjectsWithAdmin

func RegisterObjectsWithAdmin(r *gin.RouterGroup, objs []WebObject)

RegisterObjectsWithAdmin quickly Register Admin by webobjects

func Update

func Update[T any](db *gorm.DB, val *T, where ...any) error

Update

func UpdateByID

func UpdateByID[T any, E ~string | ~int](db *gorm.DB, id E, val *T, where ...any) error

func UpdateFields

func UpdateFields[T any](db *gorm.DB, model *T, vals map[string]any) error

func UpdateMapByID

func UpdateMapByID[T any, E ~string | ~int](db *gorm.DB, id E, m map[string]any, where ...any) error

func UpdateSelectByID

func UpdateSelectByID[T any, E ~string | ~int](db *gorm.DB, id E, selects []string, val *T, where ...any) error

Types

type AdminManager

type AdminManager struct {
	AdminObjects []AdminObject
	Names        []string
}

func (*AdminManager) RegisterObject

func (m *AdminManager) RegisterObject(r *gin.RouterGroup, obj WebObject)

type AdminObject

type AdminObject struct {

	// All elements are json format string
	Searchs []string
	Filters []string
	Edits   []string
	Orders  []string

	PrimaryKeyField    string
	PrimaryKeyJsonName string
	// contains filtered or unexported fields
}

type BeforeCreateFunc

type BeforeCreateFunc func(ctx *gin.Context, vptr any, vals map[string]any) error

type BeforeDeleteFunc

type BeforeDeleteFunc func(ctx *gin.Context, vptr any) error

type BeforeRenderFunc

type BeforeRenderFunc func(ctx *gin.Context, vptr any) error

type BeforeUpdateFunc

type BeforeUpdateFunc func(ctx *gin.Context, vptr any, vals map[string]any) error

type Filter

type Filter struct {
	Name  string `json:"name"`
	Op    string `json:"op"`
	Value any    `json:"value"`
}

func (*Filter) GetQuery

func (f *Filter) GetQuery() string

GetQuery return the combined filter SQL statement. such as "age >= ?", "name IN ?".

type GetDB

type GetDB func(c *gin.Context, isCreate bool) *gorm.DB // designed for group

type Key

type Key interface {
	string | uint | int
}

type ListContext

type ListContext struct {
	Pos      int
	Limit    int
	Keywords map[string]string
	Filters  []Filter
	Order    string
	Where    []any
}

List Context

type Order

type Order struct {
	Name string `json:"name"`
	Op   string `json:"op"`
}

func (*Order) GetQuery

func (o *Order) GetQuery() string

GetQuery return the combined order SQL statement. such as "id DESC".

type PrepareQuery

type PrepareQuery func(db *gorm.DB, c *gin.Context) (*gorm.DB, *QueryForm, error)

type QueryForm

type QueryForm struct {
	Pagination bool     `json:"pagination"`
	Pos        int      `json:"pos"`
	Limit      int      `json:"limit"`
	Keyword    string   `json:"keyword,omitempty"`
	Filters    []Filter `json:"filters,omitempty"`
	Orders     []Order  `json:"orders,omitempty"`
	ViewFields []string `json:"-"` // for view
	// contains filtered or unexported fields
}

func DefaultPrepareQuery

func DefaultPrepareQuery(db *gorm.DB, c *gin.Context) (*gorm.DB, *QueryForm, error)

DefaultPrepareQuery return default QueryForm.

type QueryOption

type QueryOption struct {
	Pagination  bool
	Filterables []string
	Editables   []string
	Orderables  []string
	Searchables []string
}

type QueryResult

type QueryResult[T any] struct {
	Total   int    `json:"total,omitempty"`
	Pos     int    `json:"pos,omitempty"`
	Limit   int    `json:"limit,omitempty"`
	Keyword string `json:"keyword,omitempty"`
	Items   T      `json:"items"`
}

func QueryObjects

func QueryObjects(db *gorm.DB, obj *WebObject, form *QueryForm) (r QueryResult[any], err error)

QueryObjects execute query and return data.

type QueryView

type QueryView struct {
	Name    string
	Method  string
	Prepare PrepareQuery
}

type TestClient

type TestClient struct {
	CookieJar http.CookieJar
	Scheme    string
	Host      string
	// contains filtered or unexported fields
}

func NewTestClient

func NewTestClient(r http.Handler) (c *TestClient)

func (*TestClient) Call

func (c *TestClient) Call(method, path string, form any, result any) error

func (*TestClient) CallDelete

func (c *TestClient) CallDelete(path string, form, result any) error

func (*TestClient) CallGet

func (c *TestClient) CallGet(path string, form, result any) error

func (*TestClient) CallPatch

func (c *TestClient) CallPatch(path string, form, result any) error

func (*TestClient) CallPost

func (c *TestClient) CallPost(path string, form any, result any) error

func (*TestClient) CallPut

func (c *TestClient) CallPut(path string, form, result any) error

func (*TestClient) Get

Get return *httptest.ResponseRecorder

func (*TestClient) Post

func (c *TestClient) Post(path string, body []byte) *httptest.ResponseRecorder

Post return *httptest.ResponseRecorder

func (*TestClient) SendReq

func (c *TestClient) SendReq(path string, req *http.Request) *httptest.ResponseRecorder

type WebObject

type WebObject struct {
	Model any
	Group string
	Name  string
	GetDB GetDB

	// config
	// Pagination   bool
	AllowMethods int

	// for query
	EditFields   []string
	FilterFields []string
	OrderFields  []string
	SearchFields []string
	Views        []QueryView

	// hooks
	BeforeCreate BeforeCreateFunc
	BeforeUpdate BeforeUpdateFunc
	BeforeDelete BeforeDeleteFunc
	BeforeRender BeforeRenderFunc
	// contains filtered or unexported fields
}

func (*WebObject) Build

func (obj *WebObject) Build() error

Build fill the properties of obj.

func (*WebObject) RegisterObject

func (obj *WebObject) RegisterObject(r gin.IRoutes) error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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