reomved now unnecessary libraries

This commit is contained in:
Colin Henry 2024-09-06 20:27:26 -07:00
parent 74f9fc64f4
commit b738f78e6e
4 changed files with 0 additions and 729 deletions

View File

@ -1,109 +0,0 @@
// based on the "way" package by matt ryer
// Copyright (c) 2016 Mat Ryer
package http
import (
"context"
"net/http"
"strings"
)
// contextKey is the context key type for storing
// parameters in context.Context.
type contextKey string
// Router routes HTTP requests.
type ServeMux struct {
routes []*route
// NotFound is the http.Handler to call when no routes
// match. By default uses http.NotFoundHandler().
NotFound http.Handler
}
// NewRouter makes a new Router.
func NewServeMux() *ServeMux {
return &ServeMux{
NotFound: http.NotFoundHandler(),
}
}
func (r *ServeMux) pathSegments(p string) []string {
return strings.Split(strings.Trim(p, "/"), "/")
}
// Handle adds a handler with the specified pattern.
// Pattern can contain path segments such as: /item/:id which is
// accessible via the Param function.
// If pattern ends with trailing /, it acts as a prefix.
func (r *ServeMux) Handle(pattern string, handler http.Handler) {
route := &route{
segs: r.pathSegments(pattern),
handler: handler,
prefix: strings.HasSuffix(pattern, "/") || strings.HasSuffix(pattern, "..."),
}
r.routes = append(r.routes, route)
}
// HandleFunc is the http.HandlerFunc alternative to http.Handle.
func (r *ServeMux) HandleFunc(pattern string, fn http.HandlerFunc) {
r.Handle(pattern, fn)
}
// ServeHTTP routes the incoming http.Request based on path
func (r *ServeMux) ServeHTTP(w http.ResponseWriter, req *http.Request) {
segs := r.pathSegments(req.URL.Path)
for _, route := range r.routes {
if ctx, ok := route.match(req.Context(), r, segs); ok {
route.handler.ServeHTTP(w, req.WithContext(ctx))
return
}
}
r.NotFound.ServeHTTP(w, req)
}
// Param gets the path parameter from the specified Context.
// Returns an empty string if the parameter was not found.
func Param(ctx context.Context, param string) string {
vStr, ok := ctx.Value(contextKey(param)).(string)
if !ok {
return ""
}
return vStr
}
type route struct {
segs []string
handler http.Handler
prefix bool
}
func (r *route) match(ctx context.Context, router *ServeMux, segs []string) (context.Context, bool) {
if len(segs) > len(r.segs) && !r.prefix {
return nil, false
}
for i, seg := range r.segs {
if i > len(segs)-1 {
return nil, false
}
isParam := false
if strings.HasPrefix(seg, "{") {
isParam = true
seg = strings.Trim(seg, "{}")
}
if !isParam { // verbatim check
if strings.HasSuffix(seg, "...") {
if strings.HasPrefix(segs[i], seg[:len(seg)-3]) {
return ctx, true
}
}
if seg != segs[i] {
return nil, false
}
}
if isParam {
ctx = context.WithValue(ctx, contextKey(seg), segs[i])
}
}
return ctx, true
}

View File

