diff --git a/pkg/concepts/type.go b/pkg/concepts/type.go index 8a67a6b..b375081 100644 --- a/pkg/concepts/type.go +++ b/pkg/concepts/type.go @@ -72,12 +72,24 @@ type Type struct { annotatedSupport namedSupport - owner *Version - kind TypeKind - attributes AttributeSlice - values EnumValueSlice - element *Type - index *Type + owner *Version + kind TypeKind + attributes AttributeSlice + values EnumValueSlice + element *Type + index *Type + explicitDeclared bool +} + +// ExplicitDeclared returns true if this type is explicitDeclared +// with a reference +func (t *Type) ExplicitDeclared() bool { + return t.explicitDeclared +} + +// SetExplicitDeclared sets ExplicitDeclared +func (t *Type) SetExplicitDeclared(value bool) { + t.explicitDeclared = value } // Owner returns the version that owns this type. @@ -193,6 +205,14 @@ func (t *Type) AddAttribute(attribute *Attribute) { } } +func (t *Type) AddAttributes(attributes AttributeSlice) { + if attributes.Len() > 0 { + for _, att := range attributes { + t.AddAttribute(att) + } + } +} + // FindAttribute returns the attribute with the given name, or nil if no such attribute exists. func (t *Type) FindAttribute(name *names.Name) *Attribute { for _, attribute := range t.attributes { diff --git a/pkg/generators/golang/json_generator.go b/pkg/generators/golang/json_generator.go index de6c314..97d5fa7 100644 --- a/pkg/generators/golang/json_generator.go +++ b/pkg/generators/golang/json_generator.go @@ -700,7 +700,7 @@ func (g *JSONSupportGenerator) generateStructTypeSource(typ *concepts.Type) { {{ $fieldTag := fieldTag . }} {{ $fieldMask := bitMask . }} case "{{ $fieldTag }}": - {{ generateReadValue "value" .Type .Link .LinkOwner}} + {{ generateReadValue "value" .Type .Link .LinkOwner }} object.{{ $fieldName }} = value object.bitmap_ |= {{ $fieldMask }} {{ end }} @@ -1351,7 +1351,7 @@ func (g *JSONSupportGenerator) generateReadValue(variable string, typ *concepts. {{ .Variable }} := {{ readRefTypeFunc .Type .LinkOwner }}(iterator) {{ else if .Type.IsList }} {{ if .Link }} - {{ $selectorFromLinkOwner := selectorFromLinkOwner .LinkOwner}} + {{ $selectorFromLinkOwner := selectorFromLinkOwner .LinkOwner }} {{ $structName := structName .Type }} {{ .Variable }} := &{{ $selectorFromLinkOwner }}{{ $structName }}{} for { @@ -1485,7 +1485,7 @@ func (g *JSONSupportGenerator) generateWriteValue(value string, } item := {{ .Value }}[key] stream.WriteObjectField(key) - {{ generateWriteValue "item" .Type.Element false .LinkOwner}} + {{ generateWriteValue "item" .Type.Element false .LinkOwner }} } stream.WriteObjectEnd() } else { diff --git a/pkg/language/reader.go b/pkg/language/reader.go index 4640da4..2972465 100644 --- a/pkg/language/reader.go +++ b/pkg/language/reader.go @@ -431,10 +431,34 @@ func (r *Reader) ExitClassDecl(ctx *ClassDeclContext) { } if path := annotations.ReferencePath(typ); path != "" { + // If the type has a reference set ExplicitDeclared + typ.SetExplicitDeclared(true) + r.removeLinkedAttributes(typ) r.handleClassRef(typ, path) } } +func (r *Reader) removeLinkedAttributes(typ *concepts.Type) { + for _, types := range r.version.Types() { + for _, attribute := range types.Attributes() { + // It could be that the type is already in an attribute of the service + // It needs to add the SetExplicitDeclared to it. { + if attribute.Type().Name() == typ.Name() { + attribute.SetLinkOwner(nil) + attribute.Type().SetExplicitDeclared(true) + } + if attribute.Type().IsList() || attribute.Type().IsMap() { + if attribute.Type().Element().Name().String() == typ.Name().String() { + attribute.SetLinkOwner(nil) + attribute.Type().SetExplicitDeclared(true) + attribute.Type().Element().SetExplicitDeclared(true) + attribute.Type().Element().SetOwner(typ.Owner()) + } + } + } + } +} + func (r *Reader) handleClassRef(typ *concepts.Type, path string) { if len(r.inputs) > 1 { panic("referenced service with multiple inputs in undefined") @@ -462,7 +486,6 @@ 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) } } @@ -474,9 +497,25 @@ func (r *Reader) recursivelyAddTypeToVersion(currType *concepts.Type, return } for _, attribute := range referencedType.Attributes() { + // If attribute is explicitDeclared, it needs to change it's owner to be the same as currType if attribute.Link() { // If the attribute is a Link set the LinkOwner - // to the attribute type Owner + // to the attribute type Owner, if it is also a list it should change the owner of the + // element type + existingAttType := r.version.FindType(attribute.Type().Name()) + if existingAttType != nil && existingAttType.ExplicitDeclared() { + attribute.Type().SetOwner(r.version) + // Stop the iteration over the attributes of this type + continue + } else if attribute.Type().IsList() || attribute.Type().IsMap() { + existingAttType = r.version.FindType(attribute.Type().Element().Name()) + if existingAttType != nil && existingAttType.ExplicitDeclared() { + attribute.Type().SetOwner(r.version) + attribute.Type().Element().SetOwner(r.version) + continue + } + } + // Otherwise add the setLinkOwner to the attribute attribute.SetLinkOwner(attribute.Type().Owner()) } else if attribute.Type().IsList() || attribute.Type().IsMap() { r.version.AddType(attribute.Type()) @@ -485,9 +524,13 @@ func (r *Reader) recursivelyAddTypeToVersion(currType *concepts.Type, r.recursivelyAddTypeToVersion(currType, attribute.Type()) } } - if r.version.FindType(referencedType.Name()) == nil { - r.version.AddType(referencedType) + + existingType := r.version.FindType(referencedType.Name()) + if existingType != nil && existingType.ExplicitDeclared() { + referencedType.SetExplicitDeclared(true) } + 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 0e5690a..3c45db1 100644 --- a/pkg/language/ref_test.go +++ b/pkg/language/ref_test.go @@ -206,8 +206,7 @@ var _ = Describe("Read Model with ref annotation", func() { attributeFoo := class.FindAttribute(names.ParseUsingCase("Foo")) Expect(attributeFoo).ToNot(BeNil()) Expect(attributeFoo.Type().IsList()).To(BeTrue()) - Expect(attributeFoo.LinkOwner().Name().String()).To(Equal("v1")) - Expect(attributeFoo.Type().Owner().Name().String()).To(Equal("v1")) + Expect(attributeFoo.Type().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")) @@ -267,8 +266,7 @@ var _ = Describe("Read Model with ref annotation", func() { attributeFoo := class.FindAttribute(names.ParseUsingCase("Foo")) Expect(attributeFoo).ToNot(BeNil()) Expect(attributeFoo.Type().IsList()).To(BeTrue()) - Expect(attributeFoo.LinkOwner().Name().String()).To(Equal("v1")) - Expect(attributeFoo.Type().Owner().Name().String()).To(Equal("v1")) + Expect(attributeFoo.Type().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")) @@ -331,8 +329,7 @@ var _ = Describe("Read Model with ref annotation", func() { attributeFoo := class.FindAttribute(names.ParseUsingCase("Foo")) Expect(attributeFoo).ToNot(BeNil()) Expect(attributeFoo.Type().IsList()).To(BeTrue()) - Expect(attributeFoo.LinkOwner().Name().String()).To(Equal("v1")) - Expect(attributeFoo.Type().Owner().Name().String()).To(Equal("v1")) + Expect(attributeFoo.Type().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")) diff --git a/tests/model/aro_hcp/v1_alpha1/cluster_resource.model b/tests/model/aro_hcp/v1_alpha1/cluster_resource.model index d45afaa..4be4abf 100644 --- a/tests/model/aro_hcp/v1_alpha1/cluster_resource.model +++ b/tests/model/aro_hcp/v1_alpha1/cluster_resource.model @@ -39,4 +39,9 @@ resource Cluster { locator NodePools { target NodePools } -} \ No newline at end of file + + // Reference to the resource that manages the detailed status of the cluster. + locator Status { + target ClusterStatus + } +} diff --git a/tests/model/aro_hcp/v1_alpha1/cluster_status_resource.model b/tests/model/aro_hcp/v1_alpha1/cluster_status_resource.model new file mode 100644 index 0000000..62b41b6 --- /dev/null +++ b/tests/model/aro_hcp/v1_alpha1/cluster_status_resource.model @@ -0,0 +1,23 @@ +/* +Copyright (c) 2025 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 status. +resource ClusterStatus { + // Retrieves the details of the node pool. + method Get { + out Body ClusterStatus + } +} diff --git a/tests/model/aro_hcp/v1_alpha1/cluster_status_type.model b/tests/model/aro_hcp/v1_alpha1/cluster_status_type.model new file mode 100644 index 0000000..e00a2eb --- /dev/null +++ b/tests/model/aro_hcp/v1_alpha1/cluster_status_type.model @@ -0,0 +1,19 @@ +/* +Copyright (c) 2025 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_status") +class ClusterStatus { +} diff --git a/tests/model/aro_hcp/v1_alpha1/cluster_type.model b/tests/model/aro_hcp/v1_alpha1/cluster_type.model index 793cc1f..a9e2c02 100644 --- a/tests/model/aro_hcp/v1_alpha1/cluster_type.model +++ b/tests/model/aro_hcp/v1_alpha1/cluster_type.model @@ -16,4 +16,4 @@ 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 index 136b081..b1f311f 100644 --- a/tests/model/aro_hcp/v1_alpha1/clusters_resource.model +++ b/tests/model/aro_hcp/v1_alpha1/clusters_resource.model @@ -76,4 +76,4 @@ resource Clusters { target Cluster variable ID } -} \ No newline at end of file +} diff --git a/tests/model/aro_hcp/v1_alpha1/node_pool_type.model b/tests/model/aro_hcp/v1_alpha1/node_pool_type.model index c94a048..ef6df16 100644 --- a/tests/model/aro_hcp/v1_alpha1/node_pool_type.model +++ b/tests/model/aro_hcp/v1_alpha1/node_pool_type.model @@ -16,4 +16,4 @@ 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 index 09c370b..4408870 100644 --- a/tests/model/aro_hcp/v1_alpha1/node_pools_resource.model +++ b/tests/model/aro_hcp/v1_alpha1/node_pools_resource.model @@ -72,4 +72,4 @@ resource NodePools { target NodePool variable ID } -} \ No newline at end of file +} diff --git a/tests/model/clusters_mgmt/v1/cluster_resource.model b/tests/model/clusters_mgmt/v1/cluster_resource.model index 936609f..fe9f2c3 100644 --- a/tests/model/clusters_mgmt/v1/cluster_resource.model +++ b/tests/model/clusters_mgmt/v1/cluster_resource.model @@ -41,4 +41,14 @@ resource Cluster { locator IdentityProviders { target IdentityProviders } + + // Reference to the NodePool resource + locator NodePools{ + target NodePools + } + + // Reference to the resource that manages the detailed status of the cluster. + locator Status { + target ClusterStatus + } } diff --git a/tests/model/clusters_mgmt/v1/cluster_status_resource.model b/tests/model/clusters_mgmt/v1/cluster_status_resource.model new file mode 100644 index 0000000..62b41b6 --- /dev/null +++ b/tests/model/clusters_mgmt/v1/cluster_status_resource.model @@ -0,0 +1,23 @@ +/* +Copyright (c) 2025 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 status. +resource ClusterStatus { + // Retrieves the details of the node pool. + method Get { + out Body ClusterStatus + } +} diff --git a/tests/model/clusters_mgmt/v1/cluster_status_type.model b/tests/model/clusters_mgmt/v1/cluster_status_type.model new file mode 100644 index 0000000..be4f510 --- /dev/null +++ b/tests/model/clusters_mgmt/v1/cluster_status_type.model @@ -0,0 +1,24 @@ +/* +Copyright (c) 2025 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. +*/ + +// Overall state of a cluster. +class ClusterStatus { + // State value + State ClusterState + + // Human readable value + Message String +} diff --git a/tests/model/clusters_mgmt/v1/cluster_type.model b/tests/model/clusters_mgmt/v1/cluster_type.model index 7fe405f..310f542 100644 --- a/tests/model/clusters_mgmt/v1/cluster_type.model +++ b/tests/model/clusters_mgmt/v1/cluster_type.model @@ -35,7 +35,10 @@ class Cluster { // User defined properties for tagging and querying. Properties [String]String - // Overall state of the cluster. + // Overall status of the cluster. + Status ClusterStatus + + // Overall state of the cluster State ClusterState // Flag indicating if the cluster is managed (by Red Hat) or @@ -67,4 +70,9 @@ class Cluster { // Floating point value used for tests. Factor Float + + // List of node pools on this cluster. + // NodePool is a scalable set of worker nodes attached to a hosted cluster. + link NodePools []NodePool + } diff --git a/tests/model/clusters_mgmt/v1/node_pool_resource.model b/tests/model/clusters_mgmt/v1/node_pool_resource.model new file mode 100644 index 0000000..0ad1969 --- /dev/null +++ b/tests/model/clusters_mgmt/v1/node_pool_resource.model @@ -0,0 +1,32 @@ +/* +Copyright (c) 2025 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/clusters_mgmt/v1/node_pool_type.model b/tests/model/clusters_mgmt/v1/node_pool_type.model index 9c64ffa..a14473a 100644 --- a/tests/model/clusters_mgmt/v1/node_pool_type.model +++ b/tests/model/clusters_mgmt/v1/node_pool_type.model @@ -14,4 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -Class NodePool {} \ No newline at end of file +class NodePool { + // The number of Machines (and Nodes) to create. + // Replicas and autoscaling cannot be used together. + Replicas Integer +} diff --git a/tests/model/clusters_mgmt/v1/node_pools_resource.model b/tests/model/clusters_mgmt/v1/node_pools_resource.model new file mode 100644 index 0000000..461ac93 --- /dev/null +++ b/tests/model/clusters_mgmt/v1/node_pools_resource.model @@ -0,0 +1,75 @@ +/* +Copyright (c) 2025 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 + } +}