Skip to content

Update module github.com/danielgtaylor/huma to v2 #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from

Conversation

renovate[bot]
Copy link
Contributor

@renovate renovate bot commented Nov 8, 2024

This PR contains the following updates:

Package Change Age Adoption Passing Confidence
github.com/danielgtaylor/huma v1.14.3 -> v2.32.0 age adoption passing confidence

Release Notes

danielgtaylor/huma (github.com/danielgtaylor/huma)

v2.32.0

Compare Source

Overview

HTTP HEAD Convenience Function

A convenience function was added for HTTP HEAD requests.

huma.Head(api, "/path", handler)
Stop HTML-Escaping JSON

HTTP API usage would rarely need to HTML-escape responses, so this default JSON marshaling behavior has been turned off. If you would like to keep the behavior, you can do so by modifying the huma.Config.Formats map. For example, error messages are now more readable:

  • Before: expected number \u003e= 10
  • After: expected number >= 10
Better Integer Validation

A new validation check has been added to present a better error message to the user when an integer is required but a floating point value like 1.5 is passed in. This now results in an expected integer message instead of a JSON unmarshal error.

Groups + Convenience Function Improvements

Groups and convenience functions like huma.Get now play better together. Groups will regenerate the operation ID and operation summary iff those values were auto-generated and have not been modified. This works for groups of groups as well. The following are equivalent:

huma.Get(api, "/v1/users/", handler)

v1 := huma.NewGroup(api, "/v1")
users := huma.NewGroup(v1, "/users")
huma.Get(users, "/", handler)

fmt.Println(api.OpenAPI().Paths["/v1/users/"].Summary)
// Output: Get v1 users

If you prefer full control over the operation ID and summary, use huma.Register instead. You can still use group operation modifiers and convenience modifiers which modify the operation ID and/or summary and, if modified, they will not get regenerated. You can also disable generation by changing or unsetting the operation's _convenience_id and _convenience_summary metadata fields which are added by convenience functions like huma.Get/huma.Put/etc.

What's Changed

New Contributors

Full Changelog: danielgtaylor/huma@v2.31.0...v2.32.0

v2.31.0

Compare Source

Overview
Go 1.24 omitzero Support!

Huma now supports Go's new JSON omitzero feature out of the box, treating it similar to the existing omitempty in terms of making fields optional. The updated rules for optional fields now look like this:

  1. Start with all fields required.
  2. If a field has omitempty, it is optional.
  3. If a field has omitzero, it is optional.
  4. If a field has required:"false", it is optional.
  5. If a field has required:"true", it is required.

See https://huma.rocks/features/request-validation/#optional-required for more info.

What's Changed
New Contributors

Full Changelog: danielgtaylor/huma@v2.30.0...v2.31.0

v2.30.0

Compare Source

Overview

Sponsors

A big thank you to our new sponsor:

Groups

Huma now supports groups, which port over much of the functionality from @​cardinalby's excellent https://github.com/cardinalby/hureg library (thank you for that work!). This enables creating groups of operations with the same path prefixes, middleware, operation modifiers, and transformers. Typical usage might look like this:

grp := huma.NewGroup(api, "/v1")
grp.UseMiddleware(authMiddleware)

// Register a `GET /v1/users` route that requires auth.
huma.Get(grp, "/users", func(ctx context.Context, input *struct{}) (*UsersResponse, error) {
	// ...
})

See https://huma.rocks/features/groups/ for more details.

Context Unwrapping

Due to many user requests, it is now possible to "unwrap" a router-specific context into its constituent router-specific representation. Each adapter package now has an Unwrap(huma.Context) T function that will return either a request/response pair or that router's own context type, allowing you to effectively escape Huma in router-agnostic middleware & resolvers.

[!CAUTION]
You must use the same adapter package to create the API and call Unwrap or Huma will panic!

Example usage:

router := http.NewServeMux()
api := humago.New(router, huma.DefaultConfig("My API", "1.0.0"))

api.UseMiddleware(func(ctx huma.Context, next func(huma.Context)) {
	r, w := humago.Unwrap(ctx)

	// Do something with the request/response.
	// ...

	next(ctx)
})

