crud

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2023 License: BSD-2-Clause Imports: 13 Imported by: 0

README

go-mod-crud

Package CRUD is meant to make two things: map structs to PostgreSQL tables (like ORM) and create CRUD HTTP endpoint for simple data management.

HTTP endpoint can be set to allow creating, updating, removing new object, along with returning its details, or list of objects. All requests and responses are in the JSON format.

Example usage

Structs (models)

Models are defined with structs as follows (take a closer look at the tags):

type User struct {
	ID                 int    `json:"user_id"`
	Flags              int    `json:"flags"`
	Name               string `json:"name" crud:"req lenmin:2 lenmax:50"`
	Email              string `json:"email" crud:"req"`
	Password           string `json:"password"`
	EmailActivationKey string `json:"email_activation_key" crud:""`
	CreatedAt          int    `json:"created_at"`
	CreatedByUserID    int    `json:"created_by_user_id"`
}

type Session struct {
	ID                 int    `json:"session_id"`
	Flags              int    `json:"flags"`
	Key                string `json:"key" crud:"uniq lenmin:32 lenmax:50"`
	ExpiresAt          int    `json:"expires_at"`
	UserID             int    `json:"user_id" crud:"req"`
}

type Something struct {
	ID                 int    `json:"something_id"`
	Flags              int    `json:"flags"`
	Email              string `json:"email" crud:"req"`
	Age                int    `json:"age" crud:"req valmin:18 valmax:130 val:18"`
	Price              int    `json:"price" crud:"req valmin:0 valmax:9900 val:100"`
	CurrencyRate       int    `json:"currency_rate" crud:"req valmin:40000 valmax:61234 val:10000"`
	PostCode           string `json:"post_code" crud:"req val:32-600"`
}
Field tags

Struct tags define ORM behaviour. go-mod-crud parses tags such as crud, http and various tags starting with crud_. Apart from the last one, a tag define many properties which are separated with space char, and if they contain a value other than bool (true, false), it is added after semicolon char. See below list of all the tags with examples.

Tag Example Explanation
crud crud:"req valmin:0 valmax:130 val:18" Struct field properties defining its valid value for model. See CRUD Field Properties for more info
crud_val crud_val:"Default value" Struct field default value
crud_regexp crud_regexp:"^[0-9]{2}\\-[0-9]{3}$" Regular expression that struct field must match
crud_testvalpattern crud_testvalpattern:DD-DDD Very simple pattern for generating valid test value (used for tests). In the string, D is replaced with a digit
CRUD Field Properties
Property Explanation
req Field is required
uniq Field has to be unique (like UNIQUE on the database column)
valmin If field is numeric, this is minimal value for the field
valmax If field is numeric, this is maximal value for the field
val Default value for the field. If the value is not a simple, short alphanumeric, use the crud_val tag for it
lenmin If field is string, this is a minimal length of the field value
lenmax If field is string, this is a maximal length of the field value
Database storage

Currently, go-mod-crud supports only PostgreSQL as a storage for objects.

Controller

To perform model database actions, a Controller object must be created. See below example that modify object(s) in the database.

