GormQueryFromJson

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 10, 2024 License: GPL-3.0 Imports: 4 Imported by: 0

README

Gorm Query from JSON

Want your users to be able to perform advanced searches on your database without all the hassle of handling each individual input - just provide a schema and the library will validate that your users are only doing queries you permit.

Install

go get github.com/CADawg/GormQueryFromJson

Usage

Set maximum levels of recursion

GormQueryFromJson.MaxDepth = 10

Define a schema

var mainSchema = []GormQueryFromJson.AcceptableQueryTypes{
	{
		ColumnName: "id",
		QueryTypes: []query.TypeIdentifier{GormQueryFromJson.TypeNumber},
		Limit:      1,
	},
	{
		ColumnName: "actor",
		QueryTypes: []query.TypeIdentifier{GormQueryFromJson.TypeString, GormQueryFromJson.TypeStrictString},
		Limit:      10,
	},
	{
		ColumnName: "target",
		QueryTypes: []query.TypeIdentifier{GormQueryFromJson.TypeString, GormQueryFromJson.TypeStrictString},
		Limit:      10,
	}
}
  • Limit sets the maximum times this column can be targeted in a query.
  • QueryTypes defines what queries can be run against the column
    • Or Is not a type of query for this purpose, only its children count

Define an endpoint like so

http.HandleFunc("POST /testQuery", func(res http.ResponseWriter, req *http.Request) {
    var queries []query.JSON

    err := json.NewDecoder(req.Body).Decode(&queries)

    if err != nil {
        http.Error(res, fmt.Sprintf("Error: %v", err), http.StatusBadRequest)
        return
    }

    statement := db.ToSQL(func(tx *gorm.DB) *gorm.DB {
        // use gorm to create a sql query and print out the statement
        tx = tx.Model(&types.HistoryEntry{}).Limit(100)

        // !!! This is our function
        tx, _, err = GormQueryFromJson.DoQuery(queries, tx, mainSchema)

        return tx.Find(&[]types.HistoryEntry{})
    })

    if err != nil {
        http.Error(res, fmt.Sprintf("Error: %v", err), http.StatusBadRequest)
        return
    }

    _, err = res.Write([]byte(statement))
})

You can use it however you like, just make sure that you have not placed any existing where conditions on the query. You can do so after calling DoQuery

Queries (User Side)

[
    {
        "type": "or",
        "query": [
            {
                "type": "string",
                "query": {
                    "column": "target",
                    "contains": "user2"
                }
            },
            {
                "type": "string",
                "query": {
                    "column": "target",
                    "contains": "user1"
                }
            }
        ]
    },
    {
        "type": "or",
        "query": [
            {
                "type": "string",
                "query": {
                    "column": "actor",
                    "contains": "user3"
                }
            },
            {
                "type": "string",
                "query": {
                    "column": "target",
                    "contains": "user4"
                }
            }
        ]
    },
    {
        "type": "number",
        "query": {
            "column": "id",
            "greaterThanOrEqual": 5000000
        }
    }
]

-> SQL (Where Clauses generated by above)

SELECT * FROM `table` WHERE (target LIKE '%user2%' OR target LIKE '%user1%') AND (actor LIKE '%user3%' OR target LIKE '%user4%') AND id >= 5000000

If any columns you have not allowed are included or the amount is above the limit, those where clauses will be discarded.

Documentation

Index

Constants

View Source
const MaxDepth = 10

MaxDepth is how deep a query will go before throwing.

Variables

View Source
var ErrMaxDepth = fmt.Errorf("max depth exceeded")

Functions

This section is empty.

Types

type AcceptableQueryTypes

type AcceptableQueryTypes struct {
	// ColumnName is the name of the column in the database
	ColumnName string `json:"columnName"`
	// QueryTypes are the types of queries that can be run on this column
	QueryTypes []TypeIdentifier `json:"queryTypes"`
	// Limit max number of queries that can be run on this column
	Limit int `json:"limit"`
}

type AndQuery

type AndQuery struct {
	Queries []BaseQuery `json:"queries"`
}

func (*AndQuery) ColumnUsages

func (query *AndQuery) ColumnUsages() []ColumnUsageInfo

func (*AndQuery) Depth

func (query *AndQuery) Depth() (int, error)

func (*AndQuery) GetColumnName added in v0.2.0

func (query *AndQuery) GetColumnName() (bool, string)

func (*AndQuery) Query

func (query *AndQuery) Query(database *gorm.DB) *gorm.DB

func (*AndQuery) Type

func (query *AndQuery) Type() TypeIdentifier

func (*AndQuery) UnmarshalJSON

func (query *AndQuery) UnmarshalJSON(data []byte) error

type BaseQuery