While generally not recommended, this can help you to use router-specific middleware as you migrate large existing projects to Huma, or just escape Huma's abstractions when they no longer make sense for your use-case. Sometimes the best library is the one that gets out of the way.

See https://huma.rocks/features/middleware/#unwrapping for more details.

What's Changed

New Contributors

Full Changelog: danielgtaylor/huma@v2.29.0...v2.30.0

v2.29.0

Compare Source

Overview

Support More Multipart Form Values

This enabled the use of fields with arbitrary types in the form which will get parsed & validated for you:

huma.Register(api, huma.Operation{
	OperationID: "upload-and-decode-files"
	Method:      http.MethodPost,
	Path:        "/upload",
}, func(ctx context.Context, input *struct {
	RawBody huma.MultipartFormFiles[struct {
		MyFile                    huma.FormFile   `form:"file" contentType:"text/plain" required:"true"`
		SomeOtherFiles            []huma.FormFile `form:"other-files" contentType:"text/plain" required:"true"`
		NoTagBindingFile          huma.FormFile   `contentType:"text/plain"`
		MyGreeting                string          `form:"greeting", minLength:"6"`
		SomeNumbers               []int           `form:"numbers"`
		NonTaggedValuesAreIgnored string  // ignored
	}]
}) (*struct{}, error) {
	// ...
})
Better Auto-patch Support with Sub-routers

The auto-patch functionality now tries to find a common path prefix so it's possible to do stuff like this and have the generated PATCH operation function correctly:

func main() {
    router := chi.NewRouter()
    router.Route("/api", apiMux())
    err = http.ListenAndServe(fmt.Sprintf(":%d", 8080), router)
}

// apiMux returns a function that initializes the API routes
func apiMux() func(chi.Router) {
        return func(router chi.Router) {
            humaConfig := huma.DefaultConfig("API", "dev")
            humaConfig = openapi.WithAuthSchemes(humaConfig)
            humaConfig = openapi.WithOverviewDoc(humaConfig)
            humaConfig = openapi.WithServers(humaConfig, config)
            api := humachi.New(router, humaConfig)
            huma.Register(api, huma.Operation{
                Method:      "GET",
                Path:        "/ressources/{id}",
            }, getRessourceByID)
            huma.Register(api, huma.Operation{
                Method:      "PUT",
                Path:        "/ressources/{id}",
            }, updateRessourceByID)
            autopatch.AutoPatch(api)
    }
}
Custom Param Type Enhancements

Two new interfaces enable some additional advanced customization enhancements when creating operation input parameters:

type ParamWrapper interface {
	Receiver() reflect.Value
}

type ParamReactor interface {
	OnParamSet(isSet bool, parsed any)
}

These can be used like so:

type OptionalParam[T any] struct {
	Value T
	IsSet bool
}

// Define schema to use wrapped type
func (o OptionalParam[T]) Schema(r huma.Registry) *huma.Schema {
	return huma.SchemaFromType(r, reflect.TypeOf(o.Value))
}

// Expose wrapped value to receive parsed value from Huma
// MUST have pointer receiver
func (o *OptionalParam[T]) Receiver() reflect.Value {
	return reflect.ValueOf(o).Elem().Field(0)
}

// React to request param being parsed to update internal state
// MUST have pointer receiver
func (o *OptionalParam[T]) OnParamSet(isSet bool, parsed any) {
	o.IsSet = isSet
}
Fix Panic from External Schema

It's possible to use the default schema transformers now with custom external schemas without causing a panic. For example:

Responses: map[string]*huma.Response{
    "200": {
        Content: map[string]*huma.MediaType{
            "application/json": {
                Schema: &huma.Schema{
                    Ref: "https://json-schema.org/draft/2020-12/schema",
                },
            },
        },
    },
},

Note: external schemas are not validated and are just there for informational purposes and to help with client generation.

Fiber Fixes

A major rework of the humafiber adapter was done in #​725. This ensures tests are run with the -race detector and fixes a race that was present in the Fiber adapter. It should be much more stable now.

Deep Object Support for Params

Params now support the OpenAPI deepObject style, enabling e.g. query params to send structured input in that style.

