You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
2.6 KiB
Go

package genericmethods_test
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"strings"
"testing"
)
type Validator interface {
Validate() error
}
type FooRequest struct {
A int `json:"a"`
B string `json:"b"`
}
func (foo FooRequest) Validate() error {
if foo.A < 0 {
return fmt.Errorf(`parameter "a" cannot be lesser than zero`)
}
if !strings.HasPrefix(foo.B, "baz-") {
return fmt.Errorf(`parameter "b" has wrong prefix`)
}
return nil
}
func DecodeAndValidateJSON_Generic[T Validator](r *http.Request) (T, error) {
var value T
err := json.NewDecoder(r.Body).Decode(&value)
if err != nil {
var zero T
return zero, err
}
if err := value.Validate(); err != nil {
var zero T
return zero, err
}
return value, nil
}
func TestDecodeAndValidateJSON_Generic(t *testing.T) {
m := http.NewServeMux()
m.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
foo, err := DecodeAndValidateJSON_Generic[FooRequest](r)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(foo)
})
}
func DecodeAndValidateJSON_Interface(r *http.Request, target *Validator) error {
err := json.NewDecoder(r.Body).Decode(target)
if err != nil {
return err
}
if err := (*target).Validate(); err != nil {
return err
}
return nil
}
func TestDecodeAndValidateJSON_Interface(t *testing.T) {
m := http.NewServeMux()
m.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
var foo Validator = FooRequest{}
if err := DecodeAndValidateJSON_Interface(r, &foo); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(foo)
})
}
type WithPrimaryKey interface {
PrimaryKeyPtr() *string
}
type Ref[T WithPrimaryKey] string
type Table[T WithPrimaryKey] struct {
Name string
PkColumn string
}
func IdToRef[T WithPrimaryKey](table Table[T], id string) (Ref[T], error) {
if !strings.HasPrefix(id, table.Name+":") {
var zero Ref[T]
return zero, fmt.Errorf(`invalid reference %v for table %v`, id, table)
}
return Ref[T](id), nil
}
func Read[T WithPrimaryKey](db *sql.DB, table Table[T], ref Ref[T]) (*T, error) {
result := db.QueryRow(
fmt.Sprintf(
`SELECT * FROM %s WHERE %s = ?`,
table.Name, table.PkColumn,
),
string(ref),
)
var value T
if err := result.Scan(&value); err != nil {
return nil, err
}
return &value, nil
}
type User struct {
Username string
FirstName string
LastName string
}
var _ WithPrimaryKey = &User{}
func (u *User) PrimaryKeyPtr() *string {
return &u.Username
}