Skip to content

firetiger-oss/aip

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

aip

Go utilities for building services that comply with Google's API Improvement Proposals (AIPs).

Two packages, both focused on turning AIP request fields into safe, parameterized SQL:

Package AIP What it does
aip160 AIP-160 Parse filter strings into a typed AST and generate parameterized SQL WHERE clauses, with per-field type checking and JSONB support.
aip132 AIP-132 Parse order_by strings into ordered field clauses.
go get github.com/firetiger-oss/aip@latest

aip160 — filters

Declare the fields a caller is allowed to filter on (and their SQL types), parse a filter string, then render it to SQL with bound parameters. Unknown fields and type mismatches are rejected at parse/validation time, so untrusted filter input never reaches your database as raw text.

import (
	"github.com/firetiger-oss/aip/aip160"
)

ctx, err := aip160.NewFilterContext(
	aip160.NewFilterableField(aip160.SQLTypeText, "name"),
	aip160.NewFilterableField(aip160.SQLTypeTimestamp, "create_time"),
	aip160.NewFilterableField(aip160.SQLTypeInt64, "size"),
)
if err != nil {
	// duplicate or malformed field declarations
}

filter, err := aip160.ParseFilter(`name = "report.pdf" AND size > 1024`)
if err != nil {
	// invalid filter syntax or unknown field
}

sql, params, err := ctx.ToSQL(filter)
// sql:    (("name" = $1) AND ("size" > $2))
// params: []any{"report.pdf", int64(1024)}

Columns are quoted and each comparison is parenthesized. Bound parameters use PostgreSQL $N placeholders.

ToSQL(nil) yields an empty clause, so an absent filter is a no-op.

JSONB fields

Filter into a JSONB column with NewJSONBFilterableField / NewJSONBMapFilterableField. The first string argument is the physical JSONB column; the remaining segments are the filter path (and the path within the JSON document). A map<string,string> field is addressed by key with dot notation.

ctx, _ := aip160.NewFilterContext(
	aip160.NewJSONBFilterableField(aip160.SQLTypeText, "body", "display_name"),
	aip160.NewJSONBMapFilterableField("body", "labels"),
)
filter, _ := aip160.ParseFilter(`display_name = "alice" AND labels.env = "prod"`)
sql, params, _ := ctx.ToSQL(filter)
// sql: (("body"->>'display_name' = $1) AND ("body"->'labels'->>'env' = $2))
// params: []any{"alice", "prod"}

aip132 — ordering

import (
	"github.com/firetiger-oss/aip/aip132"
	"github.com/firetiger-oss/aip/aip160"
)

clauses, err := aip132.ParseOrderBy("create_time desc, name")
// []aip132.OrderBy{{FieldPath: ...}, ...}

orderSQL, err := ctx.OrderByToSQL(clauses) // ctx is an aip160.FilterContext
// orderSQL: "create_time" DESC, "name"

OrderByToSQL returns the column list only (no ORDER BY keyword); ascending is the default, so only descending columns carry a direction suffix.

aip160.FilterContext.OrderByToSQL validates ordering fields against the same field set as filtering, so the two stay consistent.

License

Apache-2.0. See LICENSE.

The aip132 package contains code derived from the LUCI project (Apache-2.0); see NOTICE.

About

Go utilities for Google AIP-compliant services: AIP-160 filters → parameterized SQL, AIP-132 ordering.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages