goadesign / goa

🌟 Goa: Elevate Go API development! 🚀 Streamlined design, automatic code generation, and seamless HTTP/gRPC support. ✨
https://goa.design
MIT License
5.62k stars 557 forks source link

Goa generating uncompilable code while using views on types that contain OneOf union type #3486

Closed avinashbhashyam-rs closed 6 months ago

avinashbhashyam-rs commented 6 months ago

Complete design

package design

import (
    . "goa.design/goa/v3/dsl"
)

var _ = Service("Cars Service", func() {
    Description(`Test Cars Service`)

    Method("get_car", func() {
        Description("Fetches list of cars.")
        Payload(func() {
            Field(1, "ID", Int64, "ID of the car")
            Field(2, "View", String, "specifies the view of the car", func() {
                Enum("default", "extended")
                Default("default")
            })
            Required("ID")
        })
        Result(Car)

        Error("internal")
        GRPC(func() {
            Response(CodeOK)
            Response("internal", CodeInternal)
        })
    })
})

var Car = ResultType("Car", "Car", func() {
    Description("Test Car")

    Field(1, "ID", Int64, "ID of the car")
    Field(2, "Name", String, "Name of the car")
    Field(3, "Type", CarType, "Model of the car")
    Field(4, "Year", Int, "Year of the car")

    View("default", func() {
        Attribute("ID")
        Attribute("Name")
    })

    View("extended", func() {
        Attribute("ID")
        Attribute("Name")
        Attribute("Type")
        Attribute("Year")
    })

    Required("ID", "Name")
})

var CarType = Type("CarType", func() {
    Description("Car Type")

    OneOf("Type", func() {
        Field(1, "Sedan", Sedan, "Type of the car")
        Field(2, "SUV", SUV, "Type of the car")
    })

    Required("Type")
})

var Sedan = Type("Sedan", func() {
    Description("Sedan Type")

    Field(1, "DriveType", String, "Model of the car")
    Field(2, "Seats", SeatCount, "Number of seats")
})

var SUV = Type("SUV", func() {
    Description("SUV Type")

    Field(1, "DriveType", String, "Model of the car")
    Field(2, "Seats", SeatCount, "Number of seats")
})

var SeatCount = Type("SeatCount", func() {
    Description("Seat Count")

    Field(1, "Seats", Int, "Number of seats")

    Required("Seats")
})

Invalid code generated

// ValidateCarTypeView runs the validations defined on CarTypeView.
func ValidateCarTypeView(result *CarTypeView) (err error) {
    if result.Type == nil {
        err = goa.MergeErrors(err, goa.MissingFieldError("Type", "result"))
    }
    switch v := result.Type.(type) {
    case *CarTypeView_Sedan:
        if v.Sedan != nil {
            if err2 := ValidateSedanView(v.Sedan); err2 != nil {
                err = goa.MergeErrors(err, err2)
            }
        }

    case *CarTypeView_SUV:
        if v.SUV != nil {
            if err2 := ValidateSUVView(v.SUV); err2 != nil {
                err = goa.MergeErrors(err, err2)
            }
        }
    }
    return
}

The types *CarTypeView_Sedan and CarTypeView_SUV are not generated.

Note: The generated code works fine as long as the Union types Sedan and SUV do not contain any user defined types i.e if we make Seats a String as opposed to SeatCount