From 548d05d6f8b7c846c0bdc9e9976dfb78b6599bea Mon Sep 17 00:00:00 2001 From: Chris Marget Date: Tue, 22 Aug 2023 16:29:17 -0400 Subject: [PATCH 1/4] eliminate use of positional arguments in test --- ...los_connectivity_template_integration_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apstra/two_stage_l3_clos_connectivity_template_integration_test.go b/apstra/two_stage_l3_clos_connectivity_template_integration_test.go index 61400214..4e865897 100644 --- a/apstra/two_stage_l3_clos_connectivity_template_integration_test.go +++ b/apstra/two_stage_l3_clos_connectivity_template_integration_test.go @@ -366,8 +366,8 @@ func TestCtLayout(t *testing.T) { SessionAddressingIpv4: true, }, Subpolicies: []*ConnectivityTemplatePrimitive{ - {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{&rp.Id}}, - {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{&rp.Id}}, + {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{RpToAttach: &rp.Id}}, + {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{RpToAttach: &rp.Id}}, }, }, { @@ -376,8 +376,8 @@ func TestCtLayout(t *testing.T) { SessionAddressingIpv6: true, }, Subpolicies: []*ConnectivityTemplatePrimitive{ - {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{&rp.Id}}, - {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{&rp.Id}}, + {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{RpToAttach: &rp.Id}}, + {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{RpToAttach: &rp.Id}}, }, }, { @@ -413,8 +413,8 @@ func TestCtLayout(t *testing.T) { SessionAddressingIpv4: true, }, Subpolicies: []*ConnectivityTemplatePrimitive{ - {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{&rp.Id}}, - {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{&rp.Id}}, + {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{RpToAttach: &rp.Id}}, + {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{RpToAttach: &rp.Id}}, }, }, { @@ -423,8 +423,8 @@ func TestCtLayout(t *testing.T) { SessionAddressingIpv6: true, }, Subpolicies: []*ConnectivityTemplatePrimitive{ - {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{&rp.Id}}, - {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{&rp.Id}}, + {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{RpToAttach: &rp.Id}}, + {Attributes: &ConnectivityTemplatePrimitiveAttributesAttachExistingRoutingPolicy{RpToAttach: &rp.Id}}, }, }, { From b1b582f40fe90804ce80a64ac6beac5879ae54b1 Mon Sep 17 00:00:00 2001 From: Chris Marget Date: Tue, 22 Aug 2023 16:31:12 -0400 Subject: [PATCH 2/4] bugfix slice out-of-bounds when QEElement has no attributes --- apstra/query_engine.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apstra/query_engine.go b/apstra/query_engine.go index bdb4a9bd..f1b0f792 100644 --- a/apstra/query_engine.go +++ b/apstra/query_engine.go @@ -69,16 +69,17 @@ func (o *QEElement) getLast() *QEElement { func (o *QEElement) String() string { attrsSB := strings.Builder{} - // add first attribute to string builder without leading separator if len(o.attributes) > 0 { + // add first attribute to string builder without leading separator attrsSB.WriteString(o.attributes[0].String()) - } - // remaining attributes added with leading separator - for _, a := range o.attributes[1:] { - attrsSB.WriteString(qEElementAttributeSep) - attrsSB.WriteString(a.String()) + // remaining attributes added with leading separator + for _, a := range o.attributes[1:] { + attrsSB.WriteString(qEElementAttributeSep) + attrsSB.WriteString(a.String()) + } } + return fmt.Sprintf("%s(%s)", o.qeeType, attrsSB.String()) } From d5423af06e64e85badf8cdec57726a31106c77a5 Mon Sep 17 00:00:00 2001 From: Chris Marget Date: Tue, 22 Aug 2023 16:32:17 -0400 Subject: [PATCH 3/4] add `Optional()` method to `MatchQuery` --- apstra/query_engine.go | 13 +++++++++++++ apstra/query_engine_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/apstra/query_engine.go b/apstra/query_engine.go index f1b0f792..2254dd85 100644 --- a/apstra/query_engine.go +++ b/apstra/query_engine.go @@ -169,6 +169,7 @@ type PathQuery struct { blueprintId ObjectId blueprintType BlueprintType where []string + optional bool } func (o *PathQuery) getBlueprintType() BlueprintType { @@ -210,6 +211,11 @@ func (o *PathQuery) String() string { for _, where := range o.where { sb.WriteString(".where(" + where + ")") } + + if o.optional { + return "optional(" + sb.String() + ")" + } + return sb.String() } @@ -372,3 +378,10 @@ func (o *MatchQuery) Match(q *PathQuery) *MatchQuery { o.match = append(o.match, *q) return o } + +func (o *MatchQuery) Optional(q *PathQuery) *MatchQuery { + optional := *q + optional.optional = true + o.match = append(o.match, optional) + return o +} diff --git a/apstra/query_engine_test.go b/apstra/query_engine_test.go index 16602602..137c24e6 100644 --- a/apstra/query_engine_test.go +++ b/apstra/query_engine_test.go @@ -415,3 +415,33 @@ func TestMatchQueryWhere(t *testing.T) { } } } + +func TestQueryMatchOptional(t *testing.T) { + expected := "" + + "match(" + + "" + "node(type='system',system_type='switch').out().node(type='interface').out().node(type='link',name='n_link')," + + "" + "optional(" + + "" + "" + "node(type='link',name='n_link').in_().node(type='tag',name='n_tag')" + + "" + ")" + + ")" + q1 := new(PathQuery). + Node([]QEEAttribute{NodeTypeSystem.QEEAttribute(), {Key: "system_type", Value: QEStringVal("switch")}}). + Out([]QEEAttribute{}). + Node([]QEEAttribute{NodeTypeInterface.QEEAttribute()}). + Out([]QEEAttribute{}). + Node([]QEEAttribute{NodeTypeLink.QEEAttribute(), {Key: "name", Value: QEStringVal("n_link")}}) + + q2 := new(PathQuery). + Node([]QEEAttribute{NodeTypeLink.QEEAttribute(), {Key: "name", Value: QEStringVal("n_link")}}). + In([]QEEAttribute{}). + Node([]QEEAttribute{NodeTypeTag.QEEAttribute(), {Key: "name", Value: QEStringVal("n_tag")}}) + + result := new(MatchQuery). + Match(q1). + Optional(q2). + String() + + if expected != result { + t.Fatalf("expected: %q, got %q", expected, result) + } +} From bdfe8d1c3fe6855a2e1a02b59b56e2fa9f70fe5e Mon Sep 17 00:00:00 2001 From: Chris Marget Date: Tue, 22 Aug 2023 18:06:23 -0400 Subject: [PATCH 4/4] make `optional` part of the `QEQuery` interface --- apstra/query_engine.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/apstra/query_engine.go b/apstra/query_engine.go index 2254dd85..69578162 100644 --- a/apstra/query_engine.go +++ b/apstra/query_engine.go @@ -18,6 +18,7 @@ type QEQuery interface { Do(context.Context, interface{}) error String() string getBlueprintType() BlueprintType + setOptional() } var _ QEQuery = &PathQuery{} @@ -176,6 +177,10 @@ func (o *PathQuery) getBlueprintType() BlueprintType { return o.blueprintType } +func (o *PathQuery) setOptional() { + o.optional = true +} + func (o *PathQuery) Do(ctx context.Context, response interface{}) error { return o.client.runQuery(ctx, o.blueprintId, o, response) } @@ -285,9 +290,10 @@ type MatchQuery struct { context context.Context blueprintId ObjectId blueprintType BlueprintType - match []PathQuery + match []QEQuery firstElement *MatchQueryElement where []string + optional bool } //func (o *MatchQuery) Having(v QEAttrVal) *MatchQuery {} // todo @@ -317,6 +323,10 @@ func (o *MatchQuery) getBlueprintType() BlueprintType { return o.blueprintType } +func (o *MatchQuery) setOptional() { + o.optional = true +} + func (o *MatchQuery) Do(ctx context.Context, response interface{}) error { if o.client == nil { return errors.New("attempt to execute query without setting client") @@ -374,14 +384,13 @@ func (o *MatchQuery) Where(where string) *MatchQuery { return o } -func (o *MatchQuery) Match(q *PathQuery) *MatchQuery { - o.match = append(o.match, *q) +func (o *MatchQuery) Match(q QEQuery) *MatchQuery { + o.match = append(o.match, q) return o } -func (o *MatchQuery) Optional(q *PathQuery) *MatchQuery { - optional := *q - optional.optional = true - o.match = append(o.match, optional) +func (o *MatchQuery) Optional(q QEQuery) *MatchQuery { + q.setOptional() + o.match = append(o.match, q) return o }