danielgtaylor / huma

Huma REST/HTTP API Framework for Golang with OpenAPI 3.1
https://huma.rocks/
MIT License
2.21k stars 153 forks source link

[feature]: Add support of `propertyNames`. #638

Open superstas opened 3 weeks ago

superstas commented 3 weeks ago

Hi there,

I have the following types

main.go

var (
    _ huma.SchemaTransformer = Key("")
    _ huma.SchemaTransformer = Value("")
)

type (
    Key   string
    Value string
)

func (k Key) TransformSchema(r huma.Registry, s *huma.Schema) *huma.Schema {
    min, max := 1, 10
    s.Description = "A key for a map."
    s.MinLength = &min
    s.MaxLength = &max
    return s
}

func (v Value) TransformSchema(r huma.Registry, s *huma.Schema) *huma.Schema {
    min, max := 1, 32
    s.Description = "A value for a map."
    s.MinLength = &min
    s.MaxLength = &max

    return s
}

type Input struct {
    Body struct {
        Values map[Key]Value `json:"values" `
    }
}

type Output struct {
    Body struct {
        Message string `json:"message"`
    }
}

func addRoutes(api huma.API) {
    huma.Post(api, "/input", func(ctx context.Context, input *Input) (*Output, error) {
        resp := &Output{}
        resp.Body.Message = "It works!"
        return resp, nil
    })
}

One parameter, values, is a map of Key and Value. Key and Value have validation rules ( in TransfromSchema ).

The issue.

1) Key validation doesn't work.

main_test.go

func TestUserCreate(t *testing.T) {
    _, api := humatest.New(t, huma.DefaultConfig("Test API", "1.0.0"))
    addRoutes(api)

    _ = api.Post("/input", map[string]any{"values": map[string]any{"": "value"}})
}

Temporary workaround: implementation of huma.Resolver/huma.ResolverWithPath interface.

2) Generated OAS doesn't have a Key schema.

    InputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/InputBody.json
          format: uri
          readOnly: true
          type: string
        values:
          additionalProperties:
            description: A value for a map.
            maxLength: 32
            minLength: 1
            type: string
          type: object
      required:
        - values
      type: object

Value type is represented by additionalProperties.

Key may be represented by propertyNames like

...
        values:
          propertyNames:
            type: string
            maxLength: 10
            minLength: 1
          additionalProperties:
            description: A value for a map.
            maxLength: 32
            minLength: 1
            type: string
          type: object
...

From what I've checked in the source code, propertyNames is not supported yet.

Could you please briefly describe what needs to be considered for the implementation in the source code ( if someone wants to contribute it )?

Thank you.