@ -1,231 +0,0 @@
package http
import (
"context"
"net/http"
"net/http/httptest"
"testing"
)
var tests = []struct {
// RouteMethod string
RoutePattern string
Method string
Path string
Match bool
Params map[string]string
}{
// simple path matching
{
"/one",
"GET", "/one", true, nil,
},
{
"/two",
"GET", "/two", true, nil,
},
{
"/three",
"GET", "/three", true, nil,
},
// methods
{
"/methodcase",
"GET", "/methodcase", true, nil,
},
{
"/methodcase",
"get", "/methodcase", true, nil,
},
{
"/methodcase",
"get", "/methodcase", true, nil,
},
{
"/method1",
"POST", "/method1", true, nil,
},
{
"/method2",
"GET", "/method2", true, nil,
},
{
"/method3",
"PUT", "/method3", true, nil,
},
// all methods
{
"/all-methods",
"GET", "/all-methods", true, nil,
},
{
"/all-methods",
"POST", "/all-methods", true, nil,
},
{
"/all-methods",
"PUT", "/all-methods", true, nil,
},
// nested
{
"/parent/child/one",
"GET", "/parent/child/one", true, nil,
},
{
"/parent/child/two",
"GET", "/parent/child/two", true, nil,
},
{
"/parent/child/three",
"GET", "/parent/child/three", true, nil,
},
// slashes
{
"slashes/one",
"GET", "/slashes/one", true, nil,
},
{
"/slashes/two",
"GET", "slashes/two", true, nil,
},
{
"slashes/three/",
"GET", "/slashes/three", true, nil,
},
{
"/slashes/four",
"GET", "slashes/four/", true, nil,
},
// prefix
{
"/prefix/",
"GET", "/prefix/anything/else", true, nil,
},
{
"/not-prefix",
"GET", "/not-prefix/anything/else", false, nil,
},
{
"/prefixdots...",
"GET", "/prefixdots/anything/else", true, nil,
},
{
"/prefixdots...",
"GET", "/prefixdots", true, nil,
},
// path params
{
"/path-param/{id}",
"GET", "/path-param/123", true, map[string]string{"id": "123"},
},
{
"/path-params/{era}/{group}/{member}",
"GET", "/path-params/60s/beatles/lennon", true, map[string]string{
"era": "60s",
"group": "beatles",
"member": "lennon",
},
},
{
"/path-params-prefix/{era}/{group}/{member}/",
"GET", "/path-params-prefix/60s/beatles/lennon/yoko", true, map[string]string{
"era": "60s",
"group": "beatles",
"member": "lennon",
},
},
// misc no matches
{
"/not/enough",
"GET", "/not/enough/items", false, nil,
},
{
"/not/enough/items",
"GET", "/not/enough", false, nil,
},
}
func TestMux(t *testing.T) {
for _, test := range tests {
r := NewServeMux()
match := false
var ctx context.Context
r.Handle(test.RoutePattern, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
match = true
ctx = r.Context()
}))
req, err := http.NewRequest(test.Method, test.Path, nil)
if err != nil {
t.Errorf("NewRequest: %s", err)
}
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if match != test.Match {
t.Errorf("expected match %v but was %v: %s %s", test.Match, match, test.Method, test.Path)
}
if len(test.Params) > 0 {
for expK, expV := range test.Params {
// check using helper
actualValStr := Param(ctx, expK)
if actualValStr != expV {
t.Errorf("Param: context value %s expected \"%s\" but was \"%s\"", expK, expV, actualValStr)
}
}
}
}
}
func TestMultipleRoutesDifferentMethods(t *testing.T) {
r := NewServeMux()
var match string
r.Handle("/route", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
match = "GET /route"
case http.MethodDelete:
match = "DELETE /route"
case http.MethodPost:
match = "POST /route"
}
}))
r.Handle("/route", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
match = "GET /route"
}))
r.Handle("/route", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
match = "DELETE /route"
}))
r.Handle("/route", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
match = "POST /route"
}))
req, err := http.NewRequest(http.MethodGet, "/route", nil)
if err != nil {
t.Errorf("NewRequest: %s", err)
}
r.ServeHTTP(httptest.NewRecorder(), req)
if match != "GET /route" {
t.Errorf("unexpected: %s", match)
}
req, err = http.NewRequest(http.MethodDelete, "/route", nil)
if err != nil {
t.Errorf("NewRequest: %s", err)
}
r.ServeHTTP(httptest.NewRecorder(), req)
if match != "DELETE /route" {
t.Errorf("unexpected: %s", match)
}
req, err = http.NewRequest(http.MethodPost, "/route", nil)
if err != nil {
t.Errorf("NewRequest: %s", err)
}
r.ServeHTTP(httptest.NewRecorder(), req)
if match != "POST /route" {
t.Errorf("unexpected: %s", match)
}
}

View File