// GreetingOutput represents the greeting operation response.
type GreetingOutput struct {
	Body struct {
		Person Person            `json:"person"`
		Map    map[string]string `json:"map"`
	}
}

type Person struct {
	Name     string `json:"name"`
	Age      int    `json:"age,omitempty" default:"20"`
	Birthday string `json:"birthday,omitempty"`
}

func main() {
	// Create a new router & API
	router := chi.NewMux()
	api := humachi.New(router, huma.DefaultConfig("My API", "1.0.0"))

	// Register GET /greeting
	huma.Get(api, "/greeting", func(ctx context.Context, input *struct {
		Person Person            `query:"person,deepObject"`
		Map    map[string]string `query:"map,deepObject"`
	}) (*GreetingOutput, error) {
		out := &GreetingOutput{}
		out.Body.Person = input.Person
		out.Body.Map = input.Map
		return out, nil
	})

	// Start the server!
	log.Println("http://127.0.0.1:8888/docs")
	http.ListenAndServe("127.0.0.1:8888", router)
}

Example request:

curl --request GET \
  --url 'http://127.0.0.1:8888/greeting?person%5Bname%5D=foo&map%5Ba%5D=foo&map%5Bb%5D=foo2' \
  --header 'Accept: application/json, application/problem+json'
Other fixes
  • Prevent double validation errors
  • Stop Huma from overwriting a custom request body schema when a Body field is present.

What's Changed

New Contributors

Full Changelog: danielgtaylor/huma@v2.28.0...v2.29.0

v2.28.0

Compare Source

Overview

Upgraded Documentation

Stoplight Elements has been upgraded to the latest version.

Others
  • Fixed two important memory access issues (body race & Fiber context).
  • Additional docs & examples added ❤️
  • Major refactor of some of the codebase to make future changes easier.

What's Changed

New Contributors

Full Changelog: danielgtaylor/huma@v2.27.0...v2.28.0

v2.27.0

Compare Source

Sponsors

A big thank you to our new sponsors:

Consider sponsoring the project!

Overview

Write Errors & Warnings to Stderr

When writing custom commands that output to stdout, sometimes an error or warning can be generated by Huma, which could add unwanted output to stdout. This has been updated to use stderr so it is easier to differentiate. This is useful for an openapi command that dumps the OpenAPI document to stdout as it is now safe to redirect it to a file even if warnings are generated.

Better Handling of Embedded Header Fields

When headers are embedded in the output struct they are now properly referenced in the documentation and the parent embedded struct itself is ignored (previously it would include the headers twice). For example, this now works as expected:

// PaginationOutput contains reusable response headers
// for implementing pagination
type PaginationOutput struct {
	Link   string `header:"Link" doc:"HTTP links for e.g. pagination"`
	Cursor string `header:"Cursor" doc:"Identifier that can be used to paginate. If the value is empty then there are no more pages to iterate over."`
}

// list_slates.go
type listSlateResponse struct {
	pagination.PaginationOutput
	Body []listSlateBody
}
Fiber UserContext Support

When using the Fiber adapter and getting items from the context it now first checks Fiber's UserContext before checking the underlying context for the value. This makes Huma easier to use with Fiber and Fiber-specific middleware. No change in behavior is needed, things should Just Work™️.

Remove Chi From Tests

The code has been refactored to remove reliance on Chi for the tests, simplifying the project overall and relying more on the standard library.

Fix Operation Callbacks

Operation callbacks mistakenly used the wrong type of map[string]*PathItem when it should really have been map[string]map[string]*PathItem instead. The structure should look something like this, which is now supported properly to document asynchronous callbacks that your operation supports:

paths:
  /create:
    post:
      callbacks:
        myEvent:
          "{$request.body#/callbackUrl}":
            post:
              requestBody: # Contents of the callback message
                
Better Support for Embedded RawBody Field

It's now possible to embed the RawBody field and have things work. For example:

type RequestHeader struct {
	Test string `header:"Test"`
}

type EmbedGreeting struct {
	RawBody multipart.Form
}

type AnotherGreetingInput struct {
	RequestHeader
	EmbedGreeting
}
ContentTypeFilter Now Updates OpenAPI

