Skip to content

Commit

Permalink
Fix duplicate type generation (#2742)
Browse files Browse the repository at this point in the history
When design mixes implicit and explicit views in result definitions.
Each method needs its own result type to generate proper view rendering
code based on the Meta attribute for explicit views but the types
themselves only need to be generated once as they define all the fields.
  • Loading branch information
raphael authored Dec 14, 2020
1 parent 3f44066 commit e77c161
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 1 deletion.
11 changes: 10 additions & 1 deletion codegen/service/service_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,16 @@ func (d ServicesData) analyze(service *expr.ServiceExpr) *Data {
projected := seenProj[rt.ID()]
projAtt := &expr.AttributeExpr{Type: projected.Type}
vrt := buildViewedResultType(e.Result, projAtt, viewspkg, scope, viewScope)
viewedRTs = append(viewedRTs, vrt)
found := false
for _, rt := range viewedRTs {
if rt.Type.ID() == vrt.Type.ID() {
found = true
break
}
}
if !found {
viewedRTs = append(viewedRTs, vrt)
}
m.ViewedResult = vrt
seenViewed[vrt.Name+"::"+view] = vrt
}
Expand Down
1 change: 1 addition & 0 deletions codegen/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func TestService(t *testing.T) {
{"no-payload-result", testdata.EmptyPayloadMethodDSL, testdata.EmptyPayloadMethod},
{"payload-result-with-default", testdata.WithDefaultDSL, testdata.WithDefault},
{"result-with-multiple-views", testdata.MultipleMethodsResultMultipleViewsDSL, testdata.MultipleMethodsResultMultipleViews},
{"result-with-explicit-and-default-views", testdata.WithExplicitAndDefaultViewsDSL, testdata.WithExplicitAndDefaultViews},
{"result-collection-multiple-views", testdata.ResultCollectionMultipleViewsMethodDSL, testdata.ResultCollectionMultipleViewsMethod},
{"result-with-other-result", testdata.ResultWithOtherResultMethodDSL, testdata.ResultWithOtherResultMethod},
{"result-with-result-collection", testdata.ResultWithResultCollectionMethodDSL, testdata.ResultWithResultCollectionMethod},
Expand Down
100 changes: 100 additions & 0 deletions codegen/service/testdata/service_code.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,106 @@ func newSingleViewView(res *SingleView) *multiplemethodsresultmultipleviewsviews
}
`

const WithExplicitAndDefaultViews = `
// Service is the WithExplicitAndDefaultViews service interface.
type Service interface {
// A implements A.
// The "view" return value must have one of the following views
// - "default"
// - "tiny"
A(context.Context) (res *MultipleViews, view string, err error)
// A implements A.
AEndpoint(context.Context) (res *MultipleViews, err error)
}
// ServiceName is the name of the service as defined in the design. This is the
// same value that is set in the endpoint request contexts under the ServiceKey
// key.
const ServiceName = "WithExplicitAndDefaultViews"
// MethodNames lists the service method names as defined in the design. These
// are the same values that are set in the endpoint request contexts under the
// MethodKey key.
var MethodNames = [2]string{"A", "A"}
// MultipleViews is the result type of the WithExplicitAndDefaultViews service
// A method.
type MultipleViews struct {
A string
B int
}
// NewMultipleViews initializes result type MultipleViews from viewed result
// type MultipleViews.
func NewMultipleViews(vres *withexplicitanddefaultviewsviews.MultipleViews) *MultipleViews {
var res *MultipleViews
switch vres.View {
case "default", "":
res = newMultipleViews(vres.Projected)
case "tiny":
res = newMultipleViewsTiny(vres.Projected)
}
return res
}
// NewViewedMultipleViews initializes viewed result type MultipleViews from
// result type MultipleViews using the given view.
func NewViewedMultipleViews(res *MultipleViews, view string) *withexplicitanddefaultviewsviews.MultipleViews {
var vres *withexplicitanddefaultviewsviews.MultipleViews
switch view {
case "default", "":
p := newMultipleViewsView(res)
vres = &withexplicitanddefaultviewsviews.MultipleViews{Projected: p, View: "default"}
case "tiny":
p := newMultipleViewsViewTiny(res)
vres = &withexplicitanddefaultviewsviews.MultipleViews{Projected: p, View: "tiny"}
}
return vres
}
// newMultipleViews converts projected type MultipleViews to service type
// MultipleViews.
func newMultipleViews(vres *withexplicitanddefaultviewsviews.MultipleViewsView) *MultipleViews {
res := &MultipleViews{}
if vres.A != nil {
res.A = *vres.A
}
if vres.B != nil {
res.B = *vres.B
}
return res
}
// newMultipleViewsTiny converts projected type MultipleViews to service type
// MultipleViews.
func newMultipleViewsTiny(vres *withexplicitanddefaultviewsviews.MultipleViewsView) *MultipleViews {
res := &MultipleViews{}
if vres.A != nil {
res.A = *vres.A
}
return res
}
// newMultipleViewsView projects result type MultipleViews to projected type
// MultipleViewsView using the "default" view.
func newMultipleViewsView(res *MultipleViews) *withexplicitanddefaultviewsviews.MultipleViewsView {
vres := &withexplicitanddefaultviewsviews.MultipleViewsView{
A: &res.A,
B: &res.B,
}
return vres
}
// newMultipleViewsViewTiny projects result type MultipleViews to projected
// type MultipleViewsView using the "tiny" view.
func newMultipleViewsViewTiny(res *MultipleViews) *withexplicitanddefaultviewsviews.MultipleViewsView {
vres := &withexplicitanddefaultviewsviews.MultipleViewsView{
A: &res.A,
}
return vres
}
`

const ResultCollectionMultipleViewsMethod = `
// Service is the ResultCollectionMultipleViewsMethod service interface.
type Service interface {
Expand Down
28 changes: 28 additions & 0 deletions codegen/service/testdata/service_dsls.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,34 @@ var MultipleMethodsResultMultipleViewsDSL = func() {
})
}

var WithExplicitAndDefaultViewsDSL = func() {
var RTWithViews = ResultType("application/vnd.result.multiple.views", func() {
TypeName("MultipleViews")
Attributes(func() {
Attribute("a", String)
Attribute("b", Int)
Required("a", "b")
})
View("default", func() {
Attribute("a")
Attribute("b")
})
View("tiny", func() {
Attribute("a")
})
})
Service("WithExplicitAndDefaultViews", func() {
Method("A", func() {
Result(RTWithViews)
})
Method("A", func() {
Result(RTWithViews, func() {
View("tiny")
})
})
})
}

var ResultCollectionMultipleViewsMethodDSL = func() {
var RTWithViews = ResultType("application/vnd.result.multiple.views", func() {
TypeName("MultipleViews")
Expand Down

0 comments on commit e77c161

Please sign in to comment.