@ -1,102 +0,0 @@
package http
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
)
func PathParam(ctx context.Context, Param func(ctx context.Context, paramName string) string, p interface{}, paramName string, required bool) (err error) {
s := Param(ctx, paramName)
if s == "" && required {
switch v := p.(type) {
case *string:
p = v
p = nil
}
return errors.New("missing required parameter")
}
switch v := p.(type) {
case *int64:
*v, err = strconv.ParseInt(s, 10, 64)
case *int32:
var x int64
x, err = strconv.ParseInt(s, 10, 32)
*v = int32(x)
case *string:
*v = s
default:
err = fmt.Errorf("no match for pointer type %T", v)
}
return
}
func BodyParam(body io.ReadCloser, p any, v func(p any) error) (err error) {
d := json.NewDecoder(body)
if err = d.Decode(p); err == nil {
err = v(p)
}
return
}
func mappedParam(m map[string][]string, paramName string, p interface{}, required bool) (err error) {
s, exists := m[paramName]
if !exists && required {
return errors.New("missing required parameter")
}
switch v := p.(type) {
case *int64:
*v, err = strconv.ParseInt(s[0], 10, 64)
case *int32:
var x int64
x, err = strconv.ParseInt(s[0], 10, 32)
*v = int32(x)
case *bool:
*v, err = strconv.ParseBool(s[0])
case *string:
*v = s[0]
case *[]int64:
for _, i := range s {
if e, err := strconv.ParseInt(i, 10, 64); err != nil {
return err
} else {
*v = append(*v, e)
}
}
case *[]int32:
for _, i := range s {
if e, err := strconv.ParseInt(i, 10, 32); err != nil {
return err
} else {
*v = append(*v, int32(e))
}
}
case *[]string:
*v = s
default:
err = fmt.Errorf("no match for pointer type %T", v)
}
return
}
func QueryParam(query url.Values, paramName string, p interface{}, required bool) (err error) {
return mappedParam(query, paramName, p, required)
}
func HeaderParam(h http.Header, paramName string, p interface{}, required bool) (err error) {
return mappedParam(h, paramName, p, required)
}
func FormParam(form url.Values, paramName string, p interface{}, required bool) (err error) {
return mappedParam(form, paramName, p, required)
}

View File

