goprime

module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2024 License: MIT

README

goprime

Overview

The goprime package is a Go library designed to convert JSON filter specifications into SQL WHERE clauses. It provides a flexible and secure way to generate SQL conditions from JSON input, created for JSON that is generated by PrimeNG or any other similar JSON source. goprime ensures security through the use of placeholders, preventing SQL injection, and offers flexibility for custom filter implementations and column validation.

Features

  • Generate SQL Conditions: Converts JSON filter specifications into SQL WHERE clauses.
  • Flexible Filter Registration: Register custom filters for various match modes.
  • Column Validation: Register validators to enforce constraints on column names.
  • Placeholder-Based Security: Uses placeholders to safeguard against SQL injection.

Installation

To install the goprime package, use the following go get command:

go get -u github.com/AdamShannag/goprime

JSON Filter Specifications

goprime processes JSON filter specifications that follow the expected format. The format typically includes fields and match modes, and it can be generated by PrimeNG table filters or any other system that conforms to this structure.

Example JSON
{
  "name": [
    {"value": "James", "matchMode": "startsWith", "operator": "and"},
    {"value": "Butt", "matchMode": "endsWith", "operator": "and"}
  ],
  "country.name": [
    {"value": null, "matchMode": "startsWith", "operator": "and"}
  ],
  "representative": [
    {"value": ["Amy Elsner", "Anna Fali", "Bernardo Dominic", "Elwin Sharvill"], "matchMode": "in", "operator": "and"}
  ],
  "date": [
    {"value": "2024-08-12T21:00:00.000Z", "matchMode": "dateAfter", "operator": "and"},
    {"value": "2024-08-20T21:00:00.000Z", "matchMode": "dateBefore", "operator": "and"}
  ],
  "activity": [
    {"value": [68, 100], "matchMode": "between", "operator": "and"}
  ]
}

This JSON format is versatile and can be adapted to various sources as long as the structure is maintained.

Generating SQL Conditions

goprime translates the JSON filter specifications into SQL conditions. For the provided JSON example, the generated SQL WHERE conditions might look like this:

(name LIKE $1 AND name LIKE $2) AND
(country.name LIKE $3) AND
(representative IN ($4, $5, $6, $7)) AND
(date >= $8 AND date <= $9) AND
(activity BETWEEN $10 AND $11)

Registering Custom Filters

You can register custom filters for different match modes to control how each filter is applied in SQL conditions. Here’s how to set up custom filters:

// Initialize the Filter with a numbered placeholder
pf := prime.New(placeholder.Numbered("$"))

// Register filters
pf.RegisterFilter("startsWith", filters.NewPatternMatchFilter("LIKE", filters.POST))
pf.RegisterFilter("endsWith", filters.NewPatternMatchFilter("LIKE", filters.PRE))
pf.RegisterFilter("in", filters.InFilter(0))
pf.RegisterFilter("between", filters.BetweenFilter(0))
pf.RegisterFilter("dateAfter", filters.ValueFilter(">="))
pf.RegisterFilter("dateBefore", filters.ValueFilter("<="))
Implementing Custom Filters

To create your own custom filters, implement the Filter interface:

// CustomFilter is an example of a custom filter implementation
type CustomFilter struct {
	// Custom fields and methods here
}

func (f *CustomFilter) Apply(column string, totalArguments int, currentIndex int, placeholder placeholder.Placeholder) string {
	// Implement custom SQL condition generation
	return fmt.Sprintf("(%s CUSTOM_CONDITION %s)", column, placeholder.Get(currentIndex))
}

func (f *CustomFilter) EnrichValue(value *any) error {
	// Implement custom value enrichment
	return nil
}

Column Validation

Enforce constraints on column names by registering validators. This helps ensure that only valid columns are used in queries.

// Register column validators
pf.RegisterColumnValidator(column.AllowedValidator{"name", "country.name", "representative", "date", "activity"})

regexValidator, err := column.NewRegexValidator(`^[a-zA-Z0-9_]+$`)
if err != nil {
	log.Fatal(err)
}
pf.RegisterColumnValidator(regexValidator)
Implementing Custom Validators

To create your own custom validators, implement the Validator interface:

