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
27 changes: 18 additions & 9 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,20 +126,19 @@ type Context interface {
BodyWriter() io.Writer
}

// Represent http protocol version
// ProtoVersion represents the http protocol version.
type ProtoVersion struct {
Proto string
ProtoMajor int
ProtoMinor int
}

type (
humaContext Context
subContext struct {
humaContext
override context.Context
}
)
type humaContext Context

type subContext struct {
humaContext
override context.Context
}

func (c subContext) Context() context.Context {
return c.override
Expand Down Expand Up @@ -183,7 +182,7 @@ type Config struct {
// `/openapi.yaml`, for example.
OpenAPIPath string

// DocsPath is the path to the API documentation. If set to `/docs` it will
// DocsPath is the path to the API documentation. If set to `/docs `, it will
// allow clients to get `/docs` to view the documentation in a browser. If
// you wish to provide your own documentation renderer, you can leave this
// blank and attach it directly to the router or adapter.
Expand Down Expand Up @@ -295,15 +294,18 @@ func (a *api) Unmarshal(contentType string, data []byte, v any) error {
if end == -1 {
end = len(contentType)
}

ct := contentType[start:end]
if ct == "" {
// Default to assume JSON since this is an API.
ct = "application/json"
}

f, ok := a.formats[ct]
if !ok {
return fmt.Errorf("%w: %s", ErrUnknownContentType, contentType)
}

return f.Unmarshal(data, v)
}

Expand All @@ -325,12 +327,14 @@ func (a *api) Negotiate(accept string) (string, error) {

func (a *api) Transform(ctx Context, status string, v any) (any, error) {
var err error

for _, t := range a.transformers {
v, err = t(ctx, status, v)
if err != nil {
return nil, err
}
}

return v, nil
}

Expand Down Expand Up @@ -412,9 +416,11 @@ func NewAPI(config Config, a Adapter) API {
config.DefaultFormat = "application/json"
}
}

if config.DefaultFormat != "" {
newAPI.formatKeys = append(newAPI.formatKeys, config.DefaultFormat)
}

for k, v := range config.Formats {
newAPI.formats[k] = v
newAPI.formatKeys = append(newAPI.formatKeys, k)
Expand All @@ -432,6 +438,7 @@ func NewAPI(config Config, a Adapter) API {
}
ctx.BodyWriter().Write(specJSON)
})

var specJSON30 []byte
a.Handle(&Operation{
Method: http.MethodGet,
Expand All @@ -443,6 +450,7 @@ func NewAPI(config Config, a Adapter) API {
}
ctx.BodyWriter().Write(specJSON30)
})

var specYAML []byte
a.Handle(&Operation{
Method: http.MethodGet,
Expand All @@ -454,6 +462,7 @@ func NewAPI(config Config, a Adapter) API {
}
ctx.BodyWriter().Write(specYAML)
})

var specYAML30 []byte
a.Handle(&Operation{
Method: http.MethodGet,
Expand Down
2 changes: 1 addition & 1 deletion autoconfig.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package huma

// AutoConfigVar represents a variable given by the user when prompted during
// auto-configuration setup of an API.
// autoconfiguration setup of an API.
type AutoConfigVar struct {
Description string `json:"description,omitempty"`
Example string `json:"example,omitempty"`
Expand Down
8 changes: 4 additions & 4 deletions casing/casing.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ func Split(value string) []string {
return results
}

// Join will combine split parts back together with the given separator and
// optional transform functions.
// Join will combine split parts with the given separator and optional
// transform functions.
func Join(parts []string, sep string, transform ...TransformFunc) string {
for i := 0; i < len(parts); i++ {
for _, t := range transform {
Expand Down Expand Up @@ -257,7 +257,7 @@ func MergeNumbers(parts []string, suffixes ...string) []string {

if !prevNum {
if i == 0 {
// First item must always append.
// The first item must always append.
results = append(results, part)
} else {
// Concatenate the number to the previous non-number piece.
Expand All @@ -269,7 +269,7 @@ func MergeNumbers(parts []string, suffixes ...string) []string {

prevNum = true
} else {
// Special case: first part is a number, second part is not.
// Special case: the first part is a number, the second part is not.
if i == 1 && prevNum {
results[0] += part
prevNum = false
Expand Down
4 changes: 2 additions & 2 deletions cookie.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
// ReadCookie reads a single cookie from the request headers by name. If
// multiple cookies with the same name exist, the first is returned.
func ReadCookie(ctx Context, name string) (*http.Cookie, error) {
headers := []string{}
var headers []string
ctx.EachHeader(func(name, value string) {
if strings.EqualFold(name, "cookie") {
headers = append(headers, value)
Expand All @@ -24,7 +24,7 @@ func ReadCookie(ctx Context, name string) (*http.Cookie, error) {

// ReadCookies reads all cookies from the request headers.
func ReadCookies(ctx Context) []*http.Cookie {
headers := []string{}
var headers []string
ctx.EachHeader(func(name, value string) {
if strings.EqualFold(name, "cookie") {
headers = append(headers, value)
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/features/test-utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestMyAPI(t *testing.T) {
}
```

The request convenience methods take a URL path followed by any number of optional arguments. If the argument is a string, it is treated as a header, if it is an `io.Reader` is is treated as the raw body, otherwise it is marshalled as JSON and used as the request body.
The request convenience methods take a URL path followed by any number of optional arguments. If the argument is a string, it is treated as a header, if it is an `io.Reader` is is treated as the raw body, otherwise it is marshaled as JSON and used as the request body.

## Assertions

Expand Down
11 changes: 6 additions & 5 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,15 +259,16 @@ func WriteErr(api API, ctx Context, status int, msg string, errs ...error) error
var err = NewErrorWithContext(ctx, status, msg, errs...)

// NewError may have modified the status code, so update it here if needed.
// If it was not modified then this is a no-op.
// If it was not modified, then this is a no-op.
status = err.GetStatus()

writeErr := writeResponse(api, ctx, status, "", err)
if writeErr != nil {
writtenErr := writeResponse(api, ctx, status, "", err)
if writtenErr != nil {
// If we can't write the error, log it so we know what happened.
fmt.Fprintf(os.Stderr, "could not write error: %v\n", writeErr)
fmt.Fprintf(os.Stderr, "could not write error: %v\n", writtenErr)
}
return writeErr

return writtenErr
}

// Status304NotModified returns a 304. This is not really an error, but
Expand Down
9 changes: 4 additions & 5 deletions formdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (v MimeTypeValidator) Validate(fh *multipart.FileHeader, location string) (
mimeType := fh.Header.Get("Content-Type")
if mimeType == "" {
var buffer = make([]byte, 1000)
if _, err := file.Read(buffer); err != nil {
if _, err = file.Read(buffer); err != nil {
return "", &ErrorDetail{Message: "Failed to infer file media type", Location: location}
}
file.Seek(int64(0), io.SeekStart)
Expand Down Expand Up @@ -89,7 +89,7 @@ func (v MimeTypeValidator) Validate(fh *multipart.FileHeader, location string) (
}
}

// Decodes multipart.Form data into *T, returning []*ErrorDetail if any
// Decode decodes multipart.Form data into *T, returning []*ErrorDetail if any
// Schema is used to check for validation constraints
func (m *MultipartFormFiles[T]) Decode(opMediaType *MediaType, formValueParser func(val reflect.Value)) []error {
var (
Expand Down Expand Up @@ -131,9 +131,8 @@ func readSingleFile(fileHeaders []*multipart.FileHeader, key string, opMediaType
if len(fileHeaders) == 0 {
if opMediaType.Schema.requiredMap[key] {
return FormFile{}, &ErrorDetail{Message: "File required", Location: key}
} else {
return FormFile{}, nil
}
return FormFile{}, nil
} else if len(fileHeaders) == 1 {
validator := NewMimeTypeValidator(opMediaType.Encoding[key])
return readFile(fileHeaders[0], key, validator)
Expand Down Expand Up @@ -221,7 +220,7 @@ func multiPartFormFileSchema(r Registry, t reflect.Type) *Schema {
default:
schema.Properties[name] = SchemaFromField(r, f, name)

// Should we panic if [T] struct defines fields with unsupported types ?
// Should we panic if [T] struct defines fields with unsupported types?
}

if _, ok := f.Tag.Lookup("required"); ok && boolTag(f, "required", false) {
Expand Down
4 changes: 2 additions & 2 deletions group.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (g *Group) DocumentOperation(op *Operation) {
// on all operations in the group. Use this to modify the operation before it is
// registered with the router or OpenAPI document. This behaves similar to
// middleware in that you should invoke `next` to continue the chain. Skip it
// to prevent the operation from being registered, and call multiple times for
// to prevent the operation from being registered and call multiple times for
// a fan-out effect.
func (g *Group) UseModifier(modifier func(o *Operation, next func(*Operation))) {
g.modifiers = append(g.modifiers, modifier)
Expand Down Expand Up @@ -166,7 +166,7 @@ func (g *Group) ModifyOperation(op *Operation, next func(*Operation)) {

// UseMiddleware adds one or more middleware functions to the group that will be
// run on all operations in the group. Use this to add common functionality to
// all operations in the group, e.g. authentication/authorization.
// all operations in the group, e.g., authentication/authorization.
func (g *Group) UseMiddleware(middlewares ...func(ctx Context, next func(Context))) {
g.middlewares = append(g.middlewares, middlewares...)
}
Expand Down
2 changes: 1 addition & 1 deletion huma_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (UUID) Schema(r huma.Registry) *huma.Schema {
return &huma.Schema{Type: huma.TypeString, Format: "uuid"}
}

// BodyContainer is an embed request body struct to test request body unmarshalling
// BodyContainer is an embed request body struct to test request body unmarshaling
type BodyContainer struct {
Body struct {
Name string `json:"name"`
Expand Down
Loading
Loading