If an operation output implements ContentTypeFilter, then this will be called with the default value application/json and the result used to build the OpenAPI document. For example this will result in application/ld+json in the OpenAPI rather than application/json:

type Response struct {
	Message string `json:"message" doc:"The message."`
}

func (r *Response) ContentType(t string) string {
	return "application/ld+json"
}

type Output struct {
	Body Response
}
Other Fixes
  • Various doc fixes / improvements
  • New linters enabled for better code quality

What's Changed

New Contributors

Full Changelog: danielgtaylor/huma@v2.26.0...v2.27.0

v2.26.0

Compare Source

Overview

Better Marking of Visited Types

When looking for params, headers, defaults, and resolvers the Huma type traversal code now tracks previously visited types more narrowly, continuing to detect recursive loops while allowing multiple adjacent fields to use the same type. Before this fix it would ignore some fields. For example, this now works propertly to run the resolver on both HomeAddress and AwayAddress:

type Address struct {
	Line1       string `json:"line1" required:"true" minLength:"1" maxLength:"50"`
	Line2       string `json:"line2,omitempty" required:"false" minLength:"0" maxLength:"50" default:""`
	City        string `json:"city" required:"true" minLength:"1" maxLength:"64"`
	State       string `json:"state" required:"true" minLength:"1" maxLength:"32"`
	Zip         string `json:"zip" required:"true" minLength:"1" maxLength:"16"`
	CountryCode string `json:"countryCode" required:"false" minLength:"1" maxLength:"2" default:"US"`
}

func (a Address) Resolve(_ huma.Context, prefix *huma.PathBuffer) []error {
	/* ... do stuff ... */
}

type TestRequestBody struct {
	Name        string  `json:"name"`
	Age         int     `json:"age"`
	HomeAddress Address `json:"home" required:"true"`
	AwayAddress Address `json:"away" required:"true"`
}
More Resilient Fast Q Value Selection

Several minor bugs have been fixed in the fast zero-allocation q value parsing for client-based content negotiation via the Accept header. Values like a;, no longer cause a panic. Several new tests were added to ensure robustness.

No Longer Panic From Client Disconnect

When a client disconnects and a write to the socket results in an error, we now check if the context is context.Canceled and ignore it. This should not result in a panic as that has a negative impact on metrics and perceived service health. An attempt is made to tell the huma.Context that the response status code should be 499 Client Disconnected to help with metrics/logging middleware.

Others
  • Fixed doc bug
  • Minor fix when printing response bodies in tests if JSON indenting fails
  • Refactored code for sending responses to be more consistent & re-used between the handlers and WriteErr and ensure NewErrorWithContext is used everywhere. This should be a no-op for library users.

What's Changed

New Contributors

Full Changelog: danielgtaylor/huma@v2.25.0...v2.26.0

v2.25.0

Compare Source

Overview

Case-insensitive JSON

Since the standard library Go unmarshaler supports case-insensitive field matches, Huma has been updated to support this during validation as well to better support integration with legacy systems & clients. This behavior can be disabled by explicitly setting huma.ValidateStrictCasing = true (the default matches the standard library behavior). For example, given:

huma.Put(api, "/demo", func(ctx context.Context, input *struct{
	Body struct {
		Value string `json:"value"`
	}
}) (*struct{}, error) {
	fmt.Println("Value is", input.Body.Value)
	return nil, nil
})

If a client were to send {"Value": "test"} instead of {"value": "test"} it will now pass validation and work. This also works for the built-in CBOR format as well.

Support Scalar Pointers with Defaults

Defaults have become more useful by enabling the use of pointers for basic types to have default values attached. For example, given this input to an operation:

type MyInput struct {
	Body struct {
		Enabled *bool `json:"enabled,omitempty" default:"true"`
	}
}

It's now possible to explicitly send false without the value being overridden by the default. The behavior seen by the handler code is this:

Client sends Handler sees
true true
false false
null / undefined true

Since this is using the built-in mechanism to determine if a value was sent, there is no additional performance penalty for setting the default values in Huma.

What's Changed

New Contributors

Full Changelog: danielgtaylor/huma@v2.24.0...v2.25.0

v2.24.0

Compare Source

Overview

Better Support of String Subtype Slice Params