type BaseQuery interface {
	Query(database *gorm.DB) *gorm.DB
	// ColumnUsages returns a string of each column used in the query
	ColumnUsages() []ColumnUsageInfo
	// Depth returns the depth of the query or error if it is too deep
	Depth() (int, error)
	Type() TypeIdentifier
	// GetColumnName Returns false, "" if the query is an OrQuery
	GetColumnName() (bool, string)
}

func GetQueryType

func GetQueryType(id TypeIdentifier) BaseQuery

type ColumnUsageInfo

type ColumnUsageInfo struct {
	ColumnName string
	QueryType  TypeIdentifier
}

type JSON

type JSON struct {
	QueryType TypeIdentifier  `json:"type"`
	Query     json.RawMessage `json:"query"`
}

type NumberQuery

type NumberQuery struct {
	ColumnName         string `json:"column"`
	Equals             *int   `json:"equals,omitempty"`
	NotEquals          *int   `json:"notEquals,omitempty"`
	GreaterThan        *int   `json:"greaterThan,omitempty"`
	GreaterThanOrEqual *int   `json:"greaterThanOrEqual,omitempty"`
	LessThan           *int   `json:"lessThan,omitempty"`
	LessThanOrEqual    *int   `json:"lessThanOrEqual,omitempty"`
}

func (*NumberQuery) ColumnUsages

func (query *NumberQuery) ColumnUsages() []ColumnUsageInfo

func (*NumberQuery) Depth

func (query *NumberQuery) Depth() (int, error)

func (*NumberQuery) GetColumnName

func (query *NumberQuery) GetColumnName() (bool, string)

func (*NumberQuery) Query

func (query *NumberQuery) Query(database *gorm.DB) *gorm.DB

func (*NumberQuery) Type

func (query *NumberQuery) Type() TypeIdentifier

type OrQuery

type OrQuery struct {
	Queries []BaseQuery `json:"queries"`
}

func (*OrQuery) ColumnUsages

func (query *OrQuery) ColumnUsages() []ColumnUsageInfo

func (*OrQuery) Depth

func (query *OrQuery) Depth() (int, error)

func (*OrQuery) GetColumnName

func (query *OrQuery) GetColumnName() (bool, string)

func (*OrQuery) Query

func (query *OrQuery) Query(database *gorm.DB) *gorm.DB

func (*OrQuery) Type

func (query *OrQuery) Type() TypeIdentifier

func (*OrQuery) UnmarshalJSON

func (query *OrQuery) UnmarshalJSON(data []byte) error

type Results

type Results struct {
	AcceptedQueries []BaseQuery `json:"acceptedQueries"`
	// NotAcceptedQueries are queries that were not accepted by the schema
	// they may be valid for another DoQuery call, but not this one (subqueries .etc.)
	NotAcceptedQueries []BaseQuery `json:"notAcceptedQueries"`
}

func DoQuery

func DoQuery(queries []JSON, database *gorm.DB, schema []AcceptableQueryTypes) (*gorm.DB, Results, error)

type StrictStringQuery

type StrictStringQuery struct {
	ColumnName string `json:"column"`
	Equals     string `json:"equals"`
}

func (*StrictStringQuery) ColumnUsages

func (query *StrictStringQuery) ColumnUsages() []ColumnUsageInfo

func (*StrictStringQuery) Depth

func (query *StrictStringQuery) Depth() (int, error)

func (*StrictStringQuery) GetColumnName

func (query *StrictStringQuery) GetColumnName() (bool, string)

func (*StrictStringQuery) Query

func (query *StrictStringQuery) Query(database *gorm.DB) *gorm.DB

func (*StrictStringQuery) Type

func (query *StrictStringQuery) Type() TypeIdentifier

type StringQuery

type StringQuery struct {
	ColumnName string  `json:"column"`
	Equals     *string `json:"equals,omitempty"`
	Contains   *string `json:"contains,omitempty"`
}

func (*StringQuery) ColumnUsages

func (query *StringQuery) ColumnUsages() []ColumnUsageInfo

func (*StringQuery) Depth

func (query *StringQuery) Depth() (int, error)

func (*StringQuery) GetColumnName

func (query *StringQuery) GetColumnName() (bool, string)

func (*StringQuery) Query

func (query *StringQuery) Query(database *gorm.DB) *gorm.DB

func (*StringQuery) Type

func (query *StringQuery) Type() TypeIdentifier

type TypeIdentifier

type TypeIdentifier string
const (
	TypeNumber       TypeIdentifier = "number"
	TypeString       TypeIdentifier = "string"
	TypeStrictString TypeIdentifier = "strict_string"
	TypeOr           TypeIdentifier = "or"
	TypeAnd          TypeIdentifier = "and"
)

Jump to

Keyboard shortcuts

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