diff --git a/console/filter.go b/console/filter.go index dfba45bb..8a4cddf8 100644 --- a/console/filter.go +++ b/console/filter.go @@ -193,6 +193,27 @@ UNION DISTINCT }) } input.Prefix = "" // We have handled this internally + case "srcnetname", "dstnetname": + results := []struct { + Name string `ch:"name"` + }{} + if err := c.d.ClickHouseDB.Conn.Select(ctx, &results, ` +SELECT DISTINCT name +FROM networks +WHERE positionCaseInsensitive(name, $1) >= 1 +ORDER BY name +LIMIT 20`, input.Prefix); err != nil { + c.r.Err(err).Msg("unable to query database") + break + } + for _, result := range results { + completions = append(completions, filterCompletion{ + Label: result.Name, + Detail: "network name", + Quoted: true, + }) + } + input.Prefix = "" case "exportername": column = "ExporterName" detail = "exporter name" diff --git a/console/filter/parser.peg b/console/filter/parser.peg index bfa1f9fa..3de5f9cf 100644 --- a/console/filter/parser.peg +++ b/console/filter/parser.peg @@ -74,6 +74,8 @@ ConditionStringExpr "condition on string" ← / "ExporterGroup"i { return "ExporterGroup", nil } / "SrcCountry"i { return "SrcCountry", nil } / "DstCountry"i { return "DstCountry", nil } + / "SrcNetName"i { return "SrcNetName", nil } + / "DstNetName"i { return "DstNetName", nil } / "InIfName"i { return "InIfName", nil } / "OutIfName"i { return "OutIfName", nil } / "InIfDescription"i { return "InIfDescription", nil } @@ -81,7 +83,7 @@ ConditionStringExpr "condition on string" ← / "InIfConnectivity"i { return "InIfConnectivity", nil } / "OutIfConnectivity"i { return "OutIfConnectivity", nil } / "InIfProvider"i { return "InIfProvider", nil } - / "OutIfProvider"i { return "OutIfProvider", nil }) _ + / "OutIfProvider"i { return "OutIfProvider", nil }) _ rcond:RConditionStringExpr { return fmt.Sprintf("%s %s", toString(column), toString(rcond)), nil } @@ -116,14 +118,14 @@ ConditionForwardingStatusExpr "condition on forwarding status" ← } ConditionPortExpr "condition on port" ← column:("SrcPort"i { return "SrcPort", nil } - / "DstPort"i { return "DstPort", nil }) _ + / "DstPort"i { return "DstPort", nil }) _ operator:("=" / ">=" / "<=" / "<" / ">" / "!=") _ value:Unsigned16 { return fmt.Sprintf("%s %s %s", toString(column), toString(operator), toString(value)), nil } ConditionASExpr "condition on AS number" ← column:("SrcAS"i { return "SrcAS", nil } - / "DstAS"i { return "DstAS", nil }) _ + / "DstAS"i { return "DstAS", nil }) _ rcond:RConditionASExpr { return fmt.Sprintf("%s %s", toString(column), toString(rcond)), nil } diff --git a/console/filter/parser_test.go b/console/filter/parser_test.go index b149a6a4..8d23ada8 100644 --- a/console/filter/parser_test.go +++ b/console/filter/parser_test.go @@ -31,6 +31,8 @@ func TestValidFilter(t *testing.T) { {`ExporterGroup= "group"`, `ExporterGroup = 'group'`}, {`SrcAddr=203.0.113.1`, `SrcAddr = IPv6StringToNum('203.0.113.1')`}, {`DstAddr=203.0.113.2`, `DstAddr = IPv6StringToNum('203.0.113.2')`}, + {`SrcNetName="alpha"`, `SrcNetName = 'alpha'`}, + {`DstNetName="alpha"`, `DstNetName = 'alpha'`}, {`SrcAS=12322`, `SrcAS = 12322`}, {`SrcAS=AS12322`, `SrcAS = 12322`}, {`SrcAS=as12322`, `SrcAS = 12322`}, diff --git a/console/filter_test.go b/console/filter_test.go index c7b8da28..a25aa054 100644 --- a/console/filter_test.go +++ b/console/filter_test.go @@ -45,6 +45,17 @@ LIMIT 20`, Return(nil) mockConn.EXPECT(). Select(gomock.Any(), gomock.Any(), ` +SELECT DISTINCT name +FROM networks +WHERE positionCaseInsensitive(name, $1) >= 1 +ORDER BY name +LIMIT 20`, "c"). + SetArg(1, []struct { + Name string `ch:"name"` + }{{"customer-1"}, {"customer-2"}, {"customer-3"}}). + Return(nil) + mockConn.EXPECT(). + Select(gomock.Any(), gomock.Any(), ` SELECT label, detail FROM ( SELECT concat('AS', toString(DstAS)) AS label, dictGet('asns', 'name', DstAS) AS detail, 1 AS rank FROM flows @@ -108,6 +119,7 @@ UNION DISTINCT {"label": "DstAS", "detail": "column name", "quoted": false}, {"label": "DstAddr", "detail": "column name", "quoted": false}, {"label": "DstCountry", "detail": "column name", "quoted": false}, + {"label": "DstNetName", "detail": "column name", "quoted": false}, {"label": "DstPort", "detail": "column name", "quoted": false}, }}, }, { @@ -179,6 +191,15 @@ UNION DISTINCT {"label": "AS36987", "detail": "Google Kenya", "quoted": false}, {"label": "AS41264", "detail": "Google Switzerland", "quoted": false}, }}, + }, { + URL: "/api/v0/console/filter/complete", + StatusCode: 200, + JSONInput: gin.H{"what": "value", "column": "srcnetName", "prefix": "c"}, + JSONOutput: gin.H{"completions": []gin.H{ + {"label": "customer-1", "detail": "network name", "quoted": true}, + {"label": "customer-2", "detail": "network name", "quoted": true}, + {"label": "customer-3", "detail": "network name", "quoted": true}, + }}, }, }) } diff --git a/console/query.go b/console/query.go index 0b4de29b..b0043d26 100644 --- a/console/query.go +++ b/console/query.go @@ -16,6 +16,7 @@ const ( queryColumnExporterName queryColumnExporterGroup queryColumnSrcAS + queryColumnSrcNetName queryColumnSrcCountry queryColumnInIfName queryColumnInIfDescription @@ -28,6 +29,7 @@ const ( queryColumnSrcPort queryColumnSrcAddr queryColumnDstAS + queryColumnDstNetName queryColumnDstCountry queryColumnOutIfName queryColumnOutIfDescription @@ -49,6 +51,8 @@ var queryColumnMap = helpers.NewBimap(map[queryColumn]string{ queryColumnDstAddr: "DstAddr", queryColumnSrcAS: "SrcAS", queryColumnDstAS: "DstAS", + queryColumnSrcNetName: "SrcNetName", + queryColumnDstNetName: "DstNetName", queryColumnSrcCountry: "SrcCountry", queryColumnDstCountry: "DstCountry", queryColumnInIfName: "InIfName",