Skip to content

Commit

Permalink
Merge pull request #114 from hellt/selfclosing-tags-enhancement
Browse files Browse the repository at this point in the history
enhanced forceSelfClosingTags
  • Loading branch information
carlmontanari authored Jan 22, 2023
2 parents 583fc0f + 7a211e9 commit a4ea189
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 17 deletions.
5 changes: 4 additions & 1 deletion driver/netconf/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ const (

subscriptionResultPattern = `(?i)<subscription-result.*>notif-bis:(.+)</subscription-result>`

emptyTagPattern = `<(\w+)></\w+>`
// emptyTagPattern matches netconf empty tags to allow
// forcing of self-closing tags.
// See https://regex101.com/r/rmsS2E/3.
emptyTagPattern = `<([^>/]+?)(\s+[^>]+?)?>\s*</[\w-]+>`

defaultNamespace = "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults"

Expand Down
25 changes: 9 additions & 16 deletions driver/netconf/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,17 @@ func (d *Driver) RPC(opts ...util.Option) (*response.NetconfResponse, error) {
return d.sendRPC(d.buildRPCElem(op.Filter), op)
}

func forceSelfClosingTags(b []byte) []byte {
// ForceSelfClosingTags accepts a netconf looking xml byte slice and forces any "empty" tags (tags
// without attributes) to use self-closing tags. For example:
// `<running> </running>`
// Would be converted to:
// `<running/>`.
func ForceSelfClosingTags(b []byte) []byte {
ncPatterns := getNetconfPatterns()

emptyTagIdxs := ncPatterns.emptyTags.FindAllSubmatchIndex(b, -1)
r := ncPatterns.emptyTags.ReplaceAll(b, []byte("<$1$2/>"))

var nb []byte

for _, idx := range emptyTagIdxs {
// get everything in b up till the first of the submatch indexes (this is the start of an
// "empty" <thing></thing> tag), then get the name of the tag and put it in a self-closing
// tag.
nb = append(b[0:idx[0]], fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...) //nolint: gocritic

// finally, append everything *after* the submatch indexes
nb = append(nb, b[len(b)-(len(b)-idx[1]):]...)
}

return nb
return r
}

func (d *Driver) sendRPC(
Expand All @@ -58,7 +51,7 @@ func (d *Driver) sendRPC(
if d.ForceSelfClosingTags {
d.Logger.Debug("ForceSelfClosingTags is true, enforcing...")

b = forceSelfClosingTags(b)
b = ForceSelfClosingTags(b)
}

d.Logger.Debugf("sending finalized rpc payload:\n%s", string(b))
Expand Down
57 changes: 57 additions & 0 deletions driver/netconf/rpc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package netconf_test

import (
"testing"

"github.com/scrapli/scrapligo/driver/netconf"

"github.com/google/go-cmp/cmp"
)

func TestForceSelfClosingTags(t *testing.T) {
tests := map[string]struct {
got []byte
want []byte
}{
"empty_tag_no_attrs": {
got: []byte(
`<?xml version="1.0" encoding="UTF-8"?><rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><get-config><source><running></running></source></get-config></rpc>]]>]]>`, //nolint: lll
),
want: []byte(
`<?xml version="1.0" encoding="UTF-8"?><rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><get-config><source><running/></source></get-config></rpc>]]>]]>`, //nolint: lll
),
},
"empty_tag_with_attrs": {
got: []byte(
`<?xml version="1.0" encoding="UTF-8"?><rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><get><filter type="subtree"><routing-policy xmlns="http://openconfig.net/yang/routing-policy"></routing-policy></filter></get></rpc>]]>]]>`, //nolint: lll
),
want: []byte(
`<?xml version="1.0" encoding="UTF-8"?><rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><get><filter type="subtree"><routing-policy xmlns="http://openconfig.net/yang/routing-policy"/></filter></get></rpc>]]>]]>`, //nolint: lll
),
},
"empty_tag_with_attrs_and_spaces": {
got: []byte(
`<routing-policy xmlns="http://openconfig.net/yang/routing-policy"> </routing-policy>`,
),
want: []byte(`<routing-policy xmlns="http://openconfig.net/yang/routing-policy"/>`),
},
"empty_tag_no_attrs_and_spaces": {
got: []byte(`<running> </running>`),
want: []byte(`<running/>`),
},
}

for name, tt := range tests {
t.Run(name, func(t *testing.T) {
got := netconf.ForceSelfClosingTags(tt.got)
if !cmp.Equal(got, tt.want) {
t.Fatalf(
"%s: actual and expected values do not match\nactual: %s\nexpected:%s",
name,
got,
tt.want,
)
}
})
}
}

0 comments on commit a4ea189

Please sign in to comment.