Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 101 additions & 51 deletions params/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,63 +14,68 @@ import (
var ErrNotFound = errors.New("not found")

func GetString(r *http.Request, name string, required bool) (string, bool, error) {
param := r.PathValue(name)

if param == "" {
param = r.URL.Query().Get(name)
}

// fallback to a header lookup
if param == "" {
param = r.Header.Get(name)
values, ok, err := GetStringArray(r, name, required)
if err != nil {
return "", false, err
}

if required && len(param) == 0 {
return "", false, fmt.Errorf("%s: %w", name, ErrNotFound)
if !ok || len(values) == 0 {
return "", false, nil
}

return param, param != "", nil
return values[0], true, nil
}

func GetStringPath(r *http.Request, name string, required bool) (string, bool, error) {
param := r.PathValue(name)
if required && len(param) == 0 {
return "", false, fmt.Errorf("%s: %w", name, ErrNotFound)
values, ok, err := GetStringArrayPath(r, name, required)
if err != nil {
return "", false, err
}

if !ok || len(values) == 0 {
return "", false, nil
}

return param, param != "", nil
return values[0], true, nil
}

func GetStringQuery(r *http.Request, name string, required bool) (string, bool, error) {
param := r.URL.Query().Get(name)
if required && len(param) == 0 {
return "", false, fmt.Errorf("%s: %w", name, ErrNotFound)
values, ok, err := GetStringArrayQuery(r, name, required)
if err != nil {
return "", false, err
}

if !ok || len(values) == 0 {
return "", false, nil
}

return param, param != "", nil
return values[0], true, nil
}

func GetStringHeader(r *http.Request, name string, required bool) (string, bool, error) {
param := r.Header.Get(name)
if required && len(param) == 0 {
return "", false, fmt.Errorf("%s: %w", name, ErrNotFound)
values, ok, err := GetStringArrayHeader(r, name, required)
if err != nil {
return "", false, err
}

if !ok || len(values) == 0 {
return "", false, nil
}

return param, param != "", nil
return values[0], true, nil
}

func GetStringCookie(r *http.Request, name string, required bool) (string, bool, error) {
cookie, err := r.Cookie(name)
if err == nil && cookie != nil {
// Return found even if cookie is empty, because it _is_ present!
return cookie.Value, true, nil
values, ok, err := GetStringArrayCookie(r, name, required)
if err != nil {
return "", false, err
}

if required {
return "", false, fmt.Errorf("%s: %w", name, ErrNotFound)
if !ok || len(values) == 0 {
return "", false, nil
}

return "", false, nil
return values[0], true, nil
}

func GetInt32(r *http.Request, name string, required bool) (int32, bool, error) {
Expand Down Expand Up @@ -397,49 +402,94 @@ func convertUUID(s string) (uuid.UUID, bool, error) {
return id, true, nil
}

func splitAndAppend(dest []string, source string) []string {
for v := range strings.SplitSeq(source, ",") {
if v != "" {
dest = append(dest, v)
}
}

return dest
}

func GetStringArray(r *http.Request, name string, required bool) ([]string, bool, error) {
s, ok, err := GetString(r, name, required)
if err != nil || len(s) == 0 || !ok {
return nil, false, err
out, _, _ := GetStringArrayPath(r, name, false)

if len(out) == 0 {
out, _, _ = GetStringArrayQuery(r, name, false)
}

// fallback to a header lookup
if len(out) == 0 {
out, _, _ = GetStringArrayHeader(r, name, false)
}

return strings.Split(s, ","), true, nil
if len(out) == 0 {
out, _, _ = GetStringArrayCookie(r, name, false)
}

if required && len(out) == 0 {
return nil, false, fmt.Errorf("%s: %w", name, ErrNotFound)
}

return out, len(out) > 0, nil
}

func GetStringArrayPath(r *http.Request, name string, required bool) ([]string, bool, error) {
s, ok, err := GetStringPath(r, name, required)
if err != nil || len(s) == 0 || !ok {
return nil, false, err
out := splitAndAppend(nil, r.PathValue(name))

if required && len(out) == 0 {
return nil, false, fmt.Errorf("%s: %w", name, ErrNotFound)
}

return strings.Split(s, ","), true, nil
return out, len(out) > 0, nil
}

func GetStringArrayQuery(r *http.Request, name string, required bool) ([]string, bool, error) {
s, ok, err := GetStringQuery(r, name, required)
if err != nil || len(s) == 0 || !ok {
return nil, false, err
var out []string

values := r.URL.Query()[name]
for _, v := range values {
out = splitAndAppend(out, v)
}

if required && len(out) == 0 {
return nil, false, fmt.Errorf("%s: %w", name, ErrNotFound)
}

return strings.Split(s, ","), true, nil
return out, len(out) > 0, nil
}

func GetStringArrayHeader(r *http.Request, name string, required bool) ([]string, bool, error) {
s, ok, err := GetStringHeader(r, name, required)
if err != nil || len(s) == 0 || !ok {
return nil, false, err
var out []string

values := r.Header.Values(name)
for _, v := range values {
out = splitAndAppend(out, v)
}

return strings.Split(s, ","), true, nil
if required && len(out) == 0 {
return nil, false, fmt.Errorf("%s: %w", name, ErrNotFound)
}

return out, len(out) > 0, nil
}

func GetStringArrayCookie(r *http.Request, name string, required bool) ([]string, bool, error) {
s, ok, err := GetStringCookie(r, name, required)
if err != nil || len(s) == 0 || !ok {
return nil, false, err
var out []string

cookies := r.CookiesNamed(name)
for _, c := range cookies {
if c != nil {
out = splitAndAppend(out, c.Value)
}
}

if required && len(out) == 0 {
return nil, false, fmt.Errorf("%s: %w", name, ErrNotFound)
}

return strings.Split(s, ","), true, nil
return out, len(out) > 0, nil
}

func GetInt32Array(r *http.Request, name string, required bool) ([]int32, bool, error) {
Expand Down
1 change: 1 addition & 0 deletions params/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ func TestGetUUIDArray(t *testing.T) {
{"not required missing", httptest.NewRequest("GET", "/BAR?", nil), "foo", false, nil, false, false},
{"bad format", httptest.NewRequest("GET", "/BAR?foo=a123", nil), "foo", true, nil, true, false},
{"large", httptest.NewRequest("GET", fmt.Sprintf("/BAR?foo=%s,%s,%s", id1.String(), id2.String(), id3.String()), nil), "foo", true, []uuid.UUID{id1, id2, id3}, false, true},
{"large repeated", httptest.NewRequest("GET", fmt.Sprintf("/BAR?foo=%s&foo=%s,,,%s", id1.String(), id2.String(), id3.String()), nil), "foo", true, []uuid.UUID{id1, id2, id3}, false, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
Loading