From 0e835741df8a8d90a157ad4f15f5d018d4f0aab5 Mon Sep 17 00:00:00 2001 From: Basavaraj PB Date: Sat, 28 Mar 2026 10:13:34 +0530 Subject: [PATCH 1/3] Remove redundant status: enabled from generated components Signed-off-by: Basavaraj PB --- registry/component.go | 2 -- registry/model.go | 14 +++++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/registry/component.go b/registry/component.go index e6ffc9a4..8e2eac87 100644 --- a/registry/component.go +++ b/registry/component.go @@ -58,7 +58,6 @@ type ComponentCSV struct { // The Component Definition generated assumes or is only for components which have registrant as "meshery" func (c *ComponentCSV) CreateComponentDefinition(isModelPublished bool, defVersion string) (component.ComponentDefinition, error) { - status := entity.Enabled if c.Status != "" { if utils.ReplaceSpacesAndConvertToLowercase(c.Status) == "ignored" { status = entity.Ignored @@ -94,7 +93,6 @@ var compStyleValues = []string{ } func (c *ComponentCSV) UpdateCompDefinition(compDef *component.ComponentDefinition) error { - status := entity.Enabled if c.Status != "" { if utils.ReplaceSpacesAndConvertToLowercase(c.Status) == "ignored" { status = entity.Ignored diff --git a/registry/model.go b/registry/model.go index cec8a462..0a701ecc 100644 --- a/registry/model.go +++ b/registry/model.go @@ -1097,11 +1097,13 @@ func GenerateDefsForCoreRegistrant(model ModelCSV, ComponentCSVHelper *Component version: version, }) }() - status := entity.Ignored - if isModelPublished { - status = entity.Enabled + + var _status *component.ComponentDefinitionStatus + if !isModelPublished { + s := component.ComponentDefinitionStatus(entity.Ignored) + _status = &s } - _status := component.ComponentDefinitionStatus(status) + modelDirPath, compDirPath, err := createVersionedDirectoryForModelAndComp(version, model.Model, path) if err != nil { err = ErrGenerateModel(err, model.Model) @@ -1133,7 +1135,9 @@ func GenerateDefsForCoreRegistrant(model ModelCSV, ComponentCSVHelper *Component Log.Error(ErrUpdateComponent(err, modelName, comp.Component)) continue } - componentDef.Status = &_status + if _status != nil { + componentDef.Status = _status + } componentDef.Model = modelDef alreadyExists, err = componentDef.WriteComponentDefinition(compDirPath, "json") if err != nil { From 5e7bacf79ab54cde0bdda9c9fc179225be0c865c Mon Sep 17 00:00:00 2001 From: Basavaraj PB Date: Sun, 29 Mar 2026 16:23:19 +0530 Subject: [PATCH 2/3] fix: remove redundant status propagation from model to component Signed-off-by: Basavaraj PB --- registry/component.go | 19 ++++++++++--------- registry/model.go | 24 ++++++++++++------------ 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/registry/component.go b/registry/component.go index 8e2eac87..f3e7014c 100644 --- a/registry/component.go +++ b/registry/component.go @@ -58,9 +58,13 @@ type ComponentCSV struct { // The Component Definition generated assumes or is only for components which have registrant as "meshery" func (c *ComponentCSV) CreateComponentDefinition(isModelPublished bool, defVersion string) (component.ComponentDefinition, error) { + var statusPtr *component.ComponentDefinitionStatus if c.Status != "" { - if utils.ReplaceSpacesAndConvertToLowercase(c.Status) == "ignored" { - status = entity.Ignored + normalized := utils.ReplaceSpacesAndConvertToLowercase(c.Status) + + if normalized == "ignored" { + s := component.ComponentDefinitionStatus(entity.Ignored) + statusPtr = &s } } componentDefinition := &component.ComponentDefinition{ @@ -71,13 +75,15 @@ func (c *ComponentCSV) CreateComponentDefinition(isModelPublished bool, defVersi Metadata: component.ComponentDefinition_Metadata{ Published: isModelPublished, }, - Status: (*component.ComponentDefinitionStatus)(&status), Component: component.Component{ Kind: c.Component, Schema: c.Schema, Version: c.Version, }, } + if statusPtr != nil { + componentDefinition.Status = statusPtr + } if c.Description != "" { componentDefinition.Description = c.Description } @@ -93,12 +99,7 @@ var compStyleValues = []string{ } func (c *ComponentCSV) UpdateCompDefinition(compDef *component.ComponentDefinition) error { - if c.Status != "" { - if utils.ReplaceSpacesAndConvertToLowercase(c.Status) == "ignored" { - status = entity.Ignored - } - } - compDef.Status = (*component.ComponentDefinitionStatus)(&status) + var existingAddditionalProperties map[string]interface{} if c.Description != "" { compDef.Description = c.Description diff --git a/registry/model.go b/registry/model.go index 0a701ecc..5f5f8648 100644 --- a/registry/model.go +++ b/registry/model.go @@ -60,6 +60,7 @@ var defVersion = "v1.0.0" // keep type ModelCSV struct { + Status *_model.ModelDefinitionStatus `json:"status,omitempty"` Registrant string `json:"registrant" csv:"registrant"` ModelDisplayName string `json:"modelDisplayName" csv:"modelDisplayName"` Model string `json:"model" csv:"model"` @@ -174,9 +175,10 @@ func (m *ModelCSV) UpdateModelDefinition(modelDef *_model.ModelDefinition) error return nil } func (mcv *ModelCSV) CreateModelDefinition(version, defVersion string) _model.ModelDefinition { - status := entity.Ignored - if strings.ToLower(mcv.PublishToRegistry) == "true" { - status = entity.Enabled + var status _model.ModelDefinitionStatus + + if strings.ToLower(strings.TrimSpace(mcv.PublishToRegistry)) != "true" { + status = _model.ModelDefinitionStatus(entity.Ignored) } var catname category.CategoryDefinition catname.Name = mcv.Category @@ -189,7 +191,7 @@ func (mcv *ModelCSV) CreateModelDefinition(version, defVersion string) _model.Mo SchemaVersion: v1beta1.ModelSchemaVersion, Name: mcv.Model, - Status: _model.ModelDefinitionStatus(status), + Status: status, Registrant: registrant, SubCategory: mcv.SubCategory, Model: _model.Model{ @@ -533,8 +535,10 @@ func (m ModelCSVHelper) Cleanup() error { } func AssignDefaultsForCompDefs(componentDef *component.ComponentDefinition, modelDef *_model.ModelDefinition) { // Assign the status from the model to the component - compStatus := component.ComponentDefinitionStatus(modelDef.Status) - componentDef.Status = &compStatus + if modelDef.Status != "" && string(modelDef.Status) != string(entity.Enabled) { + compStatus := component.ComponentDefinitionStatus(modelDef.Status) + componentDef.Status = &compStatus + } // Initialize AdditionalProperties and Styles if nil if componentDef.Metadata.AdditionalProperties == nil { @@ -1080,10 +1084,8 @@ func InvokeGenerationFromSheetWithOptions(wg *sync.WaitGroup, path string, model func GenerateDefsForCoreRegistrant(model ModelCSV, ComponentCSVHelper *ComponentCSVHelper, path string, modelName string) error { var version string parts := strings.Split(model.SourceURL, "/") - // Assuming the URL is always of the format "protocol://github.com/owner/repo/tree/definitions/{model-name}/version/components" - // We know the version is the 7th element (0-indexed) in the split URL if len(parts) >= 8 { - version = parts[8] // Fetch the version from the expected position + version = parts[8] } else { return fmt.Errorf("invalid SourceURL format: %s", model.SourceURL) } @@ -1135,9 +1137,7 @@ func GenerateDefsForCoreRegistrant(model ModelCSV, ComponentCSVHelper *Component Log.Error(ErrUpdateComponent(err, modelName, comp.Component)) continue } - if _status != nil { - componentDef.Status = _status - } + componentDef.Status = _status componentDef.Model = modelDef alreadyExists, err = componentDef.WriteComponentDefinition(compDirPath, "json") if err != nil { From 3e4a987971bd9fe458727230ef7dd0c8f566e45c Mon Sep 17 00:00:00 2001 From: Basavaraj PB Date: Thu, 9 Apr 2026 12:58:48 +0530 Subject: [PATCH 3/3] Add unit tests and improve status handling logic Signed-off-by: Basavaraj PB --- generators/github/acm-controller/Broker.json | 13 +- .../github/acm-controller/MeshSync.json | 13 +- generators/github/channels/Channel.json | 13 +- .../github/gateway-api/GatewayClass.json | 82 ++++++++ registry/component_test.go | 180 ++++++++++-------- registry/relationship.go | 4 +- 6 files changed, 212 insertions(+), 93 deletions(-) create mode 100644 generators/github/gateway-api/GatewayClass.json diff --git a/generators/github/acm-controller/Broker.json b/generators/github/acm-controller/Broker.json index 94751333..e9b4863d 100644 --- a/generators/github/acm-controller/Broker.json +++ b/generators/github/acm-controller/Broker.json @@ -21,7 +21,10 @@ "status": "", "created_at": "0001-01-01T00:00:00Z", "updated_at": "0001-01-01T00:00:00Z", -"deleted_at": null, +"deleted_at": { +"Time": "0001-01-01T00:00:00Z", +"Valid": false +}, "schemaVersion": "" }, "connection_id": "00000000-0000-0000-0000-000000000000", @@ -46,16 +49,16 @@ "relationships": null }, "modelReference": { -"version": "", -"name": "", "displayName": "", "id": "00000000-0000-0000-0000-000000000000", +"model": { +"version": "" +}, +"name": "", "registrant": { "kind": "" }, -"model": { "version": "" -} }, "styles": null, "capabilities": null, diff --git a/generators/github/acm-controller/MeshSync.json b/generators/github/acm-controller/MeshSync.json index 15c88be7..d91f3b1d 100644 --- a/generators/github/acm-controller/MeshSync.json +++ b/generators/github/acm-controller/MeshSync.json @@ -21,7 +21,10 @@ "status": "", "created_at": "0001-01-01T00:00:00Z", "updated_at": "0001-01-01T00:00:00Z", -"deleted_at": null, +"deleted_at": { +"Time": "0001-01-01T00:00:00Z", +"Valid": false +}, "schemaVersion": "" }, "connection_id": "00000000-0000-0000-0000-000000000000", @@ -46,16 +49,16 @@ "relationships": null }, "modelReference": { -"version": "", -"name": "", "displayName": "", "id": "00000000-0000-0000-0000-000000000000", +"model": { +"version": "" +}, +"name": "", "registrant": { "kind": "" }, -"model": { "version": "" -} }, "styles": null, "capabilities": null, diff --git a/generators/github/channels/Channel.json b/generators/github/channels/Channel.json index 050297a8..5d7e9e2d 100644 --- a/generators/github/channels/Channel.json +++ b/generators/github/channels/Channel.json @@ -21,7 +21,10 @@ "status": "", "created_at": "0001-01-01T00:00:00Z", "updated_at": "0001-01-01T00:00:00Z", -"deleted_at": null, +"deleted_at": { +"Time": "0001-01-01T00:00:00Z", +"Valid": false +}, "schemaVersion": "" }, "connection_id": "00000000-0000-0000-0000-000000000000", @@ -46,16 +49,16 @@ "relationships": null }, "modelReference": { -"version": "", -"name": "", "displayName": "", "id": "00000000-0000-0000-0000-000000000000", +"model": { +"version": "" +}, +"name": "", "registrant": { "kind": "" }, -"model": { "version": "" -} }, "styles": null, "capabilities": null, diff --git a/generators/github/gateway-api/GatewayClass.json b/generators/github/gateway-api/GatewayClass.json new file mode 100644 index 00000000..a36c328e --- /dev/null +++ b/generators/github/gateway-api/GatewayClass.json @@ -0,0 +1,82 @@ +{ +"id": "00000000-0000-0000-0000-000000000000", +"schemaVersion": "components.meshery.io/v1beta1", +"version": "", +"displayName": "Gateway Class", +"description": "", +"format": "JSON", +"model": { +"id": "00000000-0000-0000-0000-000000000000", +"schemaVersion": "", +"version": "gateway.networking.k8s.io_gatewayclasses.yaml", +"name": "gateway-api", +"displayName": "gateway-api", +"status": "", +"registrant": { +"id": "00000000-0000-0000-0000-000000000000", +"name": "", +"type": "", +"sub_type": "", +"kind": "", +"status": "", +"created_at": "0001-01-01T00:00:00Z", +"updated_at": "0001-01-01T00:00:00Z", +"deleted_at": { +"Time": "0001-01-01T00:00:00Z", +"Valid": false +}, +"schemaVersion": "" +}, +"connection_id": "00000000-0000-0000-0000-000000000000", +"category": { +"id": "00000000-0000-0000-0000-000000000000", +"name": "" +}, +"subCategory": "", +"metadata": { +"source_uri": "https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/main/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml", +"svgColor": "", +"svgWhite": "" +}, +"model": { +"version": "" +}, +"components_count": 0, +"relationships_count": 0, +"created_at": "0001-01-01T00:00:00Z", +"updated_at": "0001-01-01T00:00:00Z", +"components": null, +"relationships": null +}, +"modelReference": { +"displayName": "", +"id": "00000000-0000-0000-0000-000000000000", +"model": { +"version": "" +}, +"name": "", +"registrant": { +"kind": "" +}, +"version": "" +}, +"styles": null, +"capabilities": null, +"status": null, +"metadata": { +"configurationUISchema": "", +"genealogy": "", +"instanceDetails": null, +"isAnnotation": false, +"isNamespaced": false, +"published": false +}, +"configuration": null, +"component": { +"version": "gateway.networking.k8s.io/v1", +"kind": "GatewayClass", +"schema": "{\n \"description\": \"GatewayClass describes a class of Gateways available to the user for creating\\nGateway resources.\\n\\nIt is recommended that this resource be used as a template for Gateways. This\\nmeans that a Gateway is based on the state of the GatewayClass at the time it\\nwas created and changes to the GatewayClass or associated parameters are not\\npropagated down to existing Gateways. This recommendation is intended to\\nlimit the blast radius of changes to GatewayClass or associated parameters.\\nIf implementations choose to propagate GatewayClass changes to existing\\nGateways, that MUST be clearly documented by the implementation.\\n\\nWhenever one or more Gateways are using a GatewayClass, implementations SHOULD\\nadd the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the\\nassociated GatewayClass. This ensures that a GatewayClass associated with a\\nGateway is not deleted while in use.\\n\\nGatewayClass is a Cluster level resource.\",\n \"properties\": {\n \"spec\": {\n \"description\": \"Spec defines the desired state of GatewayClass.\",\n \"properties\": {\n \"controllerName\": {\n \"description\": \"ControllerName is the name of the controller that is managing Gateways of\\nthis class. The value of this field MUST be a domain prefixed path.\\n\\nExample: \\\"example.net/gateway-controller\\\".\\n\\nThis field is not mutable and cannot be empty.\\n\\nSupport: Core\",\n \"maxLength\": 253,\n \"minLength\": 1,\n \"pattern\": \"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\\\\/[A-Za-z0-9\\\\/\\\\-._~%!$\\u0026'()*+,;=:]+$\",\n \"type\": \"string\",\n \"x-kubernetes-validations\": [\n {\n \"message\": \"Value is immutable\",\n \"rule\": \"self == oldSelf\"\n }\n ]\n },\n \"description\": {\n \"description\": \"Description helps describe a GatewayClass with more details.\",\n \"maxLength\": 64,\n \"type\": \"string\"\n },\n \"parametersRef\": {\n \"description\": \"ParametersRef is a reference to a resource that contains the configuration\\nparameters corresponding to the GatewayClass. This is optional if the\\ncontroller does not require any additional configuration.\\n\\nParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap,\\nor an implementation-specific custom resource. The resource can be\\ncluster-scoped or namespace-scoped.\\n\\nIf the referent cannot be found, refers to an unsupported kind, or when\\nthe data within that resource is malformed, the GatewayClass SHOULD be\\nrejected with the \\\"Accepted\\\" status condition set to \\\"False\\\" and an\\n\\\"InvalidParameters\\\" reason.\\n\\nA Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified,\\nthe merging behavior is implementation specific.\\nIt is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway.\\n\\nSupport: Implementation-specific\",\n \"properties\": {\n \"group\": {\n \"description\": \"Group is the group of the referent.\",\n \"maxLength\": 253,\n \"pattern\": \"^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\",\n \"type\": \"string\"\n },\n \"kind\": {\n \"description\": \"Kind is kind of the referent.\",\n \"maxLength\": 63,\n \"minLength\": 1,\n \"pattern\": \"^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$\",\n \"type\": \"string\"\n },\n \"name\": {\n \"description\": \"Name is the name of the referent.\",\n \"maxLength\": 253,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"namespace\": {\n \"description\": \"Namespace is the namespace of the referent.\\nThis field is required when referring to a Namespace-scoped resource and\\nMUST be unset when referring to a Cluster-scoped resource.\",\n \"maxLength\": 63,\n \"minLength\": 1,\n \"pattern\": \"^[a-z0-9]([-a-z0-9]*[a-z0-9])?$\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"group\",\n \"kind\",\n \"name\"\n ],\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"controllerName\"\n ],\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"spec\"\n ],\n \"title\": \"Gateway Class\",\n \"type\": \"object\"\n}" +}, +"created_at": "0001-01-01T00:00:00Z", +"updated_at": "0001-01-01T00:00:00Z" +} \ No newline at end of file diff --git a/registry/component_test.go b/registry/component_test.go index 09fb16a4..8a4d56ec 100644 --- a/registry/component_test.go +++ b/registry/component_test.go @@ -1,85 +1,113 @@ package registry import ( - "testing" - "github.com/stretchr/testify/assert" - "github.com/meshery/schemas/models/v1beta1/component" + "testing" + + "github.com/meshery/meshkit/models/meshmodel/entity" + "github.com/meshery/schemas/models/v1beta1/component" + "github.com/meshery/schemas/models/v1beta1/model" + "github.com/stretchr/testify/assert" ) func TestUpdateCompDefinitionWithDefaultCapabilities(t *testing.T) { - tests := []struct { - name string - csvCapabilities string - expectedCapabilitiesLen int - shouldHaveDefaultCaps bool - }{ - { - name: "Empty capabilities should get defaults", - csvCapabilities: "", - expectedCapabilitiesLen: 3, - shouldHaveDefaultCaps: true, - }, - { - name: "Null capabilities should get defaults", - csvCapabilities: "null", - expectedCapabilitiesLen: 3, - shouldHaveDefaultCaps: true, - }, - { - name: "Existing capabilities should be preserved", - csvCapabilities: `[{"displayName":"Custom Cap","kind":"test"}]`, - expectedCapabilitiesLen: 1, - shouldHaveDefaultCaps: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - comp := ComponentCSV{ - Component: "TestComponent", - Capabilities: tt.csvCapabilities, - Registrant: "meshery", - Model: "test-model", - } - - compDef := &component.ComponentDefinition{} - - err := comp.UpdateCompDefinition(compDef) - - assert.NoError(t, err) - assert.NotNil(t, compDef.Capabilities) - assert.Len(t, *compDef.Capabilities, tt.expectedCapabilitiesLen) - - if tt.shouldHaveDefaultCaps { - capabilities := *compDef.Capabilities - - expectedNames := []string{"Styling", "Change Shape", "Compound Drag And Drop"} - actualNames := make([]string, len(capabilities)) - for i, cap := range capabilities { - actualNames[i] = cap.DisplayName - } - - assert.ElementsMatch(t, expectedNames, actualNames) - - assert.Equal(t, "Styling", capabilities[0].DisplayName) - assert.Equal(t, "mutate", capabilities[0].Kind) - assert.Equal(t, "style", capabilities[0].Type) - } - }) - } + tests := []struct { + name string + csvCapabilities string + expectedCapabilitiesLen int + shouldHaveDefaultCaps bool + }{ + { + name: "Empty capabilities should get defaults", + csvCapabilities: "", + expectedCapabilitiesLen: 3, + shouldHaveDefaultCaps: true, + }, + { + name: "Null capabilities should get defaults", + csvCapabilities: "null", + expectedCapabilitiesLen: 3, + shouldHaveDefaultCaps: true, + }, + { + name: "Existing capabilities should be preserved", + csvCapabilities: `[{"displayName":"Custom Cap","kind":"test"}]`, + expectedCapabilitiesLen: 1, + shouldHaveDefaultCaps: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + comp := ComponentCSV{ + Component: "TestComponent", + Capabilities: tt.csvCapabilities, + Registrant: "meshery", + Model: "test-model", + } + + compDef := &component.ComponentDefinition{} + + err := comp.UpdateCompDefinition(compDef) + + assert.NoError(t, err) + assert.NotNil(t, compDef.Capabilities) + assert.Len(t, *compDef.Capabilities, tt.expectedCapabilitiesLen) + + if tt.shouldHaveDefaultCaps { + capabilities := *compDef.Capabilities + + expectedNames := []string{"Styling", "Change Shape", "Compound Drag And Drop"} + actualNames := make([]string, len(capabilities)) + for i, cap := range capabilities { + actualNames[i] = cap.DisplayName + } + + assert.ElementsMatch(t, expectedNames, actualNames) + + assert.Equal(t, "Styling", capabilities[0].DisplayName) + assert.Equal(t, "mutate", capabilities[0].Kind) + assert.Equal(t, "style", capabilities[0].Type) + } + }) + } } func TestGetMinimalUICapabilitiesFromSchema(t *testing.T) { - capabilities, err := getMinimalUICapabilitiesFromSchema() - - assert.NoError(t, err) - assert.Len(t, capabilities, 3) - - expectedNames := []string{"Styling", "Change Shape", "Compound Drag And Drop"} - actualNames := make([]string, len(capabilities)) - for i, cap := range capabilities { - actualNames[i] = cap.DisplayName - } - - assert.ElementsMatch(t, expectedNames, actualNames) -} \ No newline at end of file + capabilities, err := getMinimalUICapabilitiesFromSchema() + + assert.NoError(t, err) + assert.Len(t, capabilities, 3) + + expectedNames := []string{"Styling", "Change Shape", "Compound Drag And Drop"} + actualNames := make([]string, len(capabilities)) + for i, cap := range capabilities { + actualNames[i] = cap.DisplayName + } + + assert.ElementsMatch(t, expectedNames, actualNames) +} + +func TestAssignDefaultsForCompDefs_StatusHandling(t *testing.T) { + // Case 1: model status = "" + modelDef := &model.ModelDefinition{ + Status: "", + } + + comp := &component.ComponentDefinition{} + + AssignDefaultsForCompDefs(comp, modelDef) + + if comp.Status != nil { + t.Errorf("expected component status to be nil when model status is empty") + } + + // Case 2: model status = ignored + ignored := model.ModelDefinitionStatus(entity.Ignored) + modelDef.Status = ignored + + AssignDefaultsForCompDefs(comp, modelDef) + + if comp.Status == nil { + t.Errorf("expected component status to be set when model status is ignored") + } +} diff --git a/registry/relationship.go b/registry/relationship.go index 05df19b9..560055b6 100644 --- a/registry/relationship.go +++ b/registry/relationship.go @@ -173,14 +173,14 @@ func ProcessRelationships(relationshipCSVHelper *RelationshipCSVHelper, spreadsh continue } if rel.Metadata == nil { - rel.Metadata = &_rel.Relationship_Metadata{} + rel.Metadata = &_rel.RelationshipMetadata{} } } annotation := false if utils.ReplaceSpacesAndConvertToLowercase(relationship.IsAnnotation) == "true" { annotation = true } - rel.Metadata = &_rel.Relationship_Metadata{ + rel.Metadata = &_rel.RelationshipMetadata{ Description: &relationship.Description, IsAnnotation: &annotation, Styles: &styles,