这是indexloc提供的服务,不要输入任何密码
Skip to content

Conversation

@superstas
Copy link
Contributor

Hey @danielgtaylor

This PR proposes a solution for the issue #560.

We tested this change internally, and it decreased the size of our generated OAS by ~22%.

Conversely, this change may look backward-incompatible ( inlined types are being replaced by $ref ), so I implemented it as the registry option.

Could you please look and share your thoughts on what could be improved or what I overlooked?

Thank you.

@codecov
Copy link

codecov bot commented Mar 12, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 93.02%. Comparing base (6f2a42b) to head (233a02e).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #758      +/-   ##
==========================================
+ Coverage   93.00%   93.02%   +0.01%     
==========================================
  Files          23       23              
  Lines        5250     5265      +15     
==========================================
+ Hits         4883     4898      +15     
  Misses        315      315              
  Partials       52       52              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@danielgtaylor
Copy link
Owner

@superstas thanks this is a nice approach! I also think using a field tag could work to determine when to use a ref vs. when not to (important for refactoring without breaking clients potentially) instead of having it be all-or-nothing at the registry level.

One scenario that seems problematic though is this:

type MyCustomType int

type ResponseBody struct {
  Field1 MyCustomType `json:"field1" enum="1,2,3"`
  Field2 MyCustomType `json:"field2" minimum:"5" multipleOf="5"`
}

How would this get represented in the JSON schema if you are creating refs?

@superstas
Copy link
Contributor Author

@danielgtaylor, an approach with a field tag looks good; I'll try implementing it in a separate commit.

As for your question:

How would this get represented in the JSON schema if you are creating refs?

Well, according to the spec (ref), there can only be the summary and description fields. A situation like this is a conflict that should be shown to the user in some way.

Possible solutions:

  • Use $ref only for the very 1st field and keep the rest untouched
  • Keep all the conflict cases untouched

WDYT?

@danielgtaylor
Copy link
Owner

danielgtaylor commented Mar 18, 2025

@superstas I'm not sure how to solve this honestly. Maybe if there is additional validation on the struct field via tags then you just cannot use a $ref and must generate it inline. So maybe a $ref is only generated when:

  1. You use a named type
  2. There is no additional validation (doc should be okay, maybe example is okay?)

So if you want to use validation your custom primitive type needs to implement huma.SchemaProvider or huma.SchemaTransformer and your struct fields shouldn't add any validation.

type MyEnum string

var _ huma.SchemaTransformer = (*MyEnum)(nil)

func (e MyEnum) TransformSchema(r huma.Registry, s *huma.Schema) *huma.Schema {
	s.Enum = []any{"one", "two", "three"}
	s.Description = "This is a description of the enum"
	return s
}

// Then later use it:
type MyInput struct {
	Body struct {
		Value MyEnum `json:"value"`
	}
}

Would generate something like:

components:
  schemas:
    MyEnum:
      type: string
      enum: ["one", "two", "three"]
      description: "This is a description of the enum"
    MyInputBody:
      type: object
      properties:
        value:
          $ref: "#/components/schemas/MyEnum

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.

2 participants