Incorrect `allOf` validation.
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 35/100
Direzione di ricerca
Inizia con il riproduttore in main_test.go e il ramo allOf di Validate in validate.go, intorno alle righe 378-382. Esegui go test ./... per confermare la risposta 422, quindi traccia il modo in cui vengono validate il riferimento Base e lo schema dell'indirizzo inline. È completato quando una richiesta contenente name, age e address viene accettata, mentre i vincoli allOf rimangono applicati.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Hi there,
I have the following types:
main.go
type Base struct {
Name string `json:"name" doc:"The name to compare." minLength:"1" maxLength:"10"`
Age int `json:"age" doc:"The age to compare." minimum:"0" maximum:"100"`
}
type Address string
type Extended struct {
Base
Address Address `json:"address" doc:"The address to compare." minLength:"1" maxLength:"100"`
}
func (e Extended) TransformSchema(r huma.Registry, s *huma.Schema) *huma.Schema {
s.Properties = nil
s.Type = ""
s.Description = "Extended Schema."
s.AllOf = []*huma.Schema{
r.Schema(reflect.TypeOf(Base{}), true, ""),
{
Type: "object",
Properties: map[string]*huma.Schema{
"address": r.Schema(reflect.TypeOf(Address("")), true, ""),
},
},
}
return s
}
type Input struct {
Body struct {
Extended
}
}
type Output struct {
Body struct {
Message string `json:"message"`
}
}
func addRoutes(api huma.API) {
huma.Post(api, "/user", func(ctx context.Context, input *Input) (*Output, error) {
resp := &Output{}
resp.Body.Message = "It works!"
return resp, nil
})
}
Here's the full generated spec.
spec
```yaml
components:
schemas:
Base:
additionalProperties: false
properties:
age:
description: The age to compare.
format: int64
maximum: 100
minimum: 0
type: integer
name:
description: The name to compare.
maxLength: 10
minLength: 1
type: string
required:
- name
- age
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
InputBody:
additionalProperties: false
allOf:
- $ref: "#/components/schemas/Base"
- properties:
address:
type: string
type: object
description: Extended Schema.
required:
- address
- name
- age
OutputBody:
additionalProperties: false
properties:
$schema:
description: A URL to the JSON Schema for this object.
examples:
- https://example.com/schemas/OutputBody.json
format: uri
readOnly: true
type: string
message:
type: string
required:
- message
type: object
info:
title: My API
version: 1.0.0
openapi: 3.1.0
paths:
/user:
post:
operationId: post-user
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/InputBody"
required: true
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/OutputBody"
description: OK
default:
content:
application/problem+json:
schema:
$ref: "#/components/schemas/ErrorModel"
description: Error
summary: Post user
What I'm trying to achieve is a types' structure that generates OAS with allOf that reuses a shared schema.
InputBody:
additionalProperties: false
allOf:
- $ref: "#/components/schemas/Base"
- properties:
address:
type: string
type: object
description: Schema for the DummyFilter.
required:
- address
- name
- age
Base:
additionalProperties: false
properties:
age:
description: The age to compare.
format: int64
maximum: 100
minimum: 0
type: integer
name:
description: The name to compare.
maxLength: 10
minLength: 1
type: string
required:
- name
- age
type: object
I need such OAS in the following pipeline steps.
The issue
main_test.go
func TestUserCreate(t *testing.T) {
_, api := humatest.New(t, huma.DefaultConfig("Test API", "1.0.0"))
addRoutes(api)
_ = api.Post("/user", map[string]any{
"name": "ValidName",
"age": 1,
"address": "ValidAddress",
})
}
When I send a valid request ( with all three fields ) I get a validation error.
$> go test ./...
=== RUN TestUserCreate
main_test.go:14: Making request:
POST /user HTTP/1.1
Content-Type: application/json
{
"address": "ValidAddress",
"age": 1,
"name": "ValidName"
}
main_test.go:14: Got response:
HTTP/1.1 422 Unprocessable Entity
Connection: close
Content-Type: application/problem+json
Link: </schemas/ErrorModel.json>; rel="describedBy"
{
"$schema": "https:///schemas/ErrorModel.json",
"title": "Unprocessable Entity",
"status": 422,
"detail": "validation failed",
"errors": [
{
"message": "unexpected property",
"location": "body.address",
"value": {
"address": "ValidAddress",
"age": 1,
"name": "ValidName"
}
}
]
}
The address field is unknown.
My internal research showed that internally Huma iterates over all allOf schemas by validating input against each of them ( ref ) which doesn't look correctly.
In my opinion, the correct behavior is to merge all allOf schemas into a single one ( at least properties ) and run Validate.
This is how I fixed it internally (a quite naive approach, not well-tested yet )
diff --git a/validate.go b/validate.go
index 336817c..13dbf4f 100644
--- a/validate.go
+++ b/validate.go
@@ -376,9 +376,7 @@ func Validate(r Registry, s *Schema, path *PathBuffer, mode ValidateMode, v any,
}
if s.AllOf != nil {
- for _, sub := range s.AllOf {
- Validate(r, sub, path, mode, v, res)
- }
+ Validate(r, mergeAllOf(s.AllOf, r), path, mode, v, res)
}
if s.Not != nil {
@@ -560,6 +558,30 @@ func Validate(r Registry, s *Schema, path *PathBuffer, mode ValidateMode, v any,
}
}
+func mergeAllOf(allOf []*Schema, r Registry) *Schema {
+ if len(allOf) == 0 {
+ return nil
+ }
+
+ var merged *Schema
+ for _, s := range allOf {
+ if s.Ref != "" {
+ s = r.SchemaFromRef(s.Ref)
+ }
+
+ if merged == nil {
+ merged = s
+ continue
+ }
+
+ for k, v := range s.Properties {
+ merged.Properties[k] = v
+ }
+ }
+
+ return merged
+}
+
func handleArray[T any](r Registry, s *Schema, path *PathBuffer, mode ValidateMode, res *ValidateResult, arr []T) {
if s.MinItems != nil {
if len(arr) < *s.MinItems {
What do you think? Is there a better approach for handling such cases?
Thank you.
- Lingua principale
- Go
- Stelle
- 4.5k
- Fork
- 298
- Merge medio
- 5g 10h
- PR unite (30g)
- 21
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di danielgtaylor/huma
-
conditional: If-None-Match/If-Match lists with RFC 9110 OWS after a comma fail to matchForse già presa @stareezy-1 l’ha presa 88 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
danielgtaylor/huma#1084 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Feature request: support the HTTP QUERY method (RFC 10008)Forse già presa @lrweck l’ha presa 36 giorni fa. Aperta
Difficoltà 3/5 1-2 giorni Idoneità per principianti 25/100
danielgtaylor/huma#1123 ·
I maintainer di solito rispondono entro 1 giorno
-
[BUG] Wrong location and value in error response for invalid custom type in request bodyForse già presa Una pull request collegata a questa issue è aperta o già unita. Apertabug
Difficoltà 3/5 1-2 giorni Idoneità per principianti 72/100
danielgtaylor/huma#1109 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Update pinned @scalar/api-reference (1.44.20 → 1.65.1) and/or make the docs asset version configurableForse già presa @Orfeo42 l’ha presa 69 giorni fa. Aperta
Difficoltà 3/5 1-2 giorni Idoneità per principianti 68/100
danielgtaylor/huma#1103 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Add support for `patternProperties`Forse già presa @ChrisJr404 l’ha presa 75 giorni fa. Apertaenhancement
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
danielgtaylor/huma#1102 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di danielgtaylor/huma
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
OneBusAway/maglev#1914 ·
I maintainer di solito rispondono entro 2 giorni
-
documentation needs-triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
I maintainer di solito rispondono entro 1 giorno
-
go good first issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
area: api bug severity:low
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
Missing validation for port numbers exceeding valid TCP range (> 65535)Forse già presa @faizanfirdousi l’ha presa oggi. Aperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 80/100
llm-d/llm-d-inference-sim#853 ·
I maintainer di solito rispondono entro 1 giorno