diff --git a/pkg/tree/ops/xml.go b/pkg/tree/ops/xml.go index a561ad86..d1d2c35e 100644 --- a/pkg/tree/ops/xml.go +++ b/pkg/tree/ops/xml.go @@ -132,7 +132,7 @@ func toXmlInternal(ctx context.Context, e api.Entry, parent *etree.Element, only } } return overallDoAdd, nil - case e.ShouldDelete(): + case e.ShouldDelete() && e.GetParent() != nil: // s is meant to be removed // if delete, create the element as child of parent newElem := parent.CreateElement(e.PathName()) @@ -213,6 +213,15 @@ func toXmlInternal(ctx context.Context, e api.Entry, parent *etree.Element, only case *sdcpb.SchemaElem_Leaflist, *sdcpb.SchemaElem_Field: // check if the element remains to exist if e.ShouldDelete() { + if e.GetParent() != nil && e.GetParent().GetSchema() == nil { + ancestorWithSchema, _ := GetFirstAncestorWithSchema(e.GetParent()) + if ancestorWithSchema != nil && slices.Contains(GetSchemaKeys(ancestorWithSchema), e.PathName()) { + // Key leaf deletions should delete the list entry identified by key value. + utils.AddXMLOperation(parent, utils.XMLOperationDelete, operationWithNamespace, useOperationRemove) + xmlAddKeyElements(e.GetParent(), parent) + return true, nil + } + } // if not, add the remove / delete op utils.AddXMLOperation(parent.CreateElement(e.PathName()), utils.XMLOperationDelete, operationWithNamespace, useOperationRemove) // see case nil for an explanation of this, it is basically the same diff --git a/pkg/tree/ops/xml_test.go b/pkg/tree/ops/xml_test.go index 5d489959..2675423f 100644 --- a/pkg/tree/ops/xml_test.go +++ b/pkg/tree/ops/xml_test.go @@ -11,9 +11,11 @@ import ( "github.com/openconfig/ygot/ygot" "github.com/sdcio/data-server/pkg/pool" "github.com/sdcio/data-server/pkg/tree" + "github.com/sdcio/data-server/pkg/tree/api" "github.com/sdcio/data-server/pkg/tree/consts" "github.com/sdcio/data-server/pkg/tree/ops" "github.com/sdcio/data-server/pkg/tree/processors" + "github.com/sdcio/data-server/pkg/tree/types" "github.com/sdcio/data-server/pkg/utils" "github.com/sdcio/data-server/pkg/utils/testhelper" @@ -637,3 +639,80 @@ func TestToXMLTable(t *testing.T) { }) } } + +func TestToXML_KeyLeafDeleteUpgradesToListEntryDelete(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + scb, err := testhelper.GetSchemaClientBound(t, mockCtrl) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + converter := utils.NewConverter(scb) + tc := tree.NewTreeContext(scb, pool.NewSharedTaskPool(ctx, runtime.GOMAXPROCS(0))) + root, err := tree.NewTreeRoot(ctx, tc) + if err != nil { + t.Fatal(err) + } + + runningCfg := &sdcio_schema.Device{ + Patterntest: ygot.String("hallo 00"), + NetworkInstance: map[string]*sdcio_schema.SdcioModel_NetworkInstance{ + "default": { + AdminState: sdcio_schema.SdcioModelNi_AdminState_enable, + Description: ygot.String("Default NI"), + Type: sdcio_schema.SdcioModelNi_NiType_default, + Name: ygot.String("default"), + }, + }, + } + runningUpds, err := testhelper.ExpandUpdateFromConfig(ctx, runningCfg, converter) + if err != nil { + t.Fatal(err) + } + err = testhelper.AddToRoot(ctx, root.Entry, runningUpds, testhelper.FlagsExisting, consts.RunningIntentName, consts.RunningValuesPrio) + if err != nil { + t.Fatal(err) + } + + nameEntry, err := ops.NavigateSdcpbPath(ctx, root.Entry, &sdcpb.Path{Elem: []*sdcpb.PathElem{{Name: "network-instance", Key: map[string]string{"name": "default"}}, {Name: "name"}}}) + if err != nil { + t.Fatal(err) + } + deleteLeaf := api.NewLeafEntry( + types.NewUpdate(nameEntry, &sdcpb.TypedValue{Value: &sdcpb.TypedValue_StringVal{StringVal: "default"}}, 5, "owner1", 0), + testhelper.FlagsDelete, + nameEntry, + ) + nameEntry.GetLeafVariants().Add(deleteLeaf) + err = nil + if err != nil { + t.Fatal(err) + } + + err = root.FinishInsertionPhase(ctx) + if err != nil { + t.Fatal(err) + } + + xmlDoc, err := ops.ToXML(ctx, root.Entry, true, false, false, false) + if err != nil { + t.Fatal(err) + } + utils.XmlRecursiveSortElementsByTagName(&xmlDoc.Element) + xmlDoc.Indent(2) + xmlDocStr, err := xmlDoc.WriteToString() + if err != nil { + t.Fatal(err) + } + + want := ` + default + +` + if diff := cmp.Diff(want, xmlDocStr); diff != "" { + t.Fatalf("ToXML() mismatch (-want +got)\n%s", diff) + } +}