goadesign / goa

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

`CollectionOf()` + `View()` leading to unknown attribute errors. #3546

Closed bkeane closed 15 hours ago

bkeane commented 3 days ago

What seems to be the problem?

At least on the http service side of things, there seems to be a problem with generating the likes of...

        Result(CollectionOf(types.Resource), func() {
            View("some_view")
        })

where types.Resource is akin to ...

var Resource = ResultType("application/vnd.goa.resource", "Resource", func() {
    Attributes(func() {
        Attribute("id", UUID, "Unique identifier of the resource")
         })

    View("some_view", func() {
        Description("Some useful view")
        Attribute("id")
    })
})

goa gen results in...

[path/to/design/design.go:<line_number>] unknown attribute "id" in attribute

Empirical findings.

  1. If you remove CollectionOf, leaving Result(types.Resource, func() { View("some_view") }), gen runs cleanly.
  2. If you remove the func() { View(...) }, leaving Result(CollectionOf(types.Resource)), gen runs cleanly.
raphael commented 3 days ago

The following generated successfully for me:

package design

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

var _ = Service("service", func() {
    Method("list_resources", func() {
        Result(CollectionOf(Resource), func() {
            View("some_view")
        })
    })
})

var Resource = ResultType("application/vnd.goa.resource", "Resource", func() {
    Attributes(func() {
        Attribute("id", String, "Unique identifier of the resource")
    })

    View("some_view", func() {
        Description("Some useful view")
        Attribute("id")
    })
})

Would you have a small repro I could try?

bkeane commented 2 days ago

Indeed a slightly more complex scenario is at play. I threw a branch up with a repro example here.

EDIT: After pushing this to git, all of a sudden I stopped seeing the issue in my example. So I have yet to get to the bottom of it.

bkeane commented 1 day ago

Alright, I had some time to sit down and run through some scenarios. The original error disappeared, at one point reappeared, then quickly disappeared again without any clear causation I've been able to deduce.

However! I am able to recreate what I'm starting to believe is the culprit behind whatever 👻 bug I've been chasing.

It seems that CollectionOf is not picking up the pathing directive related to Meta("struct:pkg:path", "types"). Especially in return types.

Repro

  1. Clone this branch.
  2. goa gen github.com/bkeane/gists/design.
  3. Have a 👀 in gen/as_json/services.go.

Example

// gen/as_json/service.go

// newRow converts projected type Row to service type Row.
func newRow(vres *asjsonviews.RowView) *Row {
    res := &types.Row{
        ID:    vres.ID,
        First: vres.First,
        Last:  vres.Last,
        Email: vres.Email,
        Ssn:   vres.Ssn,
    }
    return res
}

Internal to this generated function, &types.Row is correctly referenced, but the return type of *Row is still assuming Row to be a part of the top level package design and not part of the generated types package.

If you replace CollectionOf with ArrayOf the generated code seems fine, but does not involve any response types in this scenario. I do see in a larger codebase this repo structure working with ArrayOf in more complex scenarios.

raphael commented 16 hours ago

Nice, for posterity here is a minimalistic design that reproduces the issue:

package design

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

var _ = Service("AsJson", func() {
    Method("index", func() {
        Result(CollectionOf(Row))
        HTTP(func() { GET("/") })
    })
})

var Row = ResultType("application/vnd.goa.row", "Row", func() {
    Attributes(func() {
        Field(0, "id", String, "Unique identifier a row")
    })
    Meta("struct:pkg:path", "types")
})