Skip to content

Commit 4fcc702

Browse files
committed
Consolidate loops.
1 parent 72f4ff5 commit 4fcc702

1 file changed

Lines changed: 57 additions & 73 deletions

File tree

internal/generate/types.go

Lines changed: 57 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -648,128 +648,112 @@ func createAllOf(s *openapi3.Schema, stringEnums map[string][]string, name, type
648648
return typeTpls
649649
}
650650

651+
type oneOfItem struct {
652+
schema *openapi3.Schema
653+
enumField string
654+
}
655+
651656
func createOneOf(s *openapi3.Schema, name, typeName string) ([]TypeTemplate, []EnumTemplate) {
657+
// First pass: collect metadata about variants.
652658
discriminatorKeys := map[string]struct{}{}
653659
propertyToVariantTypes := map[string]map[string]struct{}{}
660+
oneOfItems := []oneOfItem{}
654661
for _, variantRef := range s.OneOf {
655-
for propName, propRef := range variantRef.Value.Properties {
662+
item := oneOfItem{schema: variantRef.Value}
663+
keys := sortedKeys(variantRef.Value.Properties)
664+
for _, propName := range keys {
665+
propRef := variantRef.Value.Properties[propName]
666+
propertyField := strcase.ToCamel(propName)
667+
656668
if len(propRef.Value.Enum) == 1 {
657669
discriminatorKeys[propName] = struct{}{}
670+
item.enumField = strcase.ToCamel(propRef.Value.Enum[0].(string))
671+
} else if propRef.Value.Enum == nil && len(keys) == 1 {
672+
item.enumField = propertyField
658673
}
659674
if _, ok := propertyToVariantTypes[propName]; !ok {
660675
propertyToVariantTypes[propName] = map[string]struct{}{}
661676
}
662677
goType := convertToValidGoType(propName, typeName, propRef)
663678
propertyToVariantTypes[propName][goType] = struct{}{}
664679
}
680+
oneOfItems = append(oneOfItems, item)
665681
}
682+
666683
if len(discriminatorKeys) > 1 {
667684
panic(fmt.Sprintf("[ERROR] Found multiple discriminator properties for type %s: %+v", name, discriminatorKeys))
668685
}
686+
687+
// Find properties that have different types across variants.
669688
variantProperties := []string{}
670689
for propName, variantTypes := range propertyToVariantTypes {
671690
if len(variantTypes) > 1 {
672691
variantProperties = append(variantProperties, propName)
673692
}
674693
}
675694

676-
var parsedProperties []string
677-
enumTpls := make([]EnumTemplate, 0)
678-
typeTpls := make([]TypeTemplate, 0)
679-
fields := make([]TypeFields, 0)
680-
681-
for _, v := range s.OneOf {
682-
// We want to iterate over the properties of the embedded object
683-
// and find the type that is a string.
684-
var enumFieldName string
685-
686-
// Iterate over all the schema components in the spec and write the types.
687-
keys := sortedKeys(v.Value.Properties)
688-
for _, prop := range keys {
689-
p := v.Value.Properties[prop]
690-
// We want to collect all the unique properties to create our global oneOf type.
691-
propertyType := convertToValidGoType(prop, typeName, p)
692-
693-
// Check if we have an enum in order to use the corresponding type instead of
694-
// "string"
695-
if propertyType == "string" && len(p.Value.Enum) != 0 {
696-
propertyType = typeName + strcase.ToCamel(prop)
695+
// Second pass: build the unified fields list for the oneOf struct.
696+
oneOfFields := []TypeFields{}
697+
seenFields := map[string]struct{}{}
698+
for _, variantRef := range s.OneOf {
699+
for _, propName := range sortedKeys(variantRef.Value.Properties) {
700+
if _, ok := seenFields[propName]; ok {
701+
continue
697702
}
703+
seenFields[propName] = struct{}{}
698704

699-
propertyName := strcase.ToCamel(prop)
700-
701-
// Avoids duplication for every enum
702-
fmt.Printf("DEBUG FIELD %s %s\n", typeName, propertyName)
703-
if !containsMatchFirstWord(parsedProperties, propertyName) {
704-
field := TypeFields{
705-
Description: formatTypeDescription(propertyName, p.Value),
706-
Name: propertyName,
707-
Type: propertyType,
708-
SerializationInfo: fmt.Sprintf("`json:\"%s,omitempty\" yaml:\"%s,omitempty\"`", prop, prop),
709-
}
710-
711-
// We set the type of a field as "any" if every element of the oneOf property isn't the same
712-
if slices.Contains(variantProperties, prop) {
713-
field.Type = "any"
714-
}
715-
716-
// Check if the field is nullable and use omitzero instead of omitempty.
717-
if p.Value != nil && p.Value.Nullable {
718-
field.SerializationInfo = fmt.Sprintf("`json:\"%s,omitzero\" yaml:\"%s,omitzero\"`", prop, prop)
719-
}
720-
721-
fields = append(fields, field)
705+
propRef := variantRef.Value.Properties[propName]
706+
propertyField := strcase.ToCamel(propName)
707+
propertyType := convertToValidGoType(propName, typeName, propRef)
722708

723-
parsedProperties = append(parsedProperties, propertyName)
709+
// Use the enum type name instead of "string" when the property has an enum.
710+
if propertyType == "string" && len(propRef.Value.Enum) != 0 {
711+
propertyType = typeName + strcase.ToCamel(propName)
724712
}
725713

726-
if p.Value.Enum != nil {
727-
// We want to get the enum value.
728-
// Make sure there is only one.
729-
if len(p.Value.Enum) != 1 {
730-
fmt.Printf("[WARN] TODO: oneOf for %q -> %q enum %#v\n", name, prop, p.Value.Enum)
731-
continue
732-
}
714+
// Use "any" if this property has different types across variants.
715+
if slices.Contains(variantProperties, propName) {
716+
propertyType = "any"
717+
}
733718

734-
enumFieldName = strcase.ToCamel(p.Value.Enum[0].(string))
719+
field := TypeFields{
720+
Description: formatTypeDescription(propertyField, propRef.Value),
721+
Name: propertyField,
722+
Type: propertyType,
723+
SerializationInfo: fmt.Sprintf("`json:\"%s,omitempty\" yaml:\"%s,omitempty\"`", propName, propName),
735724
}
736725

737-
// Enums can appear in a valid OpenAPI spec as a OneOf without necessarily
738-
// being identified as such. If we find an object with a single property
739-
// nested inside a OneOf we will assume this is an enum and modify the name of
740-
// the struct that will be created out of this object.
741-
// e.g. https://github.com/oxidecomputer/omicron/blob/158c0b205f23772dc6c4c97633fd1769cc0e00d4/openapi/nexus.json#L18637-L18682
742-
if len(keys) == 1 && p.Value.Enum == nil {
743-
enumFieldName = propertyName
726+
// Check if the field is nullable and use omitzero instead of omitempty.
727+
if propRef.Value != nil && propRef.Value.Nullable {
728+
field.SerializationInfo = fmt.Sprintf("`json:\"%s,omitzero\" yaml:\"%s,omitzero\"`", propName, propName)
744729
}
730+
731+
oneOfFields = append(oneOfFields, field)
745732
}
733+
}
746734

735+
enumTpls := make([]EnumTemplate, 0)
736+
typeTpls := make([]TypeTemplate, 0)
737+
738+
for _, v := range oneOfItems {
747739
// TODO: This is the only place that has an "additional name" at the end
748740
// TODO: This is where the "allOf" is being detected
749-
tt, et := populateTypeTemplates(name, v.Value, enumFieldName)
741+
tt, et := populateTypeTemplates(name, v.schema, v.enumField)
750742
typeTpls = append(typeTpls, tt...)
751743
enumTpls = append(enumTpls, et...)
752744
}
753745

754-
// TODO: For now AllOf values within a OneOf are treated as enums
755-
// because that's how they are being used. Keep an eye out if this
756-
// changes
757-
for _, v := range s.OneOf {
758-
if v.Value.AllOf != nil {
759-
return typeTpls, enumTpls
760-
}
761-
}
762-
763746
// Make sure to only create structs if the oneOf is not a replacement for enums on the API spec
764-
if len(fields) > 0 {
747+
if len(oneOfFields) > 0 {
765748
typeTpl := TypeTemplate{
766749
Description: formatTypeDescription(typeName, s),
767750
Name: typeName,
768751
Type: "struct",
769-
Fields: fields,
752+
Fields: oneOfFields,
770753
}
771754
typeTpls = append(typeTpls, typeTpl)
772755
}
756+
773757
return typeTpls, enumTpls
774758
}
775759

0 commit comments

Comments
 (0)