Skip to content

Commit

Permalink
Add support for 'ref' annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
nimrodshn committed Dec 3, 2024
1 parent 30081e9 commit c64d3a7
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 2 deletions.
14 changes: 14 additions & 0 deletions pkg/annotations/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
14 changes: 14 additions & 0 deletions pkg/concepts/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package concepts

import (
"log"
"sort"

"github.com/openshift-online/ocm-api-metamodel/pkg/names"
Expand Down Expand Up @@ -185,6 +186,19 @@ func (t *Type) AddAttribute(attribute *Attribute) {
}
}

// Remove attribute removes an attribute with a given name.
func (t *Type) RemoveAttribute(name *names.Name) {
if name == nil {
return
}
for i, attribute := range t.attributes {
if attribute.Name().Equals(name) {
log.Printf("---------- Deleting attribute %s", name.String())
t.attributes = append(t.attributes[:i], t.attributes[i+1:]...)
}
}
}

// 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 {
Expand Down
4 changes: 2 additions & 2 deletions pkg/language/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,8 @@ func (r *Reader) checkParameter(parameter *concepts.Parameter) {
}
if typ != nil && typ != parameter.Type() {
r.reporter.Errorf(
"Type of default value of parameter '%s' should be '%s'",
parameter, parameter.Type(),
"Type of default value of parameter '%s' should be '%s', instead it was %s",
parameter, parameter.Type(), typ.Name().String(),
)
}
}
Expand Down
114 changes: 114 additions & 0 deletions pkg/language/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -428,6 +429,58 @@ func (r *Reader) ExitClassDecl(ctx *ClassDeclContext) {
typ.AddAttribute(memberCtx.GetResult())
}
}

if path := annotations.ReferencePath(typ); path != "" {
if len(r.inputs) > 1 {
panic("refernced service with multiple inputs in undefined")
}

if r.service.Versions().Len() > 1 {
panic("cannot infer which version to add reference with multiple versions")
}

input := r.inputs[0]
currVersion := r.service.Versions()[0]
path = strings.TrimPrefix(path, "/")
components := strings.Split(path, "/")
referencedServiceName := components[0]
referencedVersion := components[1]
referencedType := 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 locator.
refReader.loadService(fmt.Sprintf("%s/%s", input, referencedServiceName))
refVersion := refReader.service.FindVersion(names.ParseUsingSeparator(referencedVersion, "_"))
for _, currType := range refVersion.Types() {
if strings.Compare(currType.Name().String(), referencedType) == 0 {
r.recursivelyAddTypeToVersion(currVersion, currType)
}
}
}
}

// A helper function to recursively add types to a version
func (r *Reader) recursivelyAddTypeToVersion(version *concepts.Version, typ *concepts.Type) {
version.AddType(typ)
for _, attribute := range typ.Attributes() {
// We wish to define links explicitly and not inherint them
// only the attribute fields.
if version.FindType(attribute.Type().Name()) == nil { //&& !attribute.Link()
r.recursivelyAddTypeToVersion(version, attribute.Type())
}
// if attribute.Link() {
// typ.RemoveAttribute(attribute.Name())
// }
}
}

func (r *Reader) ExitStructDecl(ctx *StructDeclContext) {
Expand Down Expand Up @@ -737,6 +790,67 @@ func (r *Reader) ExitLocatorDecl(ctx *LocatorDeclContext) {
// Add the annotations:
r.addAnnotations(locator, ctx.GetAnnotations())

if path := annotations.ReferencePath(locator); path != "" {
if len(r.inputs) > 1 {
panic("refernced service with multiple inputs in undefined")
}

if r.service.Versions().Len() > 1 {
panic("cannot infer which version to add reference with multiple versions")
}

input := r.inputs[0]
currVersion := r.service.Versions()[0]
path = strings.TrimPrefix(path, "/")
components := strings.Split(path, "/")
referencedServiceName := components[0]
referencedVersion := components[1]
referencedLocator := 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 locator.
refReader.loadService(fmt.Sprintf("%s/%s", input, referencedServiceName))
refVersion := refReader.service.FindVersion(names.ParseUsingSeparator(referencedVersion, "_"))
for _, currLocator := range refVersion.Root().Locators() {
if strings.Compare(currLocator.Name().String(), referencedLocator) == 0 {
for _, resource := range refVersion.Resources() {
if resource.IsRoot() {
// We do not need to copy over the root resource.
continue
}

// Add any underlying resource to the current version.
currVersion.AddResource(resource)
// Add underlying methods.
for _, method := range resource.Methods() {
currVersion.
FindResource(resource.Name()).
AddMethod(method)
}

// Remove any undefined resources as
// This is a no-op except for the reference target added
// by the locator as the compiler will attempt to compile it
r.removeUndefinedResource(resource)
}

currVersion.AddTypes(refVersion.Types())
locator = currLocator
ctx.SetResult(locator)
return
}
}
}

// Add the members:
membersCtxs := ctx.GetMembers()
if len(membersCtxs) > 0 {
Expand Down

0 comments on commit c64d3a7

Please sign in to comment.