It's now possible to use types based on string like you commonly see for enumerations as slice inputs to Huma operations.

type MyEnum string

const (
	Value1 MyEnum = "value1"
	Value2 MyEnum = "value2"
	// ...
)

huma.Get(api, "/example", func(ctx context.Context, input *struct{
	Example []MyEnum `query:"example" enum:"value1,value2"`
}) (*struct{}, error) {
	// ...
}
Better Support of non-Object Refs

Fixes a bug that prevented deliberate refs of nullable non-objects from being used due to a panic. It's now possible to do something like this to automatically add enum values from a type when generating the schema and use a $ref in the JSON Schema:

type InstitutionKind string

const (
	Lab                InstitutionKind = "Lab"
	FoundingAgency     InstitutionKind = "FundingAgency"
	SequencingPlatform InstitutionKind = "SequencingPlatform"
	Other              InstitutionKind = "Other"
)

var InstitutionKindValues = []InstitutionKind{
	Lab,
	FoundingAgency,
	SequencingPlatform,
	Other,
}

// Register enum in OpenAPI specification
func (u InstitutionKind) Schema(r huma.Registry) *huma.Schema {
  if r.Map()["InstitutionKind"] == nil {
    schemaRef := r.Schema(reflect.TypeOf(""), false, "InstitutionKind")
    schemaRef.Title = "InstitutionKind"
    for _, v := range InstitutionKindValues {
      schemaRef.Enum = append(schemaRef.Enum, string(v))
    }
    r.Map()["InstitutionKind"] = schemaRef
  }

	return &huma.Schema{Ref: "#/components/schemas/InstitutionKind"}
}
Fix Empty Security Marshaling

The empty security object has semantic meaning in OpenAPI 3.x which enables you to override a global security setting to make one or more operations public. It's now possible to do so:

huma.Register(api, huma.Operation{
	OperationID: "GetUser",
	Method:      http.MethodGet,
	Path:        "/user/{id}",
	Security:    []map[string][]string{}, // This will not require security!
}, func(ctx context.Context, input *GetUserInput) (*GetUserOutput, error) {
	resp := &GetUserOutput{}
	resp.Body.Message = "GetUser with ID: " + input.ID + " works!"
	return resp, nil
})
Expanded Adapter Interface

The huma.Adapter interface now has methods for getting the HTTP version and TLS info of the incoming request, which enables better tracing middleware using e.g. OpenTelemetry.

Schema Transformers Automatically Call schema.PrecomputeMessages()

Schema transformers may modify the schema in ways that precomputed messages & validation cache data are no longer valid. This change makes sure to recompute them if the schema has been modified, preventing potential panics.

Configurable Array Nullability

Huma v2.20.0 introduced nullable JSON Schema arrays for Go slices due to the default behavior of Go's JSON marshaler. This change resulted in some clients (e.g. Typescript) now needing to do extra checks even when the service is sure it will never return a nil slice. This release includes a way to change the global default Huma behavior by setting huma.DefaultArrayNullable = false. It is still possible to set nullable on each field to override this behavior, but now it is easier to do so globally for those who wish to use the old (arguably less correct) behavior.

Better SSE Support

This release includes some http.ResponseController behavior to unwrap response writers to try and get access to SetWriteDeadline and Flush methods on response writers. This prevents error messages from being dumped into the console and enables Gin SSE support for the first time with the Huma sse package. Most routers should Just Work ™️ with SSE now.

Read-Only & Write-Only Behavior Clarification

The read and write-only behavior of Huma validation has been clarified in the docs. See https://huma.rocks/features/request-validation/#read-and-write-only to ensure it works as you expect.

What's Changed

New Contributors

Full Changelog: danielgtaylor/huma@v2.23.0...v2.24.0

v2.23.0

Compare Source

Overview

Pointers for Non-Param Fields

It's now possible to use pointers for non-param fields in input structs without Huma complaining. For example, here the User is not a path/query/header param and is populated from the Authorization header value for use later:

type EndpointInput struct {
  Token string `header:"Authorization"`
  User *User
}

func (i *EndpointInput) Resolve(ctx huma.Context) []error {
  user, token_valid := ValidateToken(i.Token) // user is nil if token is missing or invalid
  i.User = user 
  return nil
}
Hidden Field Validation

Hidden fields are now validated properly if they are present in the input. For example:

huma.Put(api, "/demo", func(ctx context.Context, input *struct{
	Body struct {
		Field1 string `json:"field1"
		Field2 int `json:"field2" hidden:"true" minimum:"10"`
	}
}) (*MyResponse, error) {
	// If `input.Field2` is sent by the client, the request will fail
	// if its value is below 10 due to the validation schema.
	return &MyResponse{...}, nil
})
Prevent Overwriting Schema Validations

All validations now take the existing value of the validator as input when generating the schema, which means a SchemaProvider or SchemaTransformer output won't get overwritten when generating schemas. This fixes a bug that was partially fixed but missed several important fields like pattern.

Non-Addressable Resolver

It's now possible to use non-addressable types which implement Resolver, such as custom primitive types as map keys. This is currently a little less efficient as a pointer to the type needs to be generated, but at least it is now possible and performance can be improved in the future.

Use the Status Code from NewError

When providing your own custom huma.NewError function, the resulting error's status code was ignored. This has been fixed to be used as the output status code, enabling the function to modify the status code before going out on the wire.

NewError with a Context

It's now possible to replace huma.NewErrorWithContext so your error generation function has access to the underlying request context.

NewWithPrefix & Servers

When using humago.NewWithPrefix and not providing any servers, a single server entry is now generated for you with the given prefix.

Support url.URL Parameters

You can now use a URL as an input path/query/header parameter and it will be parsed/validated for you.

Request Body Generation Improvements

Like response body generation, the request body generation has been improved to generate missing pieces of the body OpenAPI structure. This enables you to easily e.g. add a description but have Huma still generate the JSON Schema for you. Example:

func (tr TEERouter) RegisterRoutes(api huma.API) {
	operation := huma.Operation{
		Method:      http.MethodPost,
		Path:        "/tee",
		Summary:     "TEE",
		Description: "TEE description",
		RequestBody: &huma.RequestBody{
			Description: "My custom request schema",
		},
	}
	huma.Register(api, operation, tr.CalculateTEE)
}

What's Changed

New Contributors

Full Changelog: danielgtaylor/huma@v2.22.1...v2.23.0

v2.22.1

Compare Source

Overview

This patch release fixes a bug where the order of operations when resetting a buffer could cause a race condition when putting that buffer back into the shared sync.Pool for re-use when reading in request bodies.

What's Changed

Full Changelog: danielgtaylor/huma@v2.22.0...v2.22.1

v2.22.0

Compare Source

Sponsors

A big thank you to our new sponsor:

Overview

Minimum Go Version: 1.21

The minimum Go version has been upgraded to 1.21, in alignment with the official Go policy. This enables us to fix some critical vulnerabilities with optional dependencies via dependabot and allows the code to be updated to use newer packages like slices, modernizing the codebase.

Each major Go release is supported until there are two newer major releases. For example, Go 1.5 was supported until the Go 1.7 release, and Go 1.6 was supported until the Go 1.8 release.

https://go.dev/doc/devel/release

Fixes Raw Body Race Condition

This release fixes a critical bug where you could run into a race condition using a shared buffer when accessing a request's RawBody []byte field. The buffer was getting returned to the sync.Pool too early, resulting in multiple requests having concurrent access. For handlers which register needing access to the RawBody field, returning the buffer to the pool is now deferred until after then entire handler has run, fixing the issue.

[!WARNING]
If you use the RawBody feature, you should upgrade immediately. This bug results in incorrect/corrupted data.

Better encoding.TextUnmarshaler Support

Support for types which implement encoding.TextUnmarshaler has been improved. The types are now treated as a JSON Schema string by default, making it easier to set up validation and defaults without needing to provide a custom schema via huma.SchemaProvider. Among other things this can be used for custom date/time types:

type MyDate time.Time

func (d *MyDate) UnmarshalText(data []byte) error {
	t, err := time.Parse(time.RFC3339, string(data))
	if err != nil {
		return err
	}
	*d = MyDate(t)
	return nil
}

// Later use it in a request
type Request struct {
	Date MyDate `json:"date" format:"date-time" example:"2024-01-01T12:00:00Z"`
}
Precompute Schema Validation

Schema validation messages are no longer required to be precomputed manually with a call to schema.PrecomputeMessages() as this now happens at operation registration time. This simplifies using custom schemas and makes it possible to define them inline with the operation.

If you modify a schema after registration, you must still call PrecomputeMessages() manually to update the messages.

Fix Nil Response Panic

If an operation is registered as returning a body and a handler mistakenly invokes return nil, nil (meaning no response, no error) this caused a panic as the body is required. This release changes that behavior to no longer panic, but instead return the operation's default status code instead.

What's Changed

New Contributors

Full Changelog: danielgtaylor/huma@v2.21.0...v2.22.0

v2.21.0

Compare Source

Overview

Better Support for Default/Example in Custom Schemas

Fixes an issue where custom schemas could have values overwritten by the default instead of using the given value. For example:

type GreetingType int

func (*GreetingType) Schema(r huma.Registry) *huma.Schema {
	schema := &huma.Schema{
		Type:     huma.TypeInteger,
		Default:  10,
		Examples: []any{1},
	}
	return schema
}
Better Errors When Using Discriminators

OpenAPI supports using a discriminator field in schemas that use oneOf to determine which of the included schemas to validate against. Huma now uses this information to generate better error messages like expected required property color to be present instead of just saying it expected one of the schemas to match. Also handles problems with the discriminator type and value mapping. Example https://go.dev/play/p/5gkNczNJ_jK:

type Cat struct {
	Name string `json:"name" minLength:"2" maxLength:"10"`
	Kind string `json:"kind" enum:"cat"`
}

type Dog struct {
	Color string `json:"color" enum:"black,white,brown"`
	Kind  string `json:"kind" enum:"dog"`
}

type DogOrCat struct {
	Kind string `json:"kind" enum:"cat,dog"`
}

func (v DogOrCat) Schema(r huma.Registry) *huma.Schema {
	catSchema := r.Schema(reflect.TypeOf(Cat{}), true, "Cat")
	dogSchema := r.Schema(reflect.TypeOf(Dog{}), true, "Dog")

	return &huma.Schema{
		Type:        huma.TypeObject,
		Description: "Animal",
		OneOf: []*huma.Schema{
			{Ref: catSchema.Ref},
			{Ref: dogSchema.Ref},
		},
		Discriminator: &huma.Discriminator{
			PropertyName: "kind",
			Mapping: map[string]string{
				"cat": catSchema.Ref,
				"dog": dogSchema.Ref,
			},
		},
	}
}

// ...

huma.Put(api, "/demo", func(ctx context.Context, input *struct {
	Body DogOrCat
}) (*DemoResponse, error) {
	resp := &DemoResponse{}
	resp.Body.Message = "You sent a " + input.Body.Kind
	return resp, nil
})

What's Changed


Configuration

📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot force-pushed the renovate/github.com-danielgtaylor-huma-2.x branch from 151d0e8 to bc157bf Compare November 13, 2024 19:48
@renovate renovate bot force-pushed the renovate/github.com-danielgtaylor-huma-2.x branch from bc157bf to 9e5950b Compare December 10, 2024 21:27
@renovate renovate bot force-pushed the renovate/github.com-danielgtaylor-huma-2.x branch from 9e5950b to 75a5135 Compare January 20, 2025 20:45
@renovate renovate bot force-pushed the renovate/github.com-danielgtaylor-huma-2.x branch 2 times, most recently from 6a6fb1a to 834f838 Compare February 18, 2025 22:16
@renovate renovate bot force-pushed the renovate/github.com-danielgtaylor-huma-2.x branch 2 times, most recently from cacecf6 to 8dfcaed Compare February 25, 2025 18:49
@renovate renovate bot force-pushed the renovate/github.com-danielgtaylor-huma-2.x branch from 8dfcaed to 0220fed Compare March 10, 2025 18:40
@renovate renovate bot force-pushed the renovate/github.com-danielgtaylor-huma-2.x branch from 0220fed to dd44854 Compare March 18, 2025 22:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0 participants