From 07233bc622d616c5030b081941d6b78f6fccdd84 Mon Sep 17 00:00:00 2001 From: Alba Hita Catala Date: Wed, 18 Dec 2024 10:42:26 +0100 Subject: [PATCH] Fix ownership on referenced links Signed-off-by: Alba Hita Catala --- pkg/concepts/attribute.go | 13 +- pkg/generators/golang/builders_generator.go | 36 ++-- pkg/generators/golang/clients_generator.go | 6 +- pkg/generators/golang/json_generator.go | 198 ++++++++++++-------- pkg/generators/golang/types_calculator.go | 32 +++- pkg/generators/golang/types_generator.go | 29 ++- pkg/language/reader.go | 34 ++-- pkg/language/ref_test.go | 67 ++++++- 8 files changed, 277 insertions(+), 138 deletions(-) diff --git a/pkg/concepts/attribute.go b/pkg/concepts/attribute.go index f25e7c3..3037945 100644 --- a/pkg/concepts/attribute.go +++ b/pkg/concepts/attribute.go @@ -27,8 +27,17 @@ type Attribute struct { namedSupport typedSupport - owner *Type - link bool + owner *Type + link bool + linkOwner *Version +} + +func (a *Attribute) SetLinkOwner(v *Version) { + a.linkOwner = v +} + +func (a *Attribute) LinkOwner() *Version { + return a.linkOwner } // NewAttribute creates a new attribute. diff --git a/pkg/generators/golang/builders_generator.go b/pkg/generators/golang/builders_generator.go index 91ef193..643d3d1 100644 --- a/pkg/generators/golang/builders_generator.go +++ b/pkg/generators/golang/builders_generator.go @@ -642,20 +642,24 @@ func (g *BuildersGenerator) fieldName(attribute *concepts.Attribute) string { func (g *BuildersGenerator) fieldType(attribute *concepts.Attribute) *TypeReference { typ := attribute.Type() var ref *TypeReference + referencedVersion := "" + if attribute.LinkOwner() != nil { + referencedVersion = attribute.LinkOwner().Name().String() + } switch { case typ.IsScalar(): ref = g.types.ValueReference(typ) case typ.IsStruct(): - ref = g.types.BuilderReference(typ) + ref = g.types.BuilderReference(typ, referencedVersion) case typ.IsList(): if attribute.Link() { - ref = g.types.BuilderReference(typ) + ref = g.types.BuilderReference(typ, referencedVersion) } else { element := typ.Element() if element.IsScalar() { - ref = g.types.NullableReference(typ) + ref = g.types.NullableReference(typ, referencedVersion) } else { - ref = g.types.BuilderReference(element) + ref = g.types.BuilderReference(element, referencedVersion) ref = g.types.Reference( ref.Import(), ref.Selector(), @@ -667,9 +671,9 @@ func (g *BuildersGenerator) fieldType(attribute *concepts.Attribute) *TypeRefere case typ.IsMap(): element := typ.Element() if element.IsScalar() { - ref = g.types.NullableReference(typ) + ref = g.types.NullableReference(typ, referencedVersion) } else { - ref = g.types.BuilderReference(element) + ref = g.types.BuilderReference(element, referencedVersion) ref = g.types.Reference( ref.Import(), ref.Selector(), @@ -689,12 +693,10 @@ func (g *BuildersGenerator) fieldType(attribute *concepts.Attribute) *TypeRefere } func (g *BuildersGenerator) selectorType(attribute *concepts.Attribute) string { - ref := g.fieldType(attribute) - pkgName := g.packages.VersionSelector(attribute.Owner().Owner()) - if pkgName != ref.selector { - return fmt.Sprintf("%s.", ref.selector) + if attribute.LinkOwner() == nil { + return "" } - return "" + return fmt.Sprintf("%s.", g.packages.VersionSelector(attribute.LinkOwner())) } func (g *BuildersGenerator) setterName(attribute *concepts.Attribute) string { @@ -708,20 +710,24 @@ func (g *BuildersGenerator) setterName(attribute *concepts.Attribute) string { func (g *BuildersGenerator) setterType(attribute *concepts.Attribute) *TypeReference { typ := attribute.Type() var ref *TypeReference + referencedVersion := "" + if attribute.LinkOwner() != nil { + referencedVersion = attribute.LinkOwner().Name().String() + } switch { case typ.IsScalar(): ref = g.types.ValueReference(typ) case typ.IsStruct(): - ref = g.types.BuilderReference(typ) + ref = g.types.BuilderReference(typ, referencedVersion) case typ.IsList(): if attribute.Link() { - ref = g.types.BuilderReference(typ) + ref = g.types.BuilderReference(typ, referencedVersion) } else { element := typ.Element() if element.IsScalar() { ref = g.types.ValueReference(typ) } else { - ref = g.types.BuilderReference(element) + ref = g.types.BuilderReference(element, referencedVersion) ref = g.types.Reference( ref.Import(), ref.Selector(), @@ -735,7 +741,7 @@ func (g *BuildersGenerator) setterType(attribute *concepts.Attribute) *TypeRefer if element.IsScalar() { ref = g.types.ValueReference(typ) } else { - ref = g.types.BuilderReference(element) + ref = g.types.BuilderReference(element, referencedVersion) ref = g.types.Reference( ref.Import(), ref.Selector(), diff --git a/pkg/generators/golang/clients_generator.go b/pkg/generators/golang/clients_generator.go index b9b1471..994d78c 100644 --- a/pkg/generators/golang/clients_generator.go +++ b/pkg/generators/golang/clients_generator.go @@ -1049,7 +1049,7 @@ func (g *ClientsGenerator) fieldType(parameter *concepts.Parameter) *TypeReferen case parameter.IsItems(): ref = g.types.ListReference(typ) case typ.IsScalar() || typ.IsStruct() || typ.IsList() || typ.IsMap(): - ref = g.types.NullableReference(typ) + ref = g.types.NullableReference(typ, "") } if ref == nil { g.reporter.Errorf( @@ -1091,13 +1091,13 @@ func (g *ClientsGenerator) accessorType(parameter *concepts.Parameter) *TypeRefe typ := parameter.Type() switch { case typ.IsList() && typ.Element().IsScalar(): - ref = g.types.NullableReference(typ) + ref = g.types.NullableReference(typ, "") case parameter.IsItems(): ref = g.types.ListReference(typ) case typ.IsScalar(): ref = g.types.ValueReference(typ) case typ.IsStruct() || typ.IsList() || typ.IsMap(): - ref = g.types.NullableReference(typ) + ref = g.types.NullableReference(typ, "") } if ref == nil { g.reporter.Errorf( diff --git a/pkg/generators/golang/json_generator.go b/pkg/generators/golang/json_generator.go index d4fe919..76ad8a8 100644 --- a/pkg/generators/golang/json_generator.go +++ b/pkg/generators/golang/json_generator.go @@ -173,25 +173,35 @@ func (g *JSONSupportGenerator) Run() error { } for _, typ := range version.Types() { for _, att := range typ.Attributes() { - if att.Type().Owner().Name() != version.Name() || - (att.Type().IsList() && att.Type().Element().Owner().Name() != version.Name()) { + // if att.Type().Owner().Name() != version.Name() || + // (att.Type().IsList() && att.Type().Element().Owner().Name() != version.Name()) { + // importRefs = append(importRefs, + // struct { + // path string + // selector string + // }{ + // path: g.packages.VersionImport(att.Type().Owner()), + // selector: g.packages.VersionSelector(att.Type().Owner()), + // }) + // } + if att.LinkOwner() != nil { importRefs = append(importRefs, struct { path string selector string }{ - path: g.packages.VersionImport(att.Type().Owner()), - selector: g.packages.VersionSelector(att.Type().Owner()), + path: g.packages.VersionImport(att.LinkOwner()), + selector: g.packages.VersionSelector(att.LinkOwner()), }) } } switch { case typ.IsStruct(): - err = g.generateStructTypeSupport(typ, importRefs, version) + err = g.generateStructTypeSupport(typ, importRefs) case typ.IsList(): element := typ.Element() if element.IsScalar() || element.IsStruct() { - err = g.generateListTypeSupport(typ, importRefs, version) + err = g.generateListTypeSupport(typ, importRefs) } } if err != nil { @@ -201,7 +211,7 @@ func (g *JSONSupportGenerator) Run() error { // Generate the code for the model methods: for _, resource := range version.Resources() { - err = g.generateResourceSupport(resource, version) + err = g.generateResourceSupport(resource) if err != nil { return err } @@ -451,8 +461,11 @@ func (g *JSONSupportGenerator) generateVersionMetadataSupport(version *concepts. Function("enumName", g.types.EnumName). Function("structName", g.types.StructReference). Function("generateReadValue", g.generateReadValue). + Function("selectorFromLinkOwner", g.selectorFromLinkOwner). Function("generateWriteValue", g.generateWriteValue). + Function("writeRefTypeFunc", g.writeRefTypeFunc). Function("readTypeFunc", g.readTypeFunc). + Function("readRefTypeFunc", g.readRefTypeFunc). Function("writeTypeFunc", g.writeTypeFunc). Function("valueReference", g.types.ValueReference). Build() @@ -532,8 +545,7 @@ func (g *JSONSupportGenerator) generateStructTypeSupport(typ *concepts.Type, importRefs []struct { path string selector string - }, - version *concepts.Version) error { + }) error { var err error // Calculate the package and file name: @@ -552,9 +564,12 @@ func (g *JSONSupportGenerator) generateStructTypeSupport(typ *concepts.Type, Function("fieldName", g.fieldName). Function("fieldTag", g.binding.AttributeName). Function("generateReadValue", g.generateReadValue). + Function("selectorFromLinkOwner", g.selectorFromLinkOwner). Function("generateWriteValue", g.generateWriteValue). + Function("writeRefTypeFunc", g.writeRefTypeFunc). Function("marshalTypeFunc", g.marshalTypeFunc). Function("readTypeFunc", g.readTypeFunc). + Function("readRefTypeFunc", g.readRefTypeFunc). Function("structName", g.types.StructReference). Function("unmarshalTypeFunc", g.unmarshalTypeFunc). Function("valueReference", g.types.ValueReference). @@ -569,14 +584,13 @@ func (g *JSONSupportGenerator) generateStructTypeSupport(typ *concepts.Type, } // Generate the code: - g.generateStructTypeSource(typ, version) + g.generateStructTypeSource(typ) // Write the generated code: return g.buffer.Write() } -func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type, - version *concepts.Version) { +func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) { g.buffer.Import("fmt", "") g.buffer.Import("io", "") g.buffer.Import("time", "") @@ -585,10 +599,9 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type, g.buffer.Emit(` {{ $structName := structName .Type }} {{ $marshalTypeFunc := marshalTypeFunc .Type }} - {{ $writeTypeFunc := writeTypeFunc .Type .Version }} + {{ $writeTypeFunc := writeTypeFunc .Type }} {{ $unmarshalTypeFunc := unmarshalTypeFunc .Type }} - {{ $readTypeFunc := readTypeFunc .Type .Version }} - {{ $version := .Version }} + {{ $readTypeFunc := readTypeFunc .Type }} // {{ $marshalTypeFunc }} writes a value of the '{{ .Type.Name }}' type to the given writer. func {{ $marshalTypeFunc }}(object *{{ $structName }}, writer io.Writer) error { @@ -649,7 +662,7 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type, stream.WriteMore() } stream.WriteObjectField("{{ $fieldTag }}") - {{ generateWriteValue (print "object." $fieldName) $version $v.Type $v.Link }} + {{ generateWriteValue (print "object." $fieldName) $v.Type $v.Link $v.LinkOwner }} {{ if lt $i (sub $n 1) }} count++ {{ end }} @@ -698,7 +711,7 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type, {{ $fieldTag := fieldTag . }} {{ $fieldMask := bitMask . }} case "{{ $fieldTag }}": - {{ generateReadValue "value" .Type $version .Link }} + {{ generateReadValue "value" .Type .Link .LinkOwner}} object.{{ $fieldName }} = value object.bitmap_ |= {{ $fieldMask }} {{ end }} @@ -710,7 +723,6 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type, } `, "Type", typ, - "Version", version, ) } @@ -718,8 +730,7 @@ func (g *JSONSupportGenerator) generateListTypeSupport(typ *concepts.Type, importRefs []struct { path string selector string - }, - version *concepts.Version) error { + }) error { var err error // Calculate the package and file name: @@ -735,9 +746,12 @@ func (g *JSONSupportGenerator) generateListTypeSupport(typ *concepts.Type, File(fileName). Function("enumName", g.types.EnumName). Function("generateReadValue", g.generateReadValue). + Function("selectorFromLinkOwner", g.selectorFromLinkOwner). Function("generateWriteValue", g.generateWriteValue). + Function("writeRefTypeFunc", g.writeRefTypeFunc). Function("marshalTypeFunc", g.marshalTypeFunc). Function("readTypeFunc", g.readTypeFunc). + Function("readRefTypeFunc", g.readRefTypeFunc). Function("structName", g.types.StructReference). Function("unmarshalTypeFunc", g.unmarshalTypeFunc). Function("valueReference", g.types.ValueReference). @@ -747,17 +761,20 @@ func (g *JSONSupportGenerator) generateListTypeSupport(typ *concepts.Type, return err } - // Generate the code: - g.generateListTypeSource(typ, version) + for _, ref := range importRefs { + g.buffer.Import(ref.path, ref.selector) + } + // Generate the code: + g.generateListTypeSource(typ) // Write the generated code: return g.buffer.Write() } func (g *JSONSupportGenerator) generateListTypeSource( typ *concepts.Type, - version *concepts.Version, ) { + var linkOwner *concepts.Version g.buffer.Import("fmt", "") g.buffer.Import(g.packages.HelpersImport(), "") g.buffer.Import("github.com/json-iterator/go", "jsoniter") @@ -765,9 +782,9 @@ func (g *JSONSupportGenerator) generateListTypeSource( {{ $structName := structName .Type }} {{ $sliceType := valueReference .Type }} {{ $marshalTypeFunc := marshalTypeFunc .Type }} - {{ $writeTypeFunc := writeTypeFunc .Type .Version }} + {{ $writeTypeFunc := writeTypeFunc .Type }} {{ $unmarshalTypeFunc := unmarshalTypeFunc .Type }} - {{ $readTypeFunc := readTypeFunc .Type .Version }} + {{ $readTypeFunc := readTypeFunc .Type }} // {{ $marshalTypeFunc }} writes a list of values of the '{{ .Type.Element.Name }}' type to // the given writer. @@ -789,7 +806,7 @@ func (g *JSONSupportGenerator) generateListTypeSource( if i > 0 { stream.WriteMore() } - {{ generateWriteValue "value" .Version .Type.Element false }} + {{ generateWriteValue "value" .Type.Element false .linkOwner }} } stream.WriteArrayEnd() } @@ -811,19 +828,18 @@ func (g *JSONSupportGenerator) generateListTypeSource( func {{ $readTypeFunc }}(iterator *jsoniter.Iterator) {{ $sliceType }} { list := {{ valueReference .Type }}{} for iterator.ReadArray() { - {{ generateReadValue "item" .Type.Element .Version false }} + {{ generateReadValue "item" .Type.Element false .linkOwner }} list = append(list, item) } return list } `, "Type", typ, - "Version", version, + "linkOwner", linkOwner, ) } -func (g *JSONSupportGenerator) generateResourceSupport(resource *concepts.Resource, - version *concepts.Version) error { +func (g *JSONSupportGenerator) generateResourceSupport(resource *concepts.Resource) error { var err error // Calculate the package and file name: @@ -844,13 +860,16 @@ func (g *JSONSupportGenerator) generateResourceSupport(resource *concepts.Resour Function("generateReadBodyParameter", g.generateReadBodyParameter). Function("generateReadQueryParameter", g.generateReadQueryParameter). Function("generateReadValue", g.generateReadValue). + Function("selectorFromLinkOwner", g.selectorFromLinkOwner). Function("generateWriteBodyParameter", g.generateWriteBodyParameter). Function("generateWriteValue", g.generateWriteValue). + Function("writeRefTypeFunc", g.writeRefTypeFunc). Function("marshalTypeFunc", g.marshalTypeFunc). Function("parameterFieldName", g.parameterFieldName). Function("parameterQueryName", g.binding.QueryParameterName). Function("readResponseFunc", g.readResponseFunc). Function("readTypeFunc", g.readTypeFunc). + Function("readRefTypeFunc", g.readRefTypeFunc). Function("requestBodyParameters", g.binding.RequestBodyParameters). Function("requestQueryParameters", g.binding.RequestQueryParameters). Function("responseBodyParameters", g.binding.ResponseParameters). @@ -866,14 +885,14 @@ func (g *JSONSupportGenerator) generateResourceSupport(resource *concepts.Resour // Generate the code: for _, method := range resource.Methods() { - g.generateMethodSource(method, version) + g.generateMethodSource(method) } // Write the generated code: return g.buffer.Write() } -func (g *JSONSupportGenerator) generateMethodSource(method *concepts.Method, version *concepts.Version) { +func (g *JSONSupportGenerator) generateMethodSource(method *concepts.Method) { switch { case method.IsAdd(): g.generateAddMethodSource(method) @@ -890,7 +909,7 @@ func (g *JSONSupportGenerator) generateMethodSource(method *concepts.Method, ver case method.IsUpdate(): g.generateUpdateMethodSource(method) case method.IsAction(): - g.generateActionMethodSource(method, version) + g.generateActionMethodSource(method) default: g.reporter.Errorf( "Don't know how to generate encoding/decoding code for method '%s'", @@ -969,6 +988,7 @@ func (g *JSONSupportGenerator) generateListMethodSource(method *concepts.Method) var total *concepts.Parameter var items *concepts.Parameter var other []*concepts.Parameter + var linkOwner *concepts.Version for _, parameter := range method.Parameters() { switch { case parameter.Name().Equals(nomenclator.Page): @@ -1004,21 +1024,21 @@ func (g *JSONSupportGenerator) generateListMethodSource(method *concepts.Method) } switch field { {{ if .Page }} - {{ generateReadBodyParameter "response" .Version .Page }} + {{ generateReadBodyParameter "response" .Page }} {{ end }} {{ if .Size }} - {{ generateReadBodyParameter "response" .Version .Size }} + {{ generateReadBodyParameter "response" .Size }} {{ end }} {{ if .Total }} - {{ generateReadBodyParameter "response" .Version .Total }} + {{ generateReadBodyParameter "response" .Total }} {{ end }} {{ range .Other }} {{ if .Out }} - {{ generateReadBodyParameter "response" .Version . }} + {{ generateReadBodyParameter "response" . }} {{ end }} {{ end }} case "items": - {{ generateReadValue "items" .Items.Type .Version false }} + {{ generateReadValue "items" .Items.Type false .LinkOwner }} {{ if and .Items.Type.IsList .Items.Type.Element.IsScalar }} response.items = items {{ else }} @@ -1033,13 +1053,13 @@ func (g *JSONSupportGenerator) generateListMethodSource(method *concepts.Method) return iterator.Error } `, - "Version", method.Owner().Owner(), "Method", method, "Page", page, "Size", size, "Total", total, "Items", items, "Other", other, + "LinkOwner", linkOwner, ) } @@ -1094,6 +1114,7 @@ func (g *JSONSupportGenerator) generateSearchMethodSource(method *concepts.Metho var body *concepts.Parameter var items *concepts.Parameter var other []*concepts.Parameter + var linkOwner *concepts.Version for _, parameter := range method.Parameters() { switch { case parameter.Name().Equals(nomenclator.Page): @@ -1115,7 +1136,6 @@ func (g *JSONSupportGenerator) generateSearchMethodSource(method *concepts.Metho g.buffer.Import("net/http", "") g.buffer.Import(g.packages.HelpersImport(), "") g.buffer.Emit(` - {{ $version := .Version }} func {{ writeRequestFunc .Method }}(request *{{ clientRequestName .Method }}, writer io.Writer) error { {{ if .Body }} return {{ marshalTypeFunc .Body.Type }}(request.{{ parameterFieldName .Body }}, writer) @@ -1136,21 +1156,21 @@ func (g *JSONSupportGenerator) generateSearchMethodSource(method *concepts.Metho } switch field { {{ if .Page }} - {{ generateReadBodyParameter "response" $version .Page }} + {{ generateReadBodyParameter "response" .Page }} {{ end }} {{ if .Size }} - {{ generateReadBodyParameter "response" $version .Size }} + {{ generateReadBodyParameter "response" .Size }} {{ end }} {{ if .Total }} - {{ generateReadBodyParameter "response" $version .Total }} + {{ generateReadBodyParameter "response" .Total }} {{ end }} {{ range .Other }} {{ if .Out }} - {{ generateReadBodyParameter "response" $version . }} + {{ generateReadBodyParameter "response" . }} {{ end }} {{ end }} case "items": - {{ generateReadValue "items" .Items.Type $version false }} + {{ generateReadValue "items" .Items.Type false .LinkOwner }} {{ if and .Items.Type.IsList .Items.Type.Element.IsScalar }} response.items = items {{ else }} @@ -1173,6 +1193,7 @@ func (g *JSONSupportGenerator) generateSearchMethodSource(method *concepts.Metho "Body", body, "Items", items, "Other", other, + "LinkOwner", linkOwner, ) } @@ -1199,21 +1220,19 @@ func (g *JSONSupportGenerator) generateUpdateMethodSource(method *concepts.Metho ) } -func (g *JSONSupportGenerator) generateActionMethodSource(method *concepts.Method, - version *concepts.Version) { +func (g *JSONSupportGenerator) generateActionMethodSource(method *concepts.Method) { g.buffer.Import("net/http", "") g.buffer.Import(g.packages.HelpersImport(), "") g.buffer.Emit(` {{ $requestBodyParameters := requestBodyParameters .Method }} {{ $responseBodyParameters := responseBodyParameters .Method }} - {{ $version := .Version }} func {{ writeRequestFunc .Method }}(request *{{ clientRequestName .Method }}, writer io.Writer) error { {{ if $requestBodyParameters }} count := 0 stream := helpers.NewStream(writer) stream.WriteObjectStart() {{ range $requestBodyParameters }} - {{ generateWriteBodyParameter "request" $version . }} + {{ generateWriteBodyParameter "request" . }} {{ end }} stream.WriteObjectEnd() err := stream.Flush() @@ -1239,7 +1258,7 @@ func (g *JSONSupportGenerator) generateActionMethodSource(method *concepts.Metho } switch field { {{ range $responseBodyParameters }} - {{ generateReadBodyParameter "response" $version . }} + {{ generateReadBodyParameter "response" . }} {{ end }} default: iterator.ReadAny() @@ -1252,7 +1271,6 @@ func (g *JSONSupportGenerator) generateActionMethodSource(method *concepts.Metho } `, "Method", method, - "Version", version, ) } @@ -1291,14 +1309,14 @@ func (g *JSONSupportGenerator) generateReadQueryParameter(parameter *concepts.Pa } func (g *JSONSupportGenerator) generateReadBodyParameter(object string, - version *concepts.Version, parameter *concepts.Parameter) string { field := g.parameterFieldName(parameter) tag := g.binding.BodyParameterName(parameter) typ := parameter.Type() + var linkOwner *concepts.Version return g.buffer.Eval(` case "{{ .Tag }}": - {{ generateReadValue "value" .Type .Version false }} + {{ generateReadValue "value" .Type false .LinkOwner }} {{ if .Type.IsScalar }} {{ .Object }}.{{ .Field }} = &value {{ else }} @@ -1309,13 +1327,13 @@ func (g *JSONSupportGenerator) generateReadBodyParameter(object string, "Field", field, "Tag", tag, "Type", typ, - "Version", version, + "LinkOwner", linkOwner, ) } func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts.Type, - version *concepts.Version, - link bool) string { + link bool, + linkOwner *concepts.Version) string { g.buffer.Import("time", "") return g.buffer.Eval(` {{ if .Type.IsBoolean }} @@ -1341,11 +1359,12 @@ func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts. text := iterator.ReadString() {{ .Variable }} := {{ enumName .Type }}(text) {{ else if .Type.IsStruct }} - {{ .Variable }} := {{ readTypeFunc .Type .Version }}(iterator) + {{ .Variable }} := {{ readRefTypeFunc .Type .LinkOwner }}(iterator) {{ else if .Type.IsList }} {{ if .Link }} + {{ $selectorFromLinkOwner := selectorFromLinkOwner .LinkOwner}} {{ $structName := structName .Type }} - {{ .Variable }} := &{{ $structName }}{} + {{ .Variable }} := &{{ $selectorFromLinkOwner }}{{ $structName }}{} for { field := iterator.ReadObject() if field == "" { @@ -1354,17 +1373,17 @@ func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts. switch field { case "kind": text := iterator.ReadString() - {{ .Variable }}.SetLink(text == {{ $structName }}LinkKind) + {{ .Variable }}.SetLink(text == {{ $selectorFromLinkOwner }}{{ $structName }}LinkKind) case "href": {{ .Variable }}.SetHREF(iterator.ReadString()) case "items": - {{ .Variable }}.SetItems({{ readTypeFunc .Type .Version }}(iterator)) + {{ .Variable }}.SetItems({{ readRefTypeFunc .Type .LinkOwner }}(iterator)) default: iterator.ReadAny() } } {{ else }} - {{ .Variable }} := {{ readTypeFunc .Type .Version }}(iterator) + {{ .Variable }} := {{ readTypeFunc .Type }}(iterator) {{ end }} {{ else if .Type.IsMap }} {{ .Variable }} := {{ valueReference .Type }}{} @@ -1373,7 +1392,7 @@ func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts. if key == "" { break } - {{ generateReadValue "item" .Type.Element .Version false }} + {{ generateReadValue "item" .Type.Element false .LinkOwner }} {{ .Variable }}[key] = item } {{ else }} @@ -1383,18 +1402,18 @@ func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts. "Variable", variable, "Type", typ, "Link", link, - "Version", version, + "LinkOwner", linkOwner, ) } func (g *JSONSupportGenerator) generateWriteBodyParameter(object string, - version *concepts.Version, parameter *concepts.Parameter, ) string { typ := parameter.Type() field := g.parameterFieldName(parameter) tag := g.binding.BodyParameterName(parameter) var value string + var linkOwner *concepts.Version if typ.IsScalar() && !typ.IsInterface() { value = g.buffer.Eval( `*{{ .Object }}.{{ .Field }}`, @@ -1414,7 +1433,7 @@ func (g *JSONSupportGenerator) generateWriteBodyParameter(object string, stream.WriteMore() } stream.WriteObjectField("{{ .Tag }}") - {{ generateWriteValue .Value .Version .Type false }} + {{ generateWriteValue .Value .Type false .LinkOwner }} count++ } `, @@ -1423,14 +1442,14 @@ func (g *JSONSupportGenerator) generateWriteBodyParameter(object string, "Tag", tag, "Value", value, "Type", typ, - "Version", version, + "LinkOwner", linkOwner, ) } func (g *JSONSupportGenerator) generateWriteValue(value string, - version *concepts.Version, typ *concepts.Type, - link bool) string { + link bool, + linkOwner *concepts.Version) string { g.buffer.Import("sort", "") g.buffer.Import("time", "") return g.buffer.Eval(` @@ -1451,15 +1470,15 @@ func (g *JSONSupportGenerator) generateWriteValue(value string, {{ else if .Type.IsInterface }} stream.WriteVal({{ .Value }}) {{ else if .Type.IsStruct }} - {{ writeTypeFunc .Type .Version }}({{ .Value }}, stream) + {{ writeRefTypeFunc .Type .LinkOwner }}({{ .Value}}, stream) {{ else if .Type.IsList }} {{ if .Link }} stream.WriteObjectStart() stream.WriteObjectField("items") - {{ writeTypeFunc .Type .Version }}({{ .Value }}.Items(), stream) + {{ writeRefTypeFunc .Type .LinkOwner }}({{ .Value }}.Items(), stream) stream.WriteObjectEnd() {{ else }} - {{ writeTypeFunc .Type .Version }}({{ .Value }}, stream) + {{ writeTypeFunc .Type }}({{ .Value }}, stream) {{ end }} {{ else if .Type.IsMap }} if {{ .Value }} != nil { @@ -1477,7 +1496,7 @@ func (g *JSONSupportGenerator) generateWriteValue(value string, } item := {{ .Value }}[key] stream.WriteObjectField(key) - {{ generateWriteValue "item" .Version .Type.Element false }} + {{ generateWriteValue "item" .Type.Element false .LinkOwner}} } stream.WriteObjectEnd() } else { @@ -1488,7 +1507,7 @@ func (g *JSONSupportGenerator) generateWriteValue(value string, "Value", value, "Type", typ, "Link", link, - "Version", version, + "LinkOwner", linkOwner, ) } @@ -1517,11 +1536,16 @@ func (g *JSONSupportGenerator) marshalTypeFunc(typ *concepts.Type) string { return name } -func (g *JSONSupportGenerator) writeTypeFunc(typ *concepts.Type, version *concepts.Version) string { - _, selector := g.types.Package(typ) +func (g *JSONSupportGenerator) writeTypeFunc(typ *concepts.Type) string { name := names.Cat(nomenclator.Write, typ.Name()) - if selector != g.types.packages.VersionSelector(version) { - return fmt.Sprintf("%s.%s", selector, g.names.Public(name)) + return g.names.Public(name) +} + +func (g *JSONSupportGenerator) writeRefTypeFunc(typ *concepts.Type, refVersion *concepts.Version) string { + name := names.Cat(nomenclator.Write, typ.Name()) + if refVersion != nil { + version := g.packages.VersionSelector(refVersion) + return fmt.Sprintf("%s.%s", version, g.names.Public(name)) } return g.names.Public(name) } @@ -1535,11 +1559,16 @@ func (g *JSONSupportGenerator) unmarshalTypeFunc(typ *concepts.Type) string { return name } -func (g *JSONSupportGenerator) readTypeFunc(typ *concepts.Type, version *concepts.Version) string { - _, selector := g.types.Package(typ) +func (g *JSONSupportGenerator) readTypeFunc(typ *concepts.Type) string { + name := names.Cat(nomenclator.Read, typ.Name()) + return g.names.Public(name) +} + +func (g *JSONSupportGenerator) readRefTypeFunc(typ *concepts.Type, refVersion *concepts.Version) string { name := names.Cat(nomenclator.Read, typ.Name()) - if selector != g.types.packages.VersionSelector(version) { - return fmt.Sprintf("%s.%s", selector, g.names.Public(name)) + if refVersion != nil { + version := g.packages.VersionSelector(refVersion) + return fmt.Sprintf("%s.%s", version, g.names.Public(name)) } return g.names.Public(name) } @@ -1656,3 +1685,10 @@ func (g *JSONSupportGenerator) defaultValue(parameter *concepts.Parameter) strin return "" } } + +func (g JSONSupportGenerator) selectorFromLinkOwner(linkOwner *concepts.Version) string { + if linkOwner == nil { + return "" + } + return fmt.Sprintf("%s.", g.packages.VersionSelector(linkOwner)) +} diff --git a/pkg/generators/golang/types_calculator.go b/pkg/generators/golang/types_calculator.go index f8f84be..1e11bac 100644 --- a/pkg/generators/golang/types_calculator.go +++ b/pkg/generators/golang/types_calculator.go @@ -143,7 +143,7 @@ func (c *TypesCalculator) StructReference(typ *concepts.Type) *TypeReference { ref.name = c.names.Public(element.Name()) } ref.name += "List" - ref.text = fmt.Sprintf("%s.%s", ref.selector, ref.name) + ref.text = ref.name case typ.IsStruct(): ref = &TypeReference{} ref.imprt, ref.selector = c.Package(typ) @@ -151,7 +151,7 @@ func (c *TypesCalculator) StructReference(typ *concepts.Type) *TypeReference { if ref.name == "" { ref.name = c.names.Public(typ.Name()) } - ref.text = fmt.Sprintf("%s.%s", ref.selector, ref.name) + ref.text = ref.name default: c.reporter.Errorf( "Don't know how to calculate struct type reference for type '%s'", @@ -247,7 +247,7 @@ func (c *TypesCalculator) ValueReference(typ *concepts.Type) *TypeReference { // NullableReference calculates a type reference for a value of the given type that can be assigned // the nil value. -func (c *TypesCalculator) NullableReference(typ *concepts.Type) *TypeReference { +func (c *TypesCalculator) NullableReference(typ *concepts.Type, referencedVersion string) *TypeReference { switch { case (typ.IsScalar() && !typ.IsInterface()): ref := c.ValueReference(typ) @@ -255,7 +255,11 @@ func (c *TypesCalculator) NullableReference(typ *concepts.Type) *TypeReference { return ref case typ.IsStruct(): ref := c.ValueReference(typ) - ref.text = fmt.Sprintf("*%s.%s", ref.selector, ref.name) + if referencedVersion != "" { + ref.text = fmt.Sprintf("*%s.%s", referencedVersion, ref.name) + } else { + ref.text = fmt.Sprintf("*%s.%s", ref.selector, ref.name) + } return ref default: return c.ValueReference(typ) @@ -297,12 +301,12 @@ func (c *TypesCalculator) JSONTypeReference(typ *concepts.Type) *TypeReference { var ref *TypeReference switch { case typ.IsScalar(): - ref = c.NullableReference(typ) + ref = c.NullableReference(typ, "") case typ.IsList(): element := typ.Element() switch { case element.IsScalar(): - ref = c.NullableReference(typ) + ref = c.NullableReference(typ, "") case element.IsStruct(): ref = c.JSONTypeReference(element) ref.text = fmt.Sprintf("[]%s", ref.text) @@ -311,7 +315,7 @@ func (c *TypesCalculator) JSONTypeReference(typ *concepts.Type) *TypeReference { element := typ.Element() switch { case element.IsScalar(): - ref = c.NullableReference(typ) + ref = c.NullableReference(typ, "") case element.IsStruct(): ref = c.JSONTypeReference(element) ref.text = fmt.Sprintf("map[string]%s", ref.text) @@ -359,7 +363,7 @@ func (c *TypesCalculator) JSONStructReference(typ *concepts.Type) *TypeReference } // Builder reference calculates a reference for the type used build objects of the given type. -func (c *TypesCalculator) BuilderReference(typ *concepts.Type) *TypeReference { +func (c *TypesCalculator) BuilderReference(typ *concepts.Type, refVersion string) *TypeReference { var ref *TypeReference switch { case typ.IsStruct(): @@ -370,7 +374,11 @@ func (c *TypesCalculator) BuilderReference(typ *concepts.Type) *TypeReference { ref.name = c.names.Public(typ.Name()) } ref.name += "Builder" - ref.text = fmt.Sprintf("*%s.%s", ref.selector, ref.name) + if refVersion == "" { + ref.text = fmt.Sprintf("*%s.%s", ref.selector, ref.name) + } else { + ref.text = fmt.Sprintf("*%s.%s", refVersion, ref.name) + } case typ.IsList(): element := typ.Element() ref = &TypeReference{} @@ -380,7 +388,11 @@ func (c *TypesCalculator) BuilderReference(typ *concepts.Type) *TypeReference { ref.name = c.names.Public(element.Name()) } ref.name += "ListBuilder" - ref.text = fmt.Sprintf("*%s.%s", ref.selector, ref.name) + if refVersion == "" { + ref.text = fmt.Sprintf("*%s.%s", ref.selector, ref.name) + } else { + ref.text = fmt.Sprintf("*%s.%s", refVersion, ref.name) + } default: c.reporter.Errorf( "Don't know how to calculate builder reference for type '%s'", diff --git a/pkg/generators/golang/types_generator.go b/pkg/generators/golang/types_generator.go index 4c4c383..f62720c 100644 --- a/pkg/generators/golang/types_generator.go +++ b/pkg/generators/golang/types_generator.go @@ -360,7 +360,7 @@ func (g *TypesGenerator) generateStructTypeSource(typ *concepts.Type) { return {{ $objectName }}Kind } - // Link returns true iif this is a link. + // Link returns true if this is a link. func (o *{{ $objectName }}) Link() bool { return o != nil && o.bitmap_&1 != 0 } @@ -609,19 +609,23 @@ func (g *TypesGenerator) fieldName(attribute *concepts.Attribute) string { func (g *TypesGenerator) getterType(attribute *concepts.Attribute) *TypeReference { var ref *TypeReference typ := attribute.Type() + referencedVersion := "" + if attribute.LinkOwner() != nil { + referencedVersion = attribute.LinkOwner().Name().String() + } switch { case typ.IsScalar(): ref = g.types.ValueReference(typ) case typ.IsStruct(): - ref = g.types.NullableReference(typ) + ref = g.types.NullableReference(typ, referencedVersion) case typ.IsList(): if attribute.Link() { ref = g.types.ListReference(typ) } else { - ref = g.types.NullableReference(typ) + ref = g.types.NullableReference(typ, referencedVersion) } case typ.IsMap(): - ref = g.types.NullableReference(typ) + ref = g.types.NullableReference(typ, referencedVersion) } if ref == nil { g.reporter.Errorf( @@ -668,19 +672,23 @@ func (g *TypesGenerator) getterName(attribute *concepts.Attribute) string { func (g *TypesGenerator) fieldType(attribute *concepts.Attribute) *TypeReference { var ref *TypeReference typ := attribute.Type() + referencedVersion := "" + if attribute.LinkOwner() != nil { + referencedVersion = attribute.LinkOwner().Name().String() + } switch { case typ.IsScalar(): ref = g.types.ValueReference(typ) case typ.IsStruct(): - ref = g.types.NullableReference(typ) + ref = g.types.NullableReference(typ, referencedVersion) case typ.IsList(): if attribute.Link() { ref = g.types.ListReference(typ) } else { - ref = g.types.NullableReference(typ) + ref = g.types.NullableReference(typ, referencedVersion) } case typ.IsMap(): - ref = g.types.NullableReference(typ) + ref = g.types.NullableReference(typ, referencedVersion) } if ref == nil { g.reporter.Errorf( @@ -699,3 +707,10 @@ func (g *TypesGenerator) listName(typ *concepts.Type) string { } return typeName + "List" } + +func (g *TypesGenerator) versionSelector(attribute *concepts.Attribute) string { + if attribute.LinkOwner() == nil { + return "" + } + return fmt.Sprintf("*%s.", g.packages.VersionSelector(attribute.LinkOwner())) +} diff --git a/pkg/language/reader.go b/pkg/language/reader.go index 95ad2c6..9107f4d 100644 --- a/pkg/language/reader.go +++ b/pkg/language/reader.go @@ -474,6 +474,7 @@ func (r *Reader) handleClassRef(typ *concepts.Type, path string) { // Once loading the service, we find the reference type // then recursively iterate the type tree and add the types to the current version. if referencedType := refVersion.FindType(names.ParseUsingSeparator(referencedTypeName, "_")); referencedType != nil { + r.version.AddType(referencedType) r.recursivelyAddTypeToVersion(typ, referencedType) } } @@ -489,20 +490,21 @@ func (r *Reader) recursivelyAddTypeToVersion(currType *concepts.Type, // We need to check if the type was previously introduced // in that case we would simply changes the owner of the attribue // as we had already compiled it in this version. - if attribute.Type().IsList() { - if r.version.FindType(attribute.Type().Element().Name()) == nil { - r.version.AddTypeWithoutOwner(attribute.Type()) - r.version.AddTypeWithoutOwner(attribute.Type().Element()) - } else { - elementOwner := r.version.FindType(attribute.Type().Element().Name()).Owner() - if attribute.Type().Owner() != elementOwner { - attribute.Type().SetOwner(elementOwner) - attribute.Type().Element().SetOwner(elementOwner) - } - } - } else if r.version.FindType(attribute.Type().Name()) == nil { - r.version.AddTypeWithoutOwner(attribute.Type()) - } + // if attribute.Type().IsList() { + // if r.version.FindType(attribute.Type().Element().Name()) == nil { + // r.version.AddTypeWithoutOwner(attribute.Type()) + // r.version.AddTypeWithoutOwner(attribute.Type().Element()) + // } else { + // elementOwner := r.version.FindType(attribute.Type().Element().Name()).Owner() + // if attribute.Type().Owner() != elementOwner { + // attribute.Type().SetOwner(elementOwner) + // attribute.Type().Element().SetOwner(elementOwner) + // } + // } + // } else if r.version.FindType(attribute.Type().Name()) == nil { + // r.version.AddTypeWithoutOwner(attribute.Type()) + // } + attribute.SetLinkOwner(attribute.Type().Owner()) } else if attribute.Type().IsList() || attribute.Type().IsMap() { r.version.AddType(attribute.Type()) r.recursivelyAddTypeToVersion(currType, attribute.Type().Element()) @@ -510,7 +512,9 @@ func (r *Reader) recursivelyAddTypeToVersion(currType *concepts.Type, r.recursivelyAddTypeToVersion(currType, attribute.Type()) } } - r.version.AddType(referencedType) + if r.version.FindType(referencedType.Name()) == nil { + r.version.AddType(referencedType) + } } func (r *Reader) ExitStructDecl(ctx *StructDeclContext) { diff --git a/pkg/language/ref_test.go b/pkg/language/ref_test.go index 351be31..426a808 100644 --- a/pkg/language/ref_test.go +++ b/pkg/language/ref_test.go @@ -202,7 +202,7 @@ var _ = Describe("Read Model with ref annotation", func() { attributeList := class.FindAttribute(names.ParseUsingCase("Foo")) Expect(attributeList).ToNot(BeNil()) Expect(attributeList.Type().IsList()).To(BeTrue()) - Expect(attributeList.Type().Owner().Name().String()).To(Equal("v1_alpha1")) + Expect(attributeList.Type().Owner().Name().String()).To(Equal("v1")) Expect(attributeList.Type().Element().Owner().Name().String()).To(Equal("v1_alpha1")) barType := version.FindType(names.ParseUsingCase("Bar")) Expect(barType.Owner().Name().String()).To(Equal("v1_alpha1")) @@ -260,7 +260,7 @@ var _ = Describe("Read Model with ref annotation", func() { attributeList := class.FindAttribute(names.ParseUsingCase("Foo")) Expect(attributeList).ToNot(BeNil()) Expect(attributeList.Type().IsList()).To(BeTrue()) - Expect(attributeList.Type().Owner().Name().String()).To(Equal("v1_alpha1")) + Expect(attributeList.Type().Owner().Name().String()).To(Equal("v1")) Expect(attributeList.Type().Element().Owner().Name().String()).To(Equal("v1_alpha1")) myAttributeType := version.FindType(names.ParseUsingCase("MyAttribute")) Expect(myAttributeType).ToNot(BeNil()) @@ -322,7 +322,7 @@ var _ = Describe("Read Model with ref annotation", func() { attributeList := class.FindAttribute(names.ParseUsingCase("Foo")) Expect(attributeList).ToNot(BeNil()) Expect(attributeList.Type().IsList()).To(BeTrue()) - Expect(attributeList.Type().Owner().Name().String()).To(Equal("v1_alpha1")) + Expect(attributeList.Type().Owner().Name().String()).To(Equal("v1")) Expect(attributeList.Type().Element().Owner().Name().String()).To(Equal("v1_alpha1")) myAttributeType := version.FindType(names.ParseUsingCase("MyAttribute")) Expect(myAttributeType).ToNot(BeNil()) @@ -383,12 +383,69 @@ var _ = Describe("Read Model with ref annotation", func() { attributeList := class.FindAttribute(names.ParseUsingCase("Foo")) Expect(attributeList).ToNot(BeNil()) Expect(attributeList.Type().IsList()).To(BeTrue()) - Expect(attributeList.Type().Owner().Name().String()).To(Equal("v1_alpha1")) - Expect(attributeList.Type().Element().Owner().Name().String()).To(Equal("v1_alpha1")) + Expect(attributeList.Type().Owner().Name().String()).To(Equal("v1")) + Expect(attributeList.Type().Element().Owner().Name().String()).To(Equal("v1")) myAttributeType := version.FindType(names.ParseUsingCase("MyAttribute")) Expect(myAttributeType).ToNot(BeNil()) Expect(myAttributeType.Owner().Name().String()).To(Equal("v1_alpha1")) + attributeGoo := class.FindAttribute(names.ParseUsingCase("Goo")) + Expect(attributeGoo).ToNot(BeNil()) + Expect(attributeGoo.LinkOwner().Name().String).To(Equal("v1")) barType := version.FindType(names.ParseUsingCase("Bar")) Expect(barType.Owner().Name().String()).To(Equal("v1")) }) + + It("Link referenced should point to the version referenced", func() { + model := MakeModel( + "my_service/v1_alpha1/root.model", + ` + resource Root { + } + `, + "my_service/v1_alpha1/my_class.model", + ` + @ref(path="other_service/v1/my_class") + class MyClass { + } + `, + "other_service/v1/root.model", + ` + resource Root{ + } + `, + "other_service/v1/my_class.model", + ` + class MyClass { + link Foo []MyAttribute + Loo MyAttribute + }`, + "other_service/v1/my_attribute.model", + ` + class MyAttribute{ + Goo []String + } + `, + ) + // Check the attribute and its owner + service := model.FindService(names.ParseUsingSeparator("my_service", "_")) + Expect(service).ToNot(BeNil()) + version := service.FindVersion(names.ParseUsingSeparator("v1_alpha1", "_")) + Expect(version).ToNot(BeNil()) + class := version.FindType(names.ParseUsingCase("MyClass")) + Expect(class).ToNot(BeNil()) + Expect(class.Owner().Name().String()).To(Equal("v1_alpha1")) + attributeList := class.FindAttribute(names.ParseUsingCase("Foo")) + Expect(attributeList).ToNot(BeNil()) + Expect(attributeList.Type().IsList()).To(BeTrue()) + Expect(attributeList.LinkOwner().Name().String()).To(Equal("v1")) + Expect(attributeList.Type().Owner().Name().String()).To(Equal("v1")) + Expect(attributeList.Type().Element().Owner().Name().String()).To(Equal("v1_alpha1")) + myAttributeType := version.FindType(names.ParseUsingCase("MyAttribute")) + Expect(myAttributeType).ToNot(BeNil()) + Expect(myAttributeType.Owner().Name().String()).To(Equal("v1_alpha1")) + myAttribute := class.FindAttribute(names.ParseUsingCase("Loo")) + Expect(myAttribute.LinkOwner()).To(BeNil()) + Expect(myAttribute.Type().Owner().Name().String()).To(Equal("v1_alpha1")) + }) + })