huma
huma copied to clipboard
OpenAPI: How to prefix all schemas?
For some unfortunate legacy reason, we require all OpenAPI spec schemas to have a common prefix.
We currently use the following code to fixup the generated spec. The way we do that is probably quite fickle.
Is there any way to make this more robust, e.g. via some builtin functionality?
// Add custom prefix to all supported schemas.
{
const prefix = "SomePrefix"
handled := make(map[any]struct{})
handleOnce := func(v any, fn func()) {
if _, ok := handled[v]; !ok {
handled[v] = struct{}{}
fn()
}
}
m := oapi.Components.Schemas.Map()
replaceSchemas := make(map[string]string, len(m))
for _, mk := range slices.Collect(maps.Keys(m)) {
v := m[mk]
handleOnce(v, func() {
delete(m, mk)
nk := prefix + mk
replaceSchemas[mk] = nk
m[nk] = v
})
}
for _, v := range m {
for _, p := range v.Properties {
if p.Ref != "" {
if suffix, found := strings.CutPrefix(p.Ref, "#/components/schemas/"); found {
p.Ref = "#/components/schemas/" + replaceSchemas[suffix]
}
}
if p.Items != nil {
handleOnce(p.Items, func() {
if suffix, found := strings.CutPrefix(p.Items.Ref, "#/components/schemas/"); found {
p.Items.Ref = "#/components/schemas/" + replaceSchemas[suffix]
}
})
}
for ek, ev := range p.Examples {
handleOnce(ev, func() {
if evs, ok := ev.(string); ok {
if !strings.HasSuffix(evs, ".json") {
return
}
evs = strings.TrimSuffix(evs, ".json")
ss := strings.Split(evs, "/")
ss[len(ss)-1] = replaceSchemas[ss[len(ss)-1]] + ".json"
p.Examples[ek] = strings.Join(ss, "/")
}
})
}
}
}
for _, p := range oapi.Paths {
for _, op := range []*huma.Operation{
p.Get,
p.Put,
p.Post,
p.Delete,
p.Options,
p.Head,
p.Patch,
p.Trace,
} {
if op == nil {
continue
}
contents := make([]*huma.MediaType, 0)
if op.RequestBody != nil {
for _, rc := range op.RequestBody.Content {
contents = append(contents, rc)
}
}
for _, r := range op.Responses {
for _, rc := range r.Content {
contents = append(contents, rc)
}
}
for _, c := range contents {
handleOnce(c.Schema, func() {
if suffix, found := strings.CutPrefix(c.Schema.Ref, "#/components/schemas/"); found {
c.Schema.Ref = "#/components/schemas/" + replaceSchemas[suffix]
}
})
if c.Schema.Items != nil {
handleOnce(c.Schema.Items, func() {
if suffix, found := strings.CutPrefix(c.Schema.Items.Ref, "#/components/schemas/"); found {
c.Schema.Items.Ref = "#/components/schemas/" + replaceSchemas[suffix]
}
})
}
}
}
}
}
you could try something like this
registry := huma.NewMapRegistry("#/components/schemas/", CustomSchemaNamer)
humaConfig.OpenAPI.Components = &huma.Components{
Schemas: registry,
}
func CustomSchemaNamer(t reflect.Type, hint string) string {
name := huma.DefaultSchemaNamer(t, hint)
parts := strings.Split(t.PkgPath(), "/")
if len(parts) > 1 {
return parts[len(parts)-1] + "." + name
}
return name
}