// Create connection with sql
conn, _ := sql.Open("postgres", fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", dbHost, dbPort, dbUser, dbPass, dbName))
defer conn.Close()

// Create CRUD controller and an instance of a struct
c := crud.NewController(conn, "app1_")
user := &User{}

err = c.CreateDBTable(user) // Run 'CREATE TABLE'

user.Email = "test@example.com"
user.Name = "Nicholas"
user.CreatedAt = time.Now().Unix()
err = c.SaveToDB(user) // Insert object to database table

user.Email = "newemail@example.com"
err = c.SaveToDB() // Update object in the database table

err = c.DeleteFromDB() // Delete object from the database table

err = c.DropDBTable(user) // Run 'DROP TABLE'
HTTP Endpoints

With go-mod-crud, HTTP endpoints can be created to manage objects stored in the database.

If User struct is used for HTTP endpoint, fields such as Password will be present when listing users. Therefore, it's necessary to create new structs to define CRUD endpoints' input and/or output. These structs unfortunately need validation tags (which can be different than the ones from "main" struct).

In below example, User_Create defines input fields when creating a User, User_Update defines fields that are meant to change when permorming update, User_UpdatePassword is an additional struct just for updating User password, and finally - fields in User_List will be visible when listing users or reading one user. (You can define these as you like).

type User_Create {
	ID       int    `json:"user_id"`
	Name     string `json:"name" crud:"req lenmin:2 lenmax:50"`
	Email    string `json:"email" crud:"req"`
	Password string `json:"password"`
}
type User_Update {
	ID       int    `json:"user_id"`
	Name     string `json:"name" crud:"req lenmin:2 lenmax:50"`
	Email    string `json:"email" crud:"req"`
}
type User_UpdatePassword {
	ID       int `json:"user_id"`
	Password string `json:"password"`
}
type User_List {
	ID       int    `json:"user_id"`
	Name     string `json:"name"
}
var parentFunc = func() interface{} { return &User; }
var createFunc = func() interface{} { return &User_Create; }
var readFunc   = func() interface{} { return &User_List; }
var updateFunc = func() interface{} { return &User_Update; }
var listFunc   = func() interface{} { return &User_List; }

var updatePasswordFunc = func() interface{} { return &User_UpdatePassword; }

http.HandleFunc("/users/", c.GetHTTPHandler("/users/", parentFunc, createFunc, readFunc, updateFunc, parentFunc, listFunc))
http.HandleFunc("/users/password/", c.GetHTTPHandler("/users/password/", parentFunc, nil, nil, updatePasswordFunc, nil, nil))
log.Fatal(http.ListenAndServe(":9001", nil))

In the example, /users/ CRUDL endpoint is created and it allows to:

  • create new User by sending JSON payload using PUT method
  • update existing User by sending JSON payload to /users/:id with PUT method
  • get existing User details with making GET request to /users/:id
  • delete existing User with DELETE request to /users/:id
  • get list of Users with making GET request to /users/ with optional query parameters such as limit, offset to slice the returned list and filter_ params (eg. filter_email) to filter out records with by specific fields

When creating or updating an object, JSON payload with object details is required. It should match the struct used for Create and Update operations. In this case, User_Create and User_Update.

{
	"email": "test@example.com",
	"name": "Nicholas",
	...
}

Output from the endpoint is in JSON format as well and it follows below structure:

{
	"ok": 1,
	"err_text": "...",
	"data": {
		...
	}
}

Documentation

Overview

Package CRUD is meant to make two things: map structs to PostgreSQL tables (like ORM) and create CRUD HTTP endpoint for simple data management.

HTTP endpoint can be set to allow creating, updating, removing new object, along with returning its details, or list of objects. All requests and responses are in the JSON format.

Please follow GitHub page for an example: https://github.com/MikolajGasior/go-mod-crud/

Index

Constants

View Source
const OpCreate = 8
View Source
const OpDelete = 16
View Source
const OpList = 32
View Source
const OpRead = 2

Values for CRUD operations

View Source
const OpUpdate = 4
View Source
const TypeInt = 128
View Source
const TypeInt64 = 64
View Source
const TypeString = 256
View Source
const VERSION = "0.3.0"

Variables

This section is empty.

Functions

This section is empty.

Types

type Controller

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

Controller is the main component that gets and saves objects in the database and generates CRUD HTTP handler that can be attached to an HTTP server.

func NewController

func NewController(dbConn *sql.DB, tblPrefix string) *Controller

NewController returns new Controller object

func (Controller) CreateDBTable

func (c Controller) CreateDBTable(obj interface{}) *ErrController

CreateDBTable creates database table to store specified type of objects. It takes struct name and its fields, converts them into table and columns names (all lowercase with underscore), assigns column type based on the field type, and then executes "CREATE TABLE" query on attached DB connection

func (Controller) CreateDBTables

func (c Controller) CreateDBTables(xobj ...interface{}) *ErrController

CreateDBTables creates tables in the database for specified objects (see CreateDBTable for a single struct)

func (Controller) DeleteFromDB

func (c Controller) DeleteFromDB(obj interface{}) *ErrController

DeleteFromDB removes object from the database table and it does that only when ID field is set (greater than 0). Once deleted from the DB, all field values are zeroed

func (Controller) DropDBTable

func (c Controller) DropDBTable(obj interface{}) *ErrController

DropDBTable drops database table used to store specified type of objects. It just takes struct name, converts it to lowercase-with-underscore table name and executes "DROP TABLE" query using attached DB connection

func (Controller) DropDBTables

func (c Controller) DropDBTables(xobj ...interface{}) *ErrController

DropDBTables drop tables in the database for specified objects (see DropDBTable for a single struct)

func (Controller) GetFiltersInterfaces

func (c Controller) GetFiltersInterfaces(mf map[string]interface{}) []interface{}

GetFiltersInterfaces returns list of interfaces from filters map (used in querying)

func (Controller) GetFromDB

func (c Controller) GetFromDB(newObjFunc func() interface{}, order []string, limit int, offset int, filters map[string]interface{}) ([]interface{}, *ErrController)

GetFromDB runs a select query on the database with specified filters, order, limit and offset and returns a list of objects

func (Controller) GetHTTPHandler

func (c Controller) GetHTTPHandler(uri string, newObjFunc func() interface{}, newObjCreateFunc func() interface{}, newObjReadFunc func() interface{}, newObjUpdateFunc func() interface{}, newObjDeleteFunc func() interface{}, newObjListFunc func() interface{}) http.Handler

GetHTTPHandler returns a CRUD HTTP handler that can be attached to HTTP server. It creates a CRUD endpoint for creating, reading, updating, deleting and listing objects. Each of the func() argument should be funcs that create new object (instance of a struct). For each of the operation (create, read etc.), a different struct with different fields can be used. It's important to pass "uri" argument same as the one that the handler is attached to.

func (Controller) GetModelFieldInterfaces

func (c Controller) GetModelFieldInterfaces(obj interface{}) []interface{}

GetModelFieldInterfaces returns list of interfaces to object's fields without the ID field

func (*Controller) GetModelIDInterface

func (c *Controller) GetModelIDInterface(obj interface{}) interface{}

GetModelIDInterface returns an interface{} to ID field of an object

func (*Controller) GetModelIDValue

func (c *Controller) GetModelIDValue(obj interface{}) int64

GetModelIDValue returns value of ID field (int64) of an object

func (Controller) ResetFields

func (c Controller) ResetFields(obj interface{})

ResetFields zeroes object's field values

func (Controller) SaveToDB

func (c Controller) SaveToDB(obj interface{}) *ErrController

SaveToDB takes object, validates its field values and saves it in the database. If ID field is already set (it's greater than 0) then the function assumes that record with such ID already exists in the database and the function with execute an "UPDATE" query. Otherwise it will be "INSERT". After inserting, new record ID is set to struct's ID field

func (Controller) SetFromDB

func (c Controller) SetFromDB(obj interface{}, id string) *ErrController

SetFromDB sets object's fields with values from the database table with a specific id. If record does not exist in the database, all field values in the struct are zeroed

func (Controller) Validate

func (c Controller) Validate(obj interface{}, filters map[string]interface{}) (bool, map[string]int, error)

Validate checks object's fields. It returns result of validation as a bool and list of fields with invalid value

type ErrController

type ErrController struct {
	Op  string
	Err error
}

ErrController wraps original error that occurred in Err with name of the operation/step that failed, which is in Op field

func (*ErrController) Error

func (e *ErrController) Error() string

func (*ErrController) Unwrap

func (e *ErrController) Unwrap() error

type ErrHelper

type ErrHelper struct {
	Op  string
	Tag string
	Err error
}

ErrHelper wraps original error with operation/step where the error occured and optionally with a tag when parsing "crud" failed

func (ErrHelper) Error

func (e ErrHelper) Error() string

func (ErrHelper) Unwrap

func (e ErrHelper) Unwrap() error

type ErrValidation

type ErrValidation struct {
	Fields map[string]int
	Err    error
}

ErrValidation wraps error occuring during object validation

func (ErrValidation) Error

func (e ErrValidation) Error() string

func (ErrValidation) Unwrap

func (e ErrValidation) Unwrap() error

type HTTPResponse

type HTTPResponse struct {
	OK      int8                   `json:"ok"`
	ErrText string                 `json:"err_text"`
	Data    map[string]interface{} `json:"data"`
}

HTTPResponse is a base structure for all the HTTP responses from HTTP endpoints

func NewHTTPResponse

func NewHTTPResponse(ok int8, errText string) HTTPResponse

NewHTTPResponse returns new HTTPResponse object

type Helper

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

Helper reflects the object to generate and cache PostgreSQL queries (CREATE TABLE, INSERT, UPDATE etc.). Database table and column names are lowercase with underscore and they are generated from field names. Helper is created within Controller and there is no need to instantiate it

func NewHelper

func NewHelper(obj interface{}, dbTblPrefix string, forceName string, sourceHelper *Helper) *Helper

NewHelper takes object and database table name prefix as arguments and returns Helper instance

func (*Helper) Err

func (h *Helper) Err() *ErrHelper

Err returns error that occurred when reflecting struct

func (*Helper) GetFlags

func (h *Helper) GetFlags() int

GetFlags returns flags

func (Helper) GetQueryCreateTable

func (h Helper) GetQueryCreateTable() string

GetQueryCreateTable return create table query

func (*Helper) GetQueryDeleteById

func (h *Helper) GetQueryDeleteById() string

GetQueryDeleteById returns delete query

func (Helper) GetQueryDropTable

func (h Helper) GetQueryDropTable() string

GetQueryDropTable returns drop table query

func (*Helper) GetQueryInsert

func (h *Helper) GetQueryInsert() string

GetQueryInsert returns insert query

func (*Helper) GetQuerySelect

func (h *Helper) GetQuerySelect(order []string, limit int, offset int, filters map[string]interface{}, orderFieldsToInclude map[string]bool, filterFieldsToInclude map[string]bool) string

func (*Helper) GetQuerySelectById

func (h *Helper) GetQuerySelectById() string

GetQuerySelectById returns select query

func (*Helper) GetQueryUpdateById

func (h *Helper) GetQueryUpdateById() string

GetQueryUpdateById returns update query

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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