@ -1,287 +0,0 @@
package http
import (
"context"
"errors"
"io"
"net/url"
"reflect"
"strings"
"testing"
)
func TestPathParam(t *testing.T) {
t.Run("test int64 parse", func(t *testing.T) {
var p int64
err := PathParam(context.WithValue(context.Background(), contextKey("int64id"), "123"), Param, &p, "int64id", true)
if (err != nil) != false {
t.Errorf("PathParam() error = %v, wantErr %v", err, false)
return
}
if !reflect.DeepEqual(p, int64(123)) {
t.Errorf("PathParam() = %v, want %v", p, int64(123))
}
})
t.Run("test int32 parse", func(t *testing.T) {
var p int32
err := PathParam(context.WithValue(context.Background(), contextKey("int32id"), "123"), Param, &p, "int32id", true)
if (err != nil) != false {
t.Errorf("PathParam() error = %v, wantErr %v", err, false)
return
}
if !reflect.DeepEqual(p, int32(123)) {
t.Errorf("PathParam() = %v, want %v", p, int32(123))
}
})
t.Run("test string parse", func(t *testing.T) {
var p string
err := PathParam(
context.WithValue(
context.Background(),
contextKey("stringid"),
"foo"),
Param,
&p,
"stringid",
true)
if (err != nil) != false {
t.Errorf("PathParam() error = %v, wantErr %v", err, false)
return
}
if !reflect.DeepEqual(p, "foo") {
t.Errorf("PathParam() = %v, want %v", p, "foo")
}
})
t.Run("test missing required parameter", func(t *testing.T) {
var p string
err := PathParam(context.Background(), Param, &p, "stringid", true)
if (err != nil) != true {
t.Errorf("PathParam() error = %v, wantErr %v", err, true)
return
}
if !reflect.DeepEqual(p, "") {
t.Errorf("PathParam() = %v, want %v", p, "")
}
})
t.Run("test unknown type parameter", func(t *testing.T) {
var p complex64
err := PathParam(context.WithValue(context.Background(), contextKey("stringid"), "foo"),
Param, p, "stringid", true)
if (err != nil) != true {
t.Errorf("PathParam() error = %v, wantErr %v", err, true)
return
}
if !reflect.DeepEqual(p, complex64(0)) {
t.Errorf("PathParam() = %v, want %v", p, complex64(0))
}
})
}
func toValues(s string) url.Values {
v, _ := url.ParseQuery(s)
return v
}
func TestMappedParam(t *testing.T) {
t.Run("test int64 parse", func(t *testing.T) {
var p int64
err := mappedParam(toValues("x=123"), "x", &p, true)
if (err != nil) != false {
t.Errorf("QueryParam() error = %v, wantErr %v", err, false)
return
}
if !reflect.DeepEqual(p, int64(123)) {
t.Errorf("QueryParam() = %v, want %v", p, int64(123))
}
})
t.Run("test int32 parse", func(t *testing.T) {
var p int32
err := mappedParam(toValues("x=123"), "x", &p, true)
if (err != nil) != false {
t.Errorf("QueryParam() error = %v, wantErr %v", err, false)
return
}
if !reflect.DeepEqual(p, int32(123)) {
t.Errorf("QueryParam() = %v, want %v", p, int32(123))
}
})
t.Run("test bool parse", func(t *testing.T) {
var p bool
err := mappedParam(toValues("x=true"), "x", &p, true)
if (err != nil) != false {
t.Errorf("QueryParam() error = %v, wantErr %v", err, false)
return
}
if !reflect.DeepEqual(p, true) {
t.Errorf("QueryParam() = %v, want %v", p, true)
}
})
t.Run("test string parse", func(t *testing.T) {
var p string
err := mappedParam(toValues("x=foobar"), "x", &p, true)
if (err != nil) != false {
t.Errorf("QueryParam() error = %v, wantErr %v", err, false)
return
}
if !reflect.DeepEqual(p, "foobar") {
t.Errorf("QueryParam() = %v, want %v", p, "foobar")
}
})
t.Run("test []int64 parse", func(t *testing.T) {
var p []int64
err := mappedParam(toValues("x=123&x=456"), "x", &p, true)
if (err != nil) != false {
t.Errorf("QueryParam() error = %v, wantErr %v", err, false)
return
}
if !reflect.DeepEqual(p, []int64{int64(123), int64(456)}) {
t.Errorf("QueryParam() = %v, want %v", p, []int64{int64(123), int64(456)})
}
})
t.Run("test []int64 bad parse", func(t *testing.T) {
var p []int64
err := mappedParam(toValues("x=123&x=4q56"), "x", &p, true)
if (err != nil) != true {
t.Errorf("QueryParam() error = %v, wantErr %v", err, true)
return
}
if !reflect.DeepEqual(p, []int64{123}) {
t.Errorf("QueryParam() = %v, want %v", p, []int64{})
}
})
t.Run("test []int32 parse", func(t *testing.T) {
var p []int32
err := mappedParam(toValues("x=123&x=456"), "x", &p, true)
if (err != nil) != false {
t.Errorf("QueryParam() error = %v, wantErr %v", err, false)
return
}
if !reflect.DeepEqual(p, []int32{int32(123), int32(456)}) {
t.Errorf("QueryParam() = %v, want %v", p, []int32{int32(123), int32(456)})
}
})
t.Run("test []int32 bad parse", func(t *testing.T) {
var p []int32
err := mappedParam(toValues("x=123&x=4q56"), "x", &p, true)
if (err != nil) != true {
t.Errorf("QueryParam() error = %v, wantErr %v", err, true)
return
}
if !reflect.DeepEqual(p, []int32{123}) {
t.Errorf("QueryParam() = %v, want %v", p, []int32{123})
}
})
t.Run("test []string parse", func(t *testing.T) {
var p []string
err := mappedParam(toValues("x=foo&x=bar"), "x", &p, true)
if (err != nil) != false {
t.Errorf("QueryParam() error = %v, wantErr %v", err, false)
return
}
if !reflect.DeepEqual(p, []string{"foo", "bar"}) {
t.Errorf("QueryParam() = %v, want %v", p, []string{"foo", "bar"})
}
})
t.Run("test missing required parameter", func(t *testing.T) {
var p string
err := mappedParam(toValues("y=hello"), "x", &p, true)
if (err != nil) != true {
t.Errorf("QueryParam() error = %v, wantErr %v", err, true)
return
}
if !reflect.DeepEqual(p, "") {
t.Errorf("QueryParam() = %v, want %v", p, "")
}
})
t.Run("test unknown type parameter", func(t *testing.T) {
var p complex64
err := mappedParam(toValues("x=hello"), "x", &p, true)
if (err != nil) != true {
t.Errorf("QueryParam() error = %v, wantErr %v", err, true)
return
}
if !reflect.DeepEqual(p, complex64(0)) {
t.Errorf("QueryParam() = %v, want %v", p, complex64(0))
}
})
}
func TestBodyParam(t *testing.T) {
type args struct {
body io.ReadCloser
p any
v func(p any) error
}
type x struct{}
var y x
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "test happy path",
args: args{
body: io.NopCloser(strings.NewReader("{}")),
p: &y,
v: func(p any) error {
return nil
},
},
wantErr: false,
},
{
name: "test bad json",
args: args{
body: io.NopCloser(strings.NewReader("}")),
p: &y,
v: func(p any) error {
return nil
},
},
wantErr: true,
},
{
name: "test validation failed",
args: args{
body: io.NopCloser(strings.NewReader("{}")),
p: &y,
v: func(p any) error {
return errors.New("validation failed")
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := BodyParam(tt.args.body, tt.args.p, tt.args.v); (err != nil) != tt.wantErr {
t.Errorf("BodyParam() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func Ptr[T any](v T) *T {
return &v
}
func NilString() *string {
return nil
}