// CustomValidator is an example of a custom column validator
type CustomValidator struct {
	// Custom fields and methods here
}

func (v *CustomValidator) Validate(column string) error {
	// Implement custom column validation logic
	return nil
}

func (v *CustomValidator) Name() string {
	// Return the name of the validator
	return "CustomValidator"
}

Placeholder-Based Security

To prevent SQL injection, goprime uses placeholders in SQL conditions. This approach ensures that user inputs are securely handled in queries.

Implementing Custom Placeholders

If you need a custom placeholder format, implement the Placeholder interface:

// CustomPlaceholder is an example of a custom placeholder implementation
type CustomPlaceholder struct {
	// Custom fields and methods here
}

func (p *CustomPlaceholder) Get(index int) string {
	// Implement custom placeholder format
	return fmt.Sprintf("$%d", index)
}

func (p *CustomPlaceholder) Numbered() bool {
	// Indicate if the placeholder format is numbered
	return true
}

Example Usage

Here’s an example of how to use goprime to generate SQL conditions from a filter specification read from a JSON string:

package main

import (
	"encoding/json"
	"fmt"
	"github.com/AdamShannag/goprime/column"
	"github.com/AdamShannag/goprime/filter"
	"github.com/AdamShannag/goprime/filters"
	"github.com/AdamShannag/goprime/placeholder"
	"github.com/AdamShannag/goprime/prime"
	"log"
)

func main() {
	// Example JSON filter specification
	filterRequest := `{
		"name": [
			{"value": "James", "matchMode": "startsWith", "operator": "and"},
			{"value": "Butt", "matchMode": "endsWith", "operator": "and"}
		],
		"country.name": [
			{"value": null, "matchMode": "startsWith", "operator": "and"}
		],
		"representative": [
			{"value": ["Amy Elsner", "Anna Fali", "Bernardo Dominic", "Elwin Sharvill"], "matchMode": "in", "operator": "and"}
		],
		"date": [
			{"value": "2024-08-12T21:00:00.000Z", "matchMode": "dateAfter", "operator": "and"},
			{"value": "2024-08-20T21:00:00.000Z", "matchMode": "dateBefore", "operator": "and"}
		],
		"activity": [
			{"value": [68, 100], "matchMode": "between", "operator": "and"}
		]
	}`

	// Unmarshal the JSON filter specification into the Specs type
	specs := prime.Specs{}
	err := json.Unmarshal([]byte(filterRequest), &specs)
	if err != nil {
		log.Fatalf("Error unmarshalling JSON: %v", err)
	}

	// Initialize the Filter with a numbered placeholder
	pf := prime.New(placeholder.Numbered("$"))

	// Register filters
	pf.RegisterFilter("startsWith", filters.NewPatternMatchFilter("LIKE", filters.POST))
	pf.RegisterFilter(filter.ENDS_WITH, filters.NewPatternMatchFilter("LIKE", filters.PRE))
	pf.RegisterFilter("in", filters.InFilter(0))
	pf.RegisterFilter("between", filters.BetweenFilter(0))
	pf.RegisterFilter(filter.DATE_AFTER, filters.ValueFilter(">="))
	pf.RegisterFilter("dateBefore", filters.ValueFilter("<="))

	// Register column validators
	pf.RegisterColumnValidator(column.AllowedValidator{"name", "country.name", "representative", "date", "activity"})

	// Validate column names
	err = pf.ValidateColumns(specs)
	if err != nil {
		log.Fatalf("Error validating columns: %v", err)
	}

	// Generate SQL condition and values
	vals, condition, err := pf.Sql(specs)
	if err != nil {
		log.Fatalf("Error generating SQL: %v", err)
	}

	// Print the results
	fmt.Println("SQL Condition:", condition)
	// SQL Condition: ((name LIKE $1) and (name LIKE $2)) and ((representative IN ($3,$4,$5,$6))) and ((date >= $7) and (date <= $8)) and ((activity BETWEEN $9 AND $10))
	fmt.Println("Values:", vals)
	// Values: [James% %Butt Amy Elsner Anna Fali Bernardo Dominic Elwin Sharvill 2024-08-12T21:00:00.000Z 2024-08-20T21:00:00.000Z 68 100]
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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