spec
```yaml
components:
schemas:
CreateUserOutputBody:
additionalProperties: false
properties:
$schema:
description: A URL to the JSON Schema for this object.
examples:
- https://example.com/schemas/CreateUserOutputBody.json
format: uri
readOnly: true
type: string
message:
type: string
required:
- message
type: object
ErrorDetail:
additionalProperties: false
properties:
location:
description: Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id'
type: string
message:
description: Error message text
type: string
value:
description: The value at the given location
type: object
ErrorModel:
additionalProperties: false
properties:
$schema:
description: A URL to the JSON Schema for this object.
examples:
- https://example.com/schemas/ErrorModel.json
format: uri
readOnly: true
type: string
detail:
description: A human-readable explanation specific to this occurrence of the problem.
examples:
- Property foo is required but is missing.
type: string
errors:
description: Optional list of individual error details
items:
$ref: "#/components/schemas/ErrorDetail"
type:
- array
- "null"
instance:
description: A URI reference that identifies the specific occurrence of the problem.
examples:
- https://example.com/error-log/abc123
format: uri
type: string
status:
description: HTTP status code
examples:
- 400
format: int64
type: integer
title:
description: A short, human-readable summary of the problem type. This value should not change between occurrences of the error.
examples:
- Bad Request
type: string
type:
default: about:blank
description: A URI reference to human-readable documentation for the error.
examples:
- https://example.com/errors/example
format: uri
type: string
type: object
UpdateUserOutputBody:
additionalProperties: false
properties:
$schema:
description: A URL to the JSON Schema for this object.
examples:
- https://example.com/schemas/UpdateUserOutputBody.json
format: uri
readOnly: true
type: string
message:
type: string
required:
- message
type: object
UpdateUserRequest:
additionalProperties: false
description: UpdateUserInput
properties:
$schema:
description: A URL to the JSON Schema for this object.
examples:
- https://example.com/schemas/UpdateUserRequest.json
format: uri
readOnly: true
type: string
tag:
type: string
required:
- tag
- input_data
type: object
UpsertRequestUser:
additionalProperties: false
properties:
$schema:
description: A URL to the JSON Schema for this object.
examples:
- https://example.com/schemas/UpsertRequestUser.json
format: uri
readOnly: true
type: string
input_data:
$ref: "#/components/schemas/User"
tag:
type: string
required:
- tag
- input_data
type: object
User:
additionalProperties: false
properties:
name:
maxLength: 32
minLength: 1
type: string
surname:
maxLength: 32
minLength: 1
type: string
required:
- name
- surname
type: object
info:
title: My API
version: 1.0.0
openapi: 3.1.0
paths:
/user:
post:
operationId: CreateUser
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/UpsertRequestUser"
required: true
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/CreateUserOutputBody"
description: OK
default:
content:
application/problem+json:
schema:
$ref: "#/components/schemas/ErrorModel"
description: Error
put:
operationId: UpdateUser
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateUserRequest"
required: true
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateUserOutputBody"
description: OK
default:
content:
application/problem+json:
schema:
$ref: "#/components/schemas/ErrorModel"
description: Error
```
In this case, I want to have two operations Create and Update that:
Share the data structures ( UpsertRequest[D any] particularly )
But Update doesn't have one of the fields ( input_data ) on the input.
To avoid duplication of the types ( IRL, these types are quite huge and complex ), I reuse them under CreateUserInput and updateUserRequest ( a wrapper for a human-readable schema name ) + UpdateUserInput.
updateUserRequest has TransformSchema that removes input_data from s.Properties
Everything works perfectly for the OAS generation.
The issue
main_test.go
func TestUserCreate(t *testing.T) {
_, api := humatest.New(t, huma.DefaultConfig("Test API", "1.0.0"))
addRoutes(api)
// Create a new user
_ = api.Post("/user", map[string]any{"tag": "tag1", "input_data": map[string]any{"name": "John", "surname": "Doe"}})
// Update the user
_ = api.Put("/user", map[string]any{"tag": "tag1", "input_data": map[string]any{"name": "John", "surname": "Doe"}})
}
I get a panic on the PUT /user request with input_data ( that I expect shouldn't pass the validation )
My internal research showed that modifying s.Properties directly makes it inconsistent with the private propertyNames ( ref ).
I fixed this internally by this patch
diff --git a/validate.go b/validate.go
index 365a237..193f295 100644
--- a/validate.go
+++ b/validate.go
@@ -586,7 +586,11 @@ func handleMapString(r Registry, s *Schema, path *PathBuffer, mode ValidateMode,
}
for _, k := range s.propertyNames {
- v := s.Properties[k]
+ v, ok := s.Properties[k]
+ if !ok {
+ continue
+ }
// Schemas are generated such that the read/write-only properties are set
// alongside the `$ref`, if it is present (i.e. for objects). If not,
@@ -680,7 +684,11 @@ func handleMapAny(r Registry, s *Schema, path *PathBuffer, mode ValidateMode, m
}
for _, k := range s.propertyNames {
- v := s.Properties[k]
+ v, ok := s.Properties[k]
+ if !ok {
+ continue
+ }
// Schemas are generated such that the read/write-only properties are set
// alongside the `$ref`, if it is present (i.e. for objects). If not,
But there are a few questions:
Is it a bug or the wrong way of using Huma?
What is the best way to achieve the state I was trying to achieve? ( having a shared Go structure but slightly different OAS + Validation )
Hi there,
I have the following types
main.go
Here's the full generated spec.
spec
```yaml components: schemas: CreateUserOutputBody: additionalProperties: false properties: $schema: description: A URL to the JSON Schema for this object. examples: - https://example.com/schemas/CreateUserOutputBody.json format: uri readOnly: true type: string message: type: string required: - message type: object ErrorDetail: additionalProperties: false properties: location: description: Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id' type: string message: description: Error message text type: string value: description: The value at the given location type: object ErrorModel: additionalProperties: false properties: $schema: description: A URL to the JSON Schema for this object. examples: - https://example.com/schemas/ErrorModel.json format: uri readOnly: true type: string detail: description: A human-readable explanation specific to this occurrence of the problem. examples: - Property foo is required but is missing. type: string errors: description: Optional list of individual error details items: $ref: "#/components/schemas/ErrorDetail" type: - array - "null" instance: description: A URI reference that identifies the specific occurrence of the problem. examples: - https://example.com/error-log/abc123 format: uri type: string status: description: HTTP status code examples: - 400 format: int64 type: integer title: description: A short, human-readable summary of the problem type. This value should not change between occurrences of the error. examples: - Bad Request type: string type: default: about:blank description: A URI reference to human-readable documentation for the error. examples: - https://example.com/errors/example format: uri type: string type: object UpdateUserOutputBody: additionalProperties: false properties: $schema: description: A URL to the JSON Schema for this object. examples: - https://example.com/schemas/UpdateUserOutputBody.json format: uri readOnly: true type: string message: type: string required: - message type: object UpdateUserRequest: additionalProperties: false description: UpdateUserInput properties: $schema: description: A URL to the JSON Schema for this object. examples: - https://example.com/schemas/UpdateUserRequest.json format: uri readOnly: true type: string tag: type: string required: - tag - input_data type: object UpsertRequestUser: additionalProperties: false properties: $schema: description: A URL to the JSON Schema for this object. examples: - https://example.com/schemas/UpsertRequestUser.json format: uri readOnly: true type: string input_data: $ref: "#/components/schemas/User" tag: type: string required: - tag - input_data type: object User: additionalProperties: false properties: name: maxLength: 32 minLength: 1 type: string surname: maxLength: 32 minLength: 1 type: string required: - name - surname type: object info: title: My API version: 1.0.0 openapi: 3.1.0 paths: /user: post: operationId: CreateUser requestBody: content: application/json: schema: $ref: "#/components/schemas/UpsertRequestUser" required: true responses: "200": content: application/json: schema: $ref: "#/components/schemas/CreateUserOutputBody" description: OK default: content: application/problem+json: schema: $ref: "#/components/schemas/ErrorModel" description: Error put: operationId: UpdateUser requestBody: content: application/json: schema: $ref: "#/components/schemas/UpdateUserRequest" required: true responses: "200": content: application/json: schema: $ref: "#/components/schemas/UpdateUserOutputBody" description: OK default: content: application/problem+json: schema: $ref: "#/components/schemas/ErrorModel" description: Error ```In this case, I want to have two operations
Create
andUpdate
that:UpsertRequest[D any]
particularly )Update
doesn't have one of the fields (input_data
) on the input.To avoid duplication of the types ( IRL, these types are quite huge and complex ), I reuse them under
CreateUserInput
andupdateUserRequest
( a wrapper for a human-readable schema name ) +UpdateUserInput
.updateUserRequest
hasTransformSchema
that removesinput_data
froms.Properties
Everything works perfectly for the OAS generation.
The issue
main_test.go
I get a panic on the
PUT /user
request withinput_data
( that I expect shouldn't pass the validation )My internal research showed that modifying
s.Properties
directly makes it inconsistent with the privatepropertyNames
( ref ).I fixed this internally by this patch
But there are a few questions:
Thank you.