diff --git a/pkg/annotations/annotations.go b/pkg/annotations/annotations.go index 6164a62..e4bd397 100644 --- a/pkg/annotations/annotations.go +++ b/pkg/annotations/annotations.go @@ -63,3 +63,17 @@ func GoName(concept concepts.Annotated) string { } return fmt.Sprintf("%s", name) } + +// Reference checks if the given concept has a `reference` annotation. If it has it then it returns the value +// of the `path` parameter. It returns an empty string if there is no such annotation or parameter. +func ReferencePath(concept concepts.Annotated) string { + annotation := concept.GetAnnotation("ref") + if annotation == nil { + return "" + } + name := annotation.FindParameter("path") + if name == nil { + return "" + } + return fmt.Sprintf("%s", name) +} diff --git a/pkg/concepts/type.go b/pkg/concepts/type.go index e54dee6..8a67a6b 100644 --- a/pkg/concepts/type.go +++ b/pkg/concepts/type.go @@ -164,6 +164,14 @@ func (t *Type) IsScalar() bool { return t.kind == ScalarType || t.kind == EnumType || t.kind == InterfaceType } +func (t *Type) IsBasicType() bool { + if t == nil { + return false + } + + return t.kind == ScalarType +} + // IsStruct returns true iff this type is an struct type. Note that class types are also considered // struct types. func (t *Type) IsStruct() bool { diff --git a/pkg/concepts/version.go b/pkg/concepts/version.go index 603548d..63500c8 100644 --- a/pkg/concepts/version.go +++ b/pkg/concepts/version.go @@ -106,6 +106,15 @@ func (v *Version) AddType(typ *Type) { } } +// AddTypeWithoutOwner adds the given type to the version *without* changing +// its owner. This is crucial when were adding references to other types from different +// versions. +func (v *Version) AddTypeWithoutOwner(typ *Type) { + if typ != nil { + v.types[typ.Name().String()] = typ + } +} + // AddTypes adds the given types to the version. func (v *Version) AddTypes(types []*Type) { for _, typ := range types { diff --git a/pkg/generators/golang/builders_generator.go b/pkg/generators/golang/builders_generator.go index ebec888..91ef193 100644 --- a/pkg/generators/golang/builders_generator.go +++ b/pkg/generators/golang/builders_generator.go @@ -189,6 +189,7 @@ func (g *BuildersGenerator) generateStructBuilderFile(typ *concepts.Type) error Function("builderName", g.builderName). Function("fieldName", g.fieldName). Function("fieldType", g.fieldType). + Function("selectorType", g.selectorType). Function("objectName", g.objectName). Function("setterName", g.setterName). Function("setterType", g.setterType). @@ -274,6 +275,7 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) { {{ $setterName := setterName . }} {{ $setterType := setterType . }} {{ $fieldMask := bitMask . }} + {{ $selectorType := selectorType . }} {{ if .Type.IsList }} // {{ $setterName }} sets the value of the '{{ .Name }}' attribute to the given values. @@ -296,8 +298,8 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) { } {{ else }} {{ $elementBuilderName := builderName .Type.Element }} - func (b *{{ $builderName }}) {{ $setterName }}(values ...*{{ $elementBuilderName }}) *{{ $builderName }} { - b.{{ $fieldName }} = make([]*{{ $elementBuilderName }}, len(values)) + func (b *{{ $builderName }}) {{ $setterName }}(values ...*{{ selectorType . }}{{ $elementBuilderName }}) *{{ $builderName }} { + b.{{ $fieldName }} = make([]*{{ selectorType . }}{{ $elementBuilderName }}, len(values)) copy(b.{{ $fieldName }}, values) b.bitmap_ |= {{ $fieldMask }} return b @@ -337,26 +339,27 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) { {{ range .Type.Attributes }} {{ $fieldName := fieldName . }} {{ $fieldType := fieldType . }} + {{ $selectorType := selectorType . }} {{ if .Type.IsScalar }} b.{{ $fieldName }} = object.{{ $fieldName }} {{ else if .Type.IsStruct }} if object.{{ $fieldName }} != nil { - b.{{ $fieldName }} = {{ builderCtor .Type }}().Copy(object.{{ $fieldName }}) + b.{{ $fieldName }} = {{ selectorType . }}{{ builderCtor .Type }}().Copy(object.{{ $fieldName }}) } else { b.{{ $fieldName }} = nil } {{ else if .Type.IsList }} if object.{{ $fieldName }} != nil { {{ if .Link }} - b.{{ $fieldName }} = {{ builderCtor .Type }}().Copy(object.{{ $fieldName }}) + b.{{ $fieldName }} = {{ selectorType . }}{{ builderCtor .Type }}().Copy(object.{{ $fieldName }}) {{ else }} {{ if .Type.Element.IsScalar }} b.{{ $fieldName }} = make({{ $fieldType }}, len(object.{{ $fieldName }})) copy(b.{{ $fieldName }}, object.{{ $fieldName }}) {{ else if .Type.Element.IsStruct }} - b.{{ $fieldName }} = make([]*{{ builderName .Type.Element }}, len(object.{{ $fieldName }})) + b.{{ $fieldName }} = make([]*{{ selectorType . }}{{ builderName .Type.Element }}, len(object.{{ $fieldName }})) for i, v := range object.{{ $fieldName }} { - b.{{ $fieldName }}[i] = {{ builderCtor .Type.Element }}().Copy(v) + b.{{ $fieldName }}[i] = {{ selectorType . }}{{ builderCtor .Type.Element }}().Copy(v) } {{ end }} {{ end }} @@ -371,9 +374,9 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) { b.{{ $fieldName }}[k] = v } {{ else if .Type.Element.IsStruct }} - b.{{ $fieldName }} = map[string]*{{ builderName .Type.Element }}{} + b.{{ $fieldName }} = map[string]*{{ selectorType . }}{{ builderName .Type.Element }}{} for k, v := range object.{{ $fieldName }} { - b.{{ $fieldName }}[k] = {{ builderCtor .Type.Element }}().Copy(v) + b.{{ $fieldName }}[k] = {{ selectorType . }}{{ builderCtor .Type.Element }}().Copy(v) } {{ end }} } else { @@ -416,7 +419,7 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) { object.{{ $fieldName }} = make({{ $fieldType }}, len(b.{{ $fieldName }})) copy(object.{{ $fieldName }}, b.{{ $fieldName }}) {{ else if .Type.Element.IsStruct }} - object.{{ $fieldName }} = make([]*{{ objectName .Type.Element }}, len(b.{{ $fieldName }})) + object.{{ $fieldName }} = make([]*{{ selectorType . }}{{ objectName .Type.Element }}, len(b.{{ $fieldName }})) for i, v := range b.{{ $fieldName }} { object.{{ $fieldName }}[i], err = v.Build() if err != nil { @@ -434,7 +437,7 @@ func (g *BuildersGenerator) generateStructBuilderSource(typ *concepts.Type) { object.{{ $fieldName }}[k] = v } {{ else if .Type.Element.IsStruct }} - object.{{ $fieldName }} = make(map[string]*{{ objectName .Type.Element }}) + object.{{ $fieldName }} = make(map[string]*{{ selectorType . }}{{ objectName .Type.Element }}) for k, v := range b.{{ $fieldName }} { object.{{ $fieldName }}[k], err = v.Build() if err != nil { @@ -685,6 +688,15 @@ func (g *BuildersGenerator) fieldType(attribute *concepts.Attribute) *TypeRefere return ref } +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) + } + return "" +} + func (g *BuildersGenerator) setterName(attribute *concepts.Attribute) string { name := annotations.GoName(attribute) if name == "" { diff --git a/pkg/generators/golang/json_generator.go b/pkg/generators/golang/json_generator.go index 018db16..d4fe919 100644 --- a/pkg/generators/golang/json_generator.go +++ b/pkg/generators/golang/json_generator.go @@ -167,14 +167,31 @@ func (g *JSONSupportGenerator) Run() error { } // Generate the code for the model types: + var importRefs []struct { + path string + selector string + } 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()) { + importRefs = append(importRefs, + struct { + path string + selector string + }{ + path: g.packages.VersionImport(att.Type().Owner()), + selector: g.packages.VersionSelector(att.Type().Owner()), + }) + } + } switch { case typ.IsStruct(): - err = g.generateStructTypeSupport(typ) + err = g.generateStructTypeSupport(typ, importRefs, version) case typ.IsList(): element := typ.Element() if element.IsScalar() || element.IsStruct() { - err = g.generateListTypeSupport(typ) + err = g.generateListTypeSupport(typ, importRefs, version) } } if err != nil { @@ -184,7 +201,7 @@ func (g *JSONSupportGenerator) Run() error { // Generate the code for the model methods: for _, resource := range version.Resources() { - err = g.generateResourceSupport(resource) + err = g.generateResourceSupport(resource, version) if err != nil { return err } @@ -432,7 +449,7 @@ func (g *JSONSupportGenerator) generateVersionMetadataSupport(version *concepts. Package(pkgName). File(fileName). Function("enumName", g.types.EnumName). - Function("structName", g.types.StructName). + Function("structName", g.types.StructReference). Function("generateReadValue", g.generateReadValue). Function("generateWriteValue", g.generateWriteValue). Function("readTypeFunc", g.readTypeFunc). @@ -511,7 +528,12 @@ func (g *JSONSupportGenerator) generateVersionMetadataSource(version *concepts.V ) } -func (g *JSONSupportGenerator) generateStructTypeSupport(typ *concepts.Type) error { +func (g *JSONSupportGenerator) generateStructTypeSupport(typ *concepts.Type, + importRefs []struct { + path string + selector string + }, + version *concepts.Version) error { var err error // Calculate the package and file name: @@ -533,7 +555,7 @@ func (g *JSONSupportGenerator) generateStructTypeSupport(typ *concepts.Type) err Function("generateWriteValue", g.generateWriteValue). Function("marshalTypeFunc", g.marshalTypeFunc). Function("readTypeFunc", g.readTypeFunc). - Function("structName", g.types.StructName). + Function("structName", g.types.StructReference). Function("unmarshalTypeFunc", g.unmarshalTypeFunc). Function("valueReference", g.types.ValueReference). Function("writeTypeFunc", g.writeTypeFunc). @@ -542,14 +564,19 @@ func (g *JSONSupportGenerator) generateStructTypeSupport(typ *concepts.Type) err return err } + for _, ref := range importRefs { + g.buffer.Import(ref.path, ref.selector) + } + // Generate the code: - g.generateStructTypeSource(typ) + g.generateStructTypeSource(typ, version) // Write the generated code: return g.buffer.Write() } -func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) { +func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type, + version *concepts.Version) { g.buffer.Import("fmt", "") g.buffer.Import("io", "") g.buffer.Import("time", "") @@ -558,9 +585,10 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) { g.buffer.Emit(` {{ $structName := structName .Type }} {{ $marshalTypeFunc := marshalTypeFunc .Type }} - {{ $writeTypeFunc := writeTypeFunc .Type }} + {{ $writeTypeFunc := writeTypeFunc .Type .Version }} {{ $unmarshalTypeFunc := unmarshalTypeFunc .Type }} - {{ $readTypeFunc := readTypeFunc .Type }} + {{ $readTypeFunc := readTypeFunc .Type .Version }} + {{ $version := .Version }} // {{ $marshalTypeFunc }} writes a value of the '{{ .Type.Name }}' type to the given writer. func {{ $marshalTypeFunc }}(object *{{ $structName }}, writer io.Writer) error { @@ -621,7 +649,7 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) { stream.WriteMore() } stream.WriteObjectField("{{ $fieldTag }}") - {{ generateWriteValue (print "object." $fieldName) $v.Type $v.Link }} + {{ generateWriteValue (print "object." $fieldName) $version $v.Type $v.Link }} {{ if lt $i (sub $n 1) }} count++ {{ end }} @@ -670,7 +698,7 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) { {{ $fieldTag := fieldTag . }} {{ $fieldMask := bitMask . }} case "{{ $fieldTag }}": - {{ generateReadValue "value" .Type .Link }} + {{ generateReadValue "value" .Type $version .Link }} object.{{ $fieldName }} = value object.bitmap_ |= {{ $fieldMask }} {{ end }} @@ -682,10 +710,16 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) { } `, "Type", typ, + "Version", version, ) } -func (g *JSONSupportGenerator) generateListTypeSupport(typ *concepts.Type) error { +func (g *JSONSupportGenerator) generateListTypeSupport(typ *concepts.Type, + importRefs []struct { + path string + selector string + }, + version *concepts.Version) error { var err error // Calculate the package and file name: @@ -704,7 +738,7 @@ func (g *JSONSupportGenerator) generateListTypeSupport(typ *concepts.Type) error Function("generateWriteValue", g.generateWriteValue). Function("marshalTypeFunc", g.marshalTypeFunc). Function("readTypeFunc", g.readTypeFunc). - Function("structName", g.types.StructName). + Function("structName", g.types.StructReference). Function("unmarshalTypeFunc", g.unmarshalTypeFunc). Function("valueReference", g.types.ValueReference). Function("writeTypeFunc", g.writeTypeFunc). @@ -714,13 +748,16 @@ func (g *JSONSupportGenerator) generateListTypeSupport(typ *concepts.Type) error } // Generate the code: - g.generateListTypeSource(typ) + g.generateListTypeSource(typ, version) // Write the generated code: return g.buffer.Write() } -func (g *JSONSupportGenerator) generateListTypeSource(typ *concepts.Type) { +func (g *JSONSupportGenerator) generateListTypeSource( + typ *concepts.Type, + version *concepts.Version, +) { g.buffer.Import("fmt", "") g.buffer.Import(g.packages.HelpersImport(), "") g.buffer.Import("github.com/json-iterator/go", "jsoniter") @@ -728,9 +765,9 @@ func (g *JSONSupportGenerator) generateListTypeSource(typ *concepts.Type) { {{ $structName := structName .Type }} {{ $sliceType := valueReference .Type }} {{ $marshalTypeFunc := marshalTypeFunc .Type }} - {{ $writeTypeFunc := writeTypeFunc .Type }} + {{ $writeTypeFunc := writeTypeFunc .Type .Version }} {{ $unmarshalTypeFunc := unmarshalTypeFunc .Type }} - {{ $readTypeFunc := readTypeFunc .Type }} + {{ $readTypeFunc := readTypeFunc .Type .Version }} // {{ $marshalTypeFunc }} writes a list of values of the '{{ .Type.Element.Name }}' type to // the given writer. @@ -752,7 +789,7 @@ func (g *JSONSupportGenerator) generateListTypeSource(typ *concepts.Type) { if i > 0 { stream.WriteMore() } - {{ generateWriteValue "value" .Type.Element false }} + {{ generateWriteValue "value" .Version .Type.Element false }} } stream.WriteArrayEnd() } @@ -774,17 +811,19 @@ func (g *JSONSupportGenerator) generateListTypeSource(typ *concepts.Type) { func {{ $readTypeFunc }}(iterator *jsoniter.Iterator) {{ $sliceType }} { list := {{ valueReference .Type }}{} for iterator.ReadArray() { - {{ generateReadValue "item" .Type.Element false }} + {{ generateReadValue "item" .Type.Element .Version false }} list = append(list, item) } return list } `, "Type", typ, + "Version", version, ) } -func (g *JSONSupportGenerator) generateResourceSupport(resource *concepts.Resource) error { +func (g *JSONSupportGenerator) generateResourceSupport(resource *concepts.Resource, + version *concepts.Version) error { var err error // Calculate the package and file name: @@ -815,7 +854,7 @@ func (g *JSONSupportGenerator) generateResourceSupport(resource *concepts.Resour Function("requestBodyParameters", g.binding.RequestBodyParameters). Function("requestQueryParameters", g.binding.RequestQueryParameters). Function("responseBodyParameters", g.binding.ResponseParameters). - Function("structName", g.types.StructName). + Function("structName", g.types.StructReference). Function("unmarshalTypeFunc", g.unmarshalTypeFunc). Function("valueReference", g.types.ValueReference). Function("writeRequestFunc", g.writeRequestFunc). @@ -827,14 +866,14 @@ func (g *JSONSupportGenerator) generateResourceSupport(resource *concepts.Resour // Generate the code: for _, method := range resource.Methods() { - g.generateMethodSource(method) + g.generateMethodSource(method, version) } // Write the generated code: return g.buffer.Write() } -func (g *JSONSupportGenerator) generateMethodSource(method *concepts.Method) { +func (g *JSONSupportGenerator) generateMethodSource(method *concepts.Method, version *concepts.Version) { switch { case method.IsAdd(): g.generateAddMethodSource(method) @@ -851,7 +890,7 @@ func (g *JSONSupportGenerator) generateMethodSource(method *concepts.Method) { case method.IsUpdate(): g.generateUpdateMethodSource(method) case method.IsAction(): - g.generateActionMethodSource(method) + g.generateActionMethodSource(method, version) default: g.reporter.Errorf( "Don't know how to generate encoding/decoding code for method '%s'", @@ -965,21 +1004,21 @@ func (g *JSONSupportGenerator) generateListMethodSource(method *concepts.Method) } switch field { {{ if .Page }} - {{ generateReadBodyParameter "response" .Page }} + {{ generateReadBodyParameter "response" .Version .Page }} {{ end }} {{ if .Size }} - {{ generateReadBodyParameter "response" .Size }} + {{ generateReadBodyParameter "response" .Version .Size }} {{ end }} {{ if .Total }} - {{ generateReadBodyParameter "response" .Total }} + {{ generateReadBodyParameter "response" .Version .Total }} {{ end }} {{ range .Other }} {{ if .Out }} - {{ generateReadBodyParameter "response" . }} + {{ generateReadBodyParameter "response" .Version . }} {{ end }} {{ end }} case "items": - {{ generateReadValue "items" .Items.Type false }} + {{ generateReadValue "items" .Items.Type .Version false }} {{ if and .Items.Type.IsList .Items.Type.Element.IsScalar }} response.items = items {{ else }} @@ -1076,6 +1115,7 @@ 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) @@ -1096,21 +1136,21 @@ func (g *JSONSupportGenerator) generateSearchMethodSource(method *concepts.Metho } switch field { {{ if .Page }} - {{ generateReadBodyParameter "response" .Page }} + {{ generateReadBodyParameter "response" $version .Page }} {{ end }} {{ if .Size }} - {{ generateReadBodyParameter "response" .Size }} + {{ generateReadBodyParameter "response" $version .Size }} {{ end }} {{ if .Total }} - {{ generateReadBodyParameter "response" .Total }} + {{ generateReadBodyParameter "response" $version .Total }} {{ end }} {{ range .Other }} {{ if .Out }} - {{ generateReadBodyParameter "response" . }} + {{ generateReadBodyParameter "response" $version . }} {{ end }} {{ end }} case "items": - {{ generateReadValue "items" .Items.Type false }} + {{ generateReadValue "items" .Items.Type $version false }} {{ if and .Items.Type.IsList .Items.Type.Element.IsScalar }} response.items = items {{ else }} @@ -1159,20 +1199,21 @@ func (g *JSONSupportGenerator) generateUpdateMethodSource(method *concepts.Metho ) } -func (g *JSONSupportGenerator) generateActionMethodSource(method *concepts.Method) { +func (g *JSONSupportGenerator) generateActionMethodSource(method *concepts.Method, + version *concepts.Version) { 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" . }} + {{ generateWriteBodyParameter "request" $version . }} {{ end }} stream.WriteObjectEnd() err := stream.Flush() @@ -1198,7 +1239,7 @@ func (g *JSONSupportGenerator) generateActionMethodSource(method *concepts.Metho } switch field { {{ range $responseBodyParameters }} - {{ generateReadBodyParameter "response" . }} + {{ generateReadBodyParameter "response" $version . }} {{ end }} default: iterator.ReadAny() @@ -1211,6 +1252,7 @@ func (g *JSONSupportGenerator) generateActionMethodSource(method *concepts.Metho } `, "Method", method, + "Version", version, ) } @@ -1248,14 +1290,15 @@ func (g *JSONSupportGenerator) generateReadQueryParameter(parameter *concepts.Pa ) } -func (g *JSONSupportGenerator) generateReadBodyParameter(object string, parameter *concepts. - Parameter) string { +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() return g.buffer.Eval(` case "{{ .Tag }}": - {{ generateReadValue "value" .Type false }} + {{ generateReadValue "value" .Type .Version false }} {{ if .Type.IsScalar }} {{ .Object }}.{{ .Field }} = &value {{ else }} @@ -1266,10 +1309,13 @@ func (g *JSONSupportGenerator) generateReadBodyParameter(object string, paramete "Field", field, "Tag", tag, "Type", typ, + "Version", version, ) } -func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts.Type, link bool) string { +func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts.Type, + version *concepts.Version, + link bool) string { g.buffer.Import("time", "") return g.buffer.Eval(` {{ if .Type.IsBoolean }} @@ -1295,7 +1341,7 @@ func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts. text := iterator.ReadString() {{ .Variable }} := {{ enumName .Type }}(text) {{ else if .Type.IsStruct }} - {{ .Variable }} := {{ readTypeFunc .Type }}(iterator) + {{ .Variable }} := {{ readTypeFunc .Type .Version }}(iterator) {{ else if .Type.IsList }} {{ if .Link }} {{ $structName := structName .Type }} @@ -1308,17 +1354,17 @@ func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts. switch field { case "kind": text := iterator.ReadString() - {{ .Variable }}.link = text == {{ $structName }}LinkKind + {{ .Variable }}.SetLink(text == {{ $structName }}LinkKind) case "href": - {{ .Variable }}.href = iterator.ReadString() + {{ .Variable }}.SetHREF(iterator.ReadString()) case "items": - {{ .Variable }}.items = {{ readTypeFunc .Type }}(iterator) + {{ .Variable }}.SetItems({{ readTypeFunc .Type .Version }}(iterator)) default: iterator.ReadAny() } } {{ else }} - {{ .Variable }} := {{ readTypeFunc .Type }}(iterator) + {{ .Variable }} := {{ readTypeFunc .Type .Version }}(iterator) {{ end }} {{ else if .Type.IsMap }} {{ .Variable }} := {{ valueReference .Type }}{} @@ -1327,7 +1373,7 @@ func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts. if key == "" { break } - {{ generateReadValue "item" .Type.Element false }} + {{ generateReadValue "item" .Type.Element .Version false }} {{ .Variable }}[key] = item } {{ else }} @@ -1337,11 +1383,14 @@ func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts. "Variable", variable, "Type", typ, "Link", link, + "Version", version, ) } func (g *JSONSupportGenerator) generateWriteBodyParameter(object string, - parameter *concepts.Parameter) string { + version *concepts.Version, + parameter *concepts.Parameter, +) string { typ := parameter.Type() field := g.parameterFieldName(parameter) tag := g.binding.BodyParameterName(parameter) @@ -1365,7 +1414,7 @@ func (g *JSONSupportGenerator) generateWriteBodyParameter(object string, stream.WriteMore() } stream.WriteObjectField("{{ .Tag }}") - {{ generateWriteValue .Value .Type false }} + {{ generateWriteValue .Value .Version .Type false }} count++ } `, @@ -1374,10 +1423,14 @@ func (g *JSONSupportGenerator) generateWriteBodyParameter(object string, "Tag", tag, "Value", value, "Type", typ, + "Version", version, ) } -func (g *JSONSupportGenerator) generateWriteValue(value string, typ *concepts.Type, link bool) string { +func (g *JSONSupportGenerator) generateWriteValue(value string, + version *concepts.Version, + typ *concepts.Type, + link bool) string { g.buffer.Import("sort", "") g.buffer.Import("time", "") return g.buffer.Eval(` @@ -1398,15 +1451,15 @@ func (g *JSONSupportGenerator) generateWriteValue(value string, typ *concepts.Ty {{ else if .Type.IsInterface }} stream.WriteVal({{ .Value }}) {{ else if .Type.IsStruct }} - {{ writeTypeFunc .Type }}({{ .Value }}, stream) + {{ writeTypeFunc .Type .Version }}({{ .Value }}, stream) {{ else if .Type.IsList }} {{ if .Link }} stream.WriteObjectStart() stream.WriteObjectField("items") - {{ writeTypeFunc .Type }}({{ .Value }}.items, stream) + {{ writeTypeFunc .Type .Version }}({{ .Value }}.Items(), stream) stream.WriteObjectEnd() {{ else }} - {{ writeTypeFunc .Type }}({{ .Value }}, stream) + {{ writeTypeFunc .Type .Version }}({{ .Value }}, stream) {{ end }} {{ else if .Type.IsMap }} if {{ .Value }} != nil { @@ -1424,7 +1477,7 @@ func (g *JSONSupportGenerator) generateWriteValue(value string, typ *concepts.Ty } item := {{ .Value }}[key] stream.WriteObjectField(key) - {{ generateWriteValue "item" .Type.Element false }} + {{ generateWriteValue "item" .Version .Type.Element false }} } stream.WriteObjectEnd() } else { @@ -1435,6 +1488,7 @@ func (g *JSONSupportGenerator) generateWriteValue(value string, typ *concepts.Ty "Value", value, "Type", typ, "Link", link, + "Version", version, ) } @@ -1463,9 +1517,13 @@ func (g *JSONSupportGenerator) marshalTypeFunc(typ *concepts.Type) string { return name } -func (g *JSONSupportGenerator) writeTypeFunc(typ *concepts.Type) string { +func (g *JSONSupportGenerator) writeTypeFunc(typ *concepts.Type, version *concepts.Version) string { + _, selector := g.types.Package(typ) name := names.Cat(nomenclator.Write, typ.Name()) - return g.names.Private(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) unmarshalTypeFunc(typ *concepts.Type) string { @@ -1477,9 +1535,13 @@ func (g *JSONSupportGenerator) unmarshalTypeFunc(typ *concepts.Type) string { return name } -func (g *JSONSupportGenerator) readTypeFunc(typ *concepts.Type) string { +func (g *JSONSupportGenerator) readTypeFunc(typ *concepts.Type, version *concepts.Version) string { + _, selector := g.types.Package(typ) name := names.Cat(nomenclator.Read, typ.Name()) - return g.names.Private(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) fieldName(attribute *concepts.Attribute) string { diff --git a/pkg/generators/golang/types_calculator.go b/pkg/generators/golang/types_calculator.go index 7b0fdcb..f8f84be 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 = ref.name + ref.text = fmt.Sprintf("%s.%s", ref.selector, 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 = ref.name + ref.text = fmt.Sprintf("%s.%s", ref.selector, ref.name) default: c.reporter.Errorf( "Don't know how to calculate struct type reference for type '%s'", @@ -213,7 +213,7 @@ func (c *TypesCalculator) ValueReference(typ *concepts.Type) *TypeReference { ref.text = fmt.Sprintf("[]%s", ref.text) case element.IsStruct(): ref = c.ValueReference(element) - ref.text = fmt.Sprintf("[]*%s", ref.text) + ref.text = fmt.Sprintf("[]*%s.%s", ref.selector, ref.text) } case typ.IsMap(): element := typ.Element() @@ -223,7 +223,7 @@ func (c *TypesCalculator) ValueReference(typ *concepts.Type) *TypeReference { ref.text = fmt.Sprintf("map[string]%s", ref.text) case element.IsStruct(): ref = c.ValueReference(element) - ref.text = fmt.Sprintf("map[string]*%s", ref.text) + ref.text = fmt.Sprintf("map[string]*%s.%s", ref.selector, ref.text) } case typ.IsStruct(): ref = &TypeReference{} @@ -239,6 +239,7 @@ func (c *TypesCalculator) ValueReference(typ *concepts.Type) *TypeReference { "Don't know how to calculate value reference for type '%s'", typ, ) + ref = &TypeReference{} } return ref @@ -248,10 +249,14 @@ func (c *TypesCalculator) ValueReference(typ *concepts.Type) *TypeReference { // the nil value. func (c *TypesCalculator) NullableReference(typ *concepts.Type) *TypeReference { switch { - case (typ.IsScalar() && !typ.IsInterface()) || typ.IsStruct(): + case (typ.IsScalar() && !typ.IsInterface()): ref := c.ValueReference(typ) ref.text = fmt.Sprintf("*%s", ref.text) return ref + case typ.IsStruct(): + ref := c.ValueReference(typ) + ref.text = fmt.Sprintf("*%s.%s", ref.selector, ref.name) + return ref default: return c.ValueReference(typ) } diff --git a/pkg/generators/golang/types_generator.go b/pkg/generators/golang/types_generator.go index f239fb1..4c4c383 100644 --- a/pkg/generators/golang/types_generator.go +++ b/pkg/generators/golang/types_generator.go @@ -505,6 +505,32 @@ func (g *TypesGenerator) generateStructTypeSource(typ *concepts.Type) { return len(l.items) } + // Items sets the items of the list. + func (l *{{ $listName }}) SetLink(link bool) { + l.link = link + return + } + + // Items sets the items of the list. + func (l *{{ $listName }}) SetHREF(href string) { + l.href = href + return + } + + // Items sets the items of the list. + func (l *{{ $listName }}) SetItems(items []*{{ $objectName }}) { + l.items = items + return + } + + // Items returns the items of the list. + func (l *{{ $listName }}) Items() []*{{ $objectName }} { + if l == nil { + return nil + } + return l.items + } + // Empty returns true if the list is empty. func (l *{{ $listName }}) Empty() bool { return l == nil || len(l.items) == 0 diff --git a/pkg/language/reader.go b/pkg/language/reader.go index bc65209..95ad2c6 100644 --- a/pkg/language/reader.go +++ b/pkg/language/reader.go @@ -30,6 +30,7 @@ import ( "github.com/antlr/antlr4/runtime/Go/antlr" + "github.com/openshift-online/ocm-api-metamodel/pkg/annotations" "github.com/openshift-online/ocm-api-metamodel/pkg/concepts" "github.com/openshift-online/ocm-api-metamodel/pkg/names" "github.com/openshift-online/ocm-api-metamodel/pkg/nomenclator" @@ -132,7 +133,7 @@ func (r *Reader) Read() (model *concepts.Model, err error) { for _, service := range r.model.Services() { for _, version := range service.Versions() { for _, typ := range version.Types() { - if typ.Kind() != concepts.ListType { + if typ.Kind() != concepts.ListType && typ.Owner().Name() == version.Name() { listName := names.Cat(typ.Name(), nomenclator.List) listType := version.FindType(listName) if listType == nil { @@ -408,8 +409,20 @@ func (r *Reader) ExitClassDecl(ctx *ClassDeclContext) { typ.SetKind(concepts.ClassType) r.removeUndefinedType(typ) } else { - r.reporter.Errorf("Type '%s' is already defined", name) - return + // we would like to override the owner of any previously defined types + // these might come from references. + // e.g. + // @ref(name="/some/service/foo") + // Class foo { + // Bar Sometype + // } + // some_type.model - an overriding decleration. + // Class SomeType {...} + r.version.AddType(typ) + listName := names.Cat(typ.Name(), nomenclator.List) + if listType := r.version.FindType(listName); listType != nil { + r.version.AddType(listType) + } } // Add the documentation: @@ -428,6 +441,76 @@ func (r *Reader) ExitClassDecl(ctx *ClassDeclContext) { typ.AddAttribute(memberCtx.GetResult()) } } + + if path := annotations.ReferencePath(typ); path != "" { + r.handleClassRef(typ, path) + } +} + +func (r *Reader) handleClassRef(typ *concepts.Type, path string) { + if len(r.inputs) > 1 { + panic("referenced service with multiple inputs in undefined") + } + input := r.inputs[0] + path = strings.TrimPrefix(path, "/") + components := strings.Split(path, "/") + referencedServiceName := components[0] + referencedVersion := components[1] + referencedTypeName := components[2] + + // Create an ad-hoc reader and model for the specific referenced service. + refReader := NewReader(). + Reporter(r.reporter) + refReader.model = concepts.NewModel() + + // Initialize the indexes of undefined concepts: + refReader.undefinedTypes = make(map[string]*concepts.Type) + refReader.undefinedResources = make(map[string]*concepts.Resource) + refReader.undefinedErrors = make(map[string]*concepts.Error) + + // load the ad-hoc service and version referenced and find the correct type. + refReader.loadService(fmt.Sprintf("%s/%s", input, referencedServiceName)) + refVersion := refReader.service.FindVersion(names.ParseUsingSeparator(referencedVersion, "_")) + // 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.recursivelyAddTypeToVersion(typ, referencedType) + } +} + +// A helper function to recursively add types to a version +func (r *Reader) recursivelyAddTypeToVersion(currType *concepts.Type, + referencedType *concepts.Type) { + if referencedType.IsBasicType() { + return + } + for _, attribute := range referencedType.Attributes() { + if attribute.Link() { + // 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()) + } + } else if attribute.Type().IsList() || attribute.Type().IsMap() { + r.version.AddType(attribute.Type()) + r.recursivelyAddTypeToVersion(currType, attribute.Type().Element()) + } else { + r.recursivelyAddTypeToVersion(currType, attribute.Type()) + } + } + r.version.AddType(referencedType) } func (r *Reader) ExitStructDecl(ctx *StructDeclContext) { diff --git a/pkg/language/ref_test.go b/pkg/language/ref_test.go new file mode 100644 index 0000000..351be31 --- /dev/null +++ b/pkg/language/ref_test.go @@ -0,0 +1,394 @@ +/* +Copyright (c) 2024 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package language + +import ( + "github.com/openshift-online/ocm-api-metamodel/pkg/names" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Read Model with ref annotation", func() { + + It("Reads referenced class scalar attribute", 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 { + MyAttribute Integer + } + `, + ) + // 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()) + attribute := class.FindAttribute(names.ParseUsingCase("MyAttribute")) + Expect(attribute).ToNot(BeNil()) + Expect(attribute.Type().Owner().Name().String()).To(Equal("v1")) + }) + + It("References respect link attribute", 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 MyAttribute MyAttribute + } + `, + "other_service/v1/my_attribute.model", + ` + class MyAttribute{ + } + `, + ) + // 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()) + myAttribute := class.FindAttribute(names.ParseUsingCase("MyAttribute")) + Expect(myAttribute).ToNot(BeNil()) + Expect(myAttribute.Type().Owner().Name().String()).To(Equal("v1")) + Expect(myAttribute.Link()).To(BeTrue()) + }) + + It("Reads referenced class list attribute", 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 { + Foo []MyAttribute + }`, + "other_service/v1/my_attribute.model", + ` + class MyAttribute{ + } + `, + ) + // 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()) + attributeType := version.FindType(names.ParseUsingCase("MyAttribute")) + Expect(attributeType).ToNot(BeNil()) + Expect(attributeType.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.Type().Owner().Name().String()).To(Equal("v1_alpha1")) + }) + + It("Overrides class with other class definition", 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 { + } + `, + "my_service/v1_alpha1/my_attribute.model", + ` + @ref(path="other_service/v1/my_attribute") + class MyAttribute { + } + `, + "other_service/v1/root.model", + ` + resource Root{ + } + `, + "other_service/v1/my_class.model", + ` + class MyClass { + link Foo []MyAttribute + }`, + "other_service/v1/my_attribute.model", + ` + class MyAttribute{ + Goo Bar + } + `, + "other_service/v1/bar.model", + ` + class Bar { + } + `, + ) + // 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.Type().Owner().Name().String()).To(Equal("v1_alpha1")) + 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")) + }) + + It("Overrides class with other class definition link attribute", 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 { + } + `, + "my_service/v1_alpha1/my_attribute.model", + ` + @ref(path="other_service/v1/my_attribute") + class MyAttribute { + } + `, + "other_service/v1/root.model", + ` + resource Root{ + } + `, + "other_service/v1/my_class.model", + ` + class MyClass { + link Foo []MyAttribute + }`, + "other_service/v1/my_attribute.model", + ` + class MyAttribute{ + link Goo Bar + } + `, + "other_service/v1/bar.model", + ` + class Bar { + } + `, + ) + // 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.Type().Owner().Name().String()).To(Equal("v1_alpha1")) + 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")) + barType := version.FindType(names.ParseUsingCase("Bar")) + // Referenced classes link doesn't change the owner + Expect(barType.Owner().Name().String()).To(Equal("v1")) + }) + + It("Overrides class with other class definition list attribute", 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 { + } + `, + "my_service/v1_alpha1/my_attribute.model", + ` + @ref(path="other_service/v1/my_attribute") + class MyAttribute { + } + `, + "other_service/v1/root.model", + ` + resource Root{ + } + `, + "other_service/v1/my_class.model", + ` + class MyClass { + link Foo []MyAttribute + }`, + "other_service/v1/my_attribute.model", + ` + class MyAttribute{ + Goo []Bar + } + `, + "other_service/v1/bar.model", + ` + class Bar { + } + `, + ) + // 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.Type().Owner().Name().String()).To(Equal("v1_alpha1")) + 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")) + barType := version.FindType(names.ParseUsingCase("Bar")) + Expect(barType.Owner().Name().String()).To(Equal("v1_alpha1")) + }) + + It("Overrides class with other class definition link list attribute", 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 { + } + `, + "my_service/v1_alpha1/my_attribute.model", + ` + @ref(path="other_service/v1/my_attribute") + class MyAttribute { + } + `, + "other_service/v1/root.model", + ` + resource Root{ + } + `, + "other_service/v1/my_class.model", + ` + class MyClass { + link Foo []MyAttribute + }`, + "other_service/v1/my_attribute.model", + ` + class MyAttribute{ + link Goo []Bar + } + `, + "other_service/v1/bar.model", + ` + class Bar { + } + `, + ) + // 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.Type().Owner().Name().String()).To(Equal("v1_alpha1")) + 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")) + barType := version.FindType(names.ParseUsingCase("Bar")) + Expect(barType.Owner().Name().String()).To(Equal("v1")) + }) +}) diff --git a/tests/model/aro_hcp/v1_alpha1/cluster_resource.model b/tests/model/aro_hcp/v1_alpha1/cluster_resource.model new file mode 100644 index 0000000..d45afaa --- /dev/null +++ b/tests/model/aro_hcp/v1_alpha1/cluster_resource.model @@ -0,0 +1,42 @@ +/* +Copyright (c) 2024 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Manages a specific cluster. +resource Cluster { + // Retrieves the details of the cluster. + method Get { + out Body Cluster + } + + // Updates the cluster. + method Update { + in out Body Cluster + } + + // Deletes the cluster. + method Delete { + // Dry run flag is used to check if the operation can be completed, but won't delete. + in DryRun Boolean = false + + // BestEffort flag is used to check if the cluster deletion should be best-effort mode or not. + in BestEffort Boolean = false + } + + // Reference to the resource that manages the collection of node pool resources. + locator NodePools { + target NodePools + } +} \ No newline at end of file diff --git a/tests/model/aro_hcp/v1_alpha1/cluster_type.model b/tests/model/aro_hcp/v1_alpha1/cluster_type.model new file mode 100644 index 0000000..793cc1f --- /dev/null +++ b/tests/model/aro_hcp/v1_alpha1/cluster_type.model @@ -0,0 +1,19 @@ +/* +Copyright (c) 2024 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +@ref(path = "/clusters_mgmt/v1/cluster") +class Cluster { +} \ No newline at end of file diff --git a/tests/model/aro_hcp/v1_alpha1/clusters_resource.model b/tests/model/aro_hcp/v1_alpha1/clusters_resource.model new file mode 100644 index 0000000..136b081 --- /dev/null +++ b/tests/model/aro_hcp/v1_alpha1/clusters_resource.model @@ -0,0 +1,79 @@ +/* +Copyright (c) 2024 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Manages the collection of aro_hcp clusters. +resource Clusters { + // Retrieves the list of clusters. + method List { + // Index of the requested page, where one corresponds to the first page. + in out Page Integer = 1 + + // Maximum number of items that will be contained in the returned page. + in out Size Integer = 100 + + // Search criteria. + // + // The syntax of this parameter is similar to the syntax of the _where_ clause of a + // SQL statement, but using the names of the attributes of the cluster instead of + // the names of the columns of a table. For example, in order to retrieve all the + // clusters with a name starting with `my` in the `us-east-1` region the value + // should be: + // + // ```sql + // name like 'my%' and region.id = 'us-east-1' + // ``` + // + // If the parameter isn't provided, or if the value is empty, then all the + // clusters that the user has permission to see will be returned. + in Search String + + // Order criteria. + // + // The syntax of this parameter is similar to the syntax of the _order by_ clause of + // a SQL statement, but using the names of the attributes of the cluster instead of + // the names of the columns of a table. For example, in order to sort the clusters + // descending by region identifier the value should be: + // + // ```sql + // region.id desc + // ``` + // + // If the parameter isn't provided, or if the value is empty, then the order of the + // results is undefined. + in Order String + + // Total number of items of the collection that match the search criteria, + // regardless of the size of the page. + out Total Integer + + // Retrieved list of clusters. + out Items []Cluster + } + + // Provision a new cluster and add it to the collection of clusters. + // + // See the `register_cluster` method for adding an existing cluster. + method Add { + // Description of the cluster. + in out Body Cluster + } + + // Returns a reference to the service that manages an specific cluster. + locator Cluster { + target Cluster + variable ID + } +} \ No newline at end of file diff --git a/tests/model/aro_hcp/v1_alpha1/node_pool_resource.model b/tests/model/aro_hcp/v1_alpha1/node_pool_resource.model new file mode 100644 index 0000000..018e7ec --- /dev/null +++ b/tests/model/aro_hcp/v1_alpha1/node_pool_resource.model @@ -0,0 +1,32 @@ +/* +Copyright (c) 2024 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Manages a specific nodepool. +resource NodePool { + // Retrieves the details of the node pool. + method Get { + out Body NodePool + } + + // Updates the node pool. + method Update { + in out Body NodePool + } + + // Deletes the node pool. + method Delete { + } +} diff --git a/tests/model/aro_hcp/v1_alpha1/node_pool_type.model b/tests/model/aro_hcp/v1_alpha1/node_pool_type.model new file mode 100644 index 0000000..c94a048 --- /dev/null +++ b/tests/model/aro_hcp/v1_alpha1/node_pool_type.model @@ -0,0 +1,19 @@ +/* +Copyright (c) 2024 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +@ref(path = "/clusters_mgmt/v1/node_pool") +class NodePool { +} \ No newline at end of file diff --git a/tests/model/aro_hcp/v1_alpha1/node_pools_resource.model b/tests/model/aro_hcp/v1_alpha1/node_pools_resource.model new file mode 100644 index 0000000..09c370b --- /dev/null +++ b/tests/model/aro_hcp/v1_alpha1/node_pools_resource.model @@ -0,0 +1,75 @@ +/* +Copyright (c) 2022 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Manages the collection of node pools of a cluster. +resource NodePools { + // Retrieves the list of node pools. + method List { + // Index of the requested page, where one corresponds to the first page. + in out Page Integer = 1 + + // Number of items contained in the returned page. + in out Size Integer = 100 + + // Search criteria. + // + // The syntax of this parameter is similar to the syntax of the _where_ clause of a + // SQL statement, but using the names of the attributes of the node pools instead of + // the names of the columns of a table. For example, in order to retrieve all the + // node pools with replicas of two the following is required: + // + // ```sql + // replicas = 2 + // ``` + // + // If the parameter isn't provided, or if the value is empty, then all the + // node pools that the user has permission to see will be returned. + in Search String + + // Order criteria. + // + // The syntax of this parameter is similar to the syntax of the _order by_ clause of + // a SQL statement, but using the names of the attributes of the node pools instead of + // the names of the columns of a table. For example, in order to sort the node pools + // descending by identifier the value should be: + // + // ```sql + // id desc + // ``` + // + // If the parameter isn't provided, or if the value is empty, then the order of the + // results is undefined. + in Order String + + // Total number of items of the collection. + out Total Integer + + // Retrieved list of node pools. + out Items []NodePool + } + + // Adds a new node pool to the cluster. + method Add { + // Description of the node pool + in out Body NodePool + } + + // Reference to the service that manages a specific node pool. + locator NodePool { + target NodePool + variable ID + } +} \ No newline at end of file diff --git a/tests/model/aro_hcp/v1_alpha1/root_resource.model b/tests/model/aro_hcp/v1_alpha1/root_resource.model new file mode 100644 index 0000000..178bd3b --- /dev/null +++ b/tests/model/aro_hcp/v1_alpha1/root_resource.model @@ -0,0 +1,18 @@ +/* +Copyright (c) 2024 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Root of the tree of resources of the aro_hcp service. +resource Root {} diff --git a/tests/model/clusters_mgmt/v1/node_pool_type.model b/tests/model/clusters_mgmt/v1/node_pool_type.model new file mode 100644 index 0000000..9c64ffa --- /dev/null +++ b/tests/model/clusters_mgmt/v1/node_pool_type.model @@ -0,0 +1,17 @@ +/* +Copyright (c) 2024 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +Class NodePool {} \ No newline at end of file