render filterable communities
This commit is contained in:
parent
d7f3b6884c
commit
46eb39f9b5
@ -308,7 +308,8 @@ type PaginatedResponse struct {
|
||||
}
|
||||
|
||||
type FilterableResponse struct {
|
||||
Filters *SearchFilters `json:"filters"`
|
||||
FiltersAvailable *SearchFilters `json:"filters_available"`
|
||||
FiltersApplied *SearchFilters `json:"filters_applied"`
|
||||
}
|
||||
|
||||
type PaginatedRoutesResponse struct {
|
||||
|
@ -123,7 +123,8 @@ func apiLookupPrefixGlobal(
|
||||
},
|
||||
},
|
||||
FilterableResponse: api.FilterableResponse{
|
||||
Filters: filtersAvailable,
|
||||
FiltersAvailable: filtersAvailable,
|
||||
FiltersApplied: filters,
|
||||
},
|
||||
}
|
||||
|
||||
|
13
client/components/lookup/filter-groups.jsx
Normal file
13
client/components/lookup/filter-groups.jsx
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
export const FILTER_KEY_SOURCES = "sources"
|
||||
export const FILTER_KEY_ASNS = "asns"
|
||||
export const FILTER_KEY_COMMUNITIES = "communities"
|
||||
export const FILTER_KEY_EXT_COMMUNITIES = "ext_communities"
|
||||
export const FILTER_KEY_LARGE_COMMUNITIES = "large_communities"
|
||||
|
||||
export const FILTER_GROUP_SOURCES = 0
|
||||
export const FILTER_GROUP_ASNS = 1
|
||||
export const FILTER_GROUP_COMMUNITIES = 2
|
||||
export const FILTER_GROUP_EXT_COMMUNITIES = 3
|
||||
export const FILTER_GROUP_LARGE_COMMUNITIES = 4
|
||||
|
220
client/components/lookup/filters.jsx
Normal file
220
client/components/lookup/filters.jsx
Normal file
@ -0,0 +1,220 @@
|
||||
|
||||
|
||||
import React from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import {makeReadableCommunity}
|
||||
from 'components/routeservers/communities/utils'
|
||||
|
||||
import {FILTER_GROUP_SOURCES,
|
||||
FILTER_GROUP_ASNS,
|
||||
FILTER_GROUP_COMMUNITIES,
|
||||
FILTER_GROUP_EXT_COMMUNITIES,
|
||||
FILTER_GROUP_LARGE_COMMUNITIES}
|
||||
from './filter-groups'
|
||||
|
||||
|
||||
|
||||
class RouteserversSelect extends React.Component {
|
||||
render() {
|
||||
// Sort filters available
|
||||
const sortedFiltersAvailable = this.props.available.sort((a, b) => {
|
||||
return a.value - b.value;
|
||||
});
|
||||
|
||||
// Build options
|
||||
const optionsAvailable = sortedFiltersAvailable.map((filter) => {
|
||||
return <option key={filter.value} value={filter.value}>
|
||||
{filter.name} ({filter.cardinality})
|
||||
</option>;
|
||||
});
|
||||
|
||||
return (
|
||||
<table className="select-ctrl">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="select-container">
|
||||
<select className="form-control">
|
||||
{optionsAvailable}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PeersFilterSelect extends React.Component {
|
||||
render() {
|
||||
// Sort filters available
|
||||
const sortedFiltersAvailable = this.props.available.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
// Build options
|
||||
const optionsAvailable = sortedFiltersAvailable.map((filter) => {
|
||||
return <option key={filter.value} value={filter.value}>
|
||||
{filter.name} ({filter.cardinality})
|
||||
</option>;
|
||||
});
|
||||
|
||||
return (
|
||||
<table className="select-ctrl">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="select-container">
|
||||
<select className="form-control">
|
||||
{optionsAvailable}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class _CommunitiesSelect extends React.Component {
|
||||
render() {
|
||||
const communitiesAvailable = this.props.available.communities.sort((a, b) => {
|
||||
return (a.value[0] - b.value[0]) * 100000 + (a.value[1] - b.value[1]);
|
||||
});
|
||||
|
||||
const extCommunitiesAvailable = this.props.available.ext.sort((a, b) => {
|
||||
return (a.value[1] - b.value[1]) * 100000 + (a.value[2] - b.value[2]);
|
||||
});
|
||||
|
||||
const largeCommunitiesAvailable = this.props.available.large.sort((a, b) => {
|
||||
return (a.value[0] - b.value[0]) * 10000000000 +
|
||||
(a.value[1] - b.value[1]) * 100000 +
|
||||
(a.value[2] - b.value[2]);
|
||||
});
|
||||
|
||||
const communitiesOptions = communitiesAvailable.map((filter) => {
|
||||
const name = makeReadableCommunity(this.props.communities, filter.value);
|
||||
const cls = `select-bgp-community-0-${filter.value[0]} ` +
|
||||
`select-bgp-community-1-${filter.value[1]}`;
|
||||
return (
|
||||
<option key={filter.value} value={filter.value} className={cls}>
|
||||
{filter.name} {name} ({filter.cardinality})
|
||||
</option>
|
||||
);
|
||||
});
|
||||
|
||||
const extCommunitiesOptions = extCommunitiesAvailable.map((filter) => {
|
||||
const name = makeReadableCommunity(this.props.communities, filter.value);
|
||||
const cls = `select-bgp-community-0-${filter.value[0]} ` +
|
||||
`select-bgp-community-1-${filter.value[1]}` +
|
||||
`select-bgp_community-2-${filter.value[2]}`;
|
||||
return (
|
||||
<option key={filter.value} value={filter.value} className={cls}>
|
||||
{filter.name} {name} ({filter.cardinality})
|
||||
</option>
|
||||
);
|
||||
});
|
||||
|
||||
const largeCommunitiesOptions = largeCommunitiesAvailable.map((filter) => {
|
||||
const name = makeReadableCommunity(this.props.communities, filter.value);
|
||||
const cls = `select-bgp-community-0-${filter.value[0]} ` +
|
||||
`select-bgp-community-1-${filter.value[1]}` +
|
||||
`select-bgp_community-2-${filter.value[2]}`;
|
||||
return (
|
||||
<option key={filter.value} value={filter.value} className={cls}>
|
||||
{filter.name} {name} ({filter.cardinality})
|
||||
</option>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
return (
|
||||
<table className="select-ctrl">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="select-container">
|
||||
<select className="form-control">
|
||||
{communitiesOptions.length > 0 &&
|
||||
<optgroup label="Communities">
|
||||
{communitiesOptions}
|
||||
</optgroup>}
|
||||
|
||||
{extCommunitiesOptions.length > 0 &&
|
||||
<optgroup label="Ext. Communities">
|
||||
{extCommunitiesOptions}
|
||||
</optgroup>}
|
||||
|
||||
{largeCommunitiesOptions.length > 0 &&
|
||||
<optgroup label="Large Communities">
|
||||
{largeCommunitiesOptions}
|
||||
</optgroup>}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button className="btn">
|
||||
<i className="fa fa-plus"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const CommunitiesSelect = connect(
|
||||
(state) => ({
|
||||
communities: state.config.bgp_communities,
|
||||
})
|
||||
)(_CommunitiesSelect);
|
||||
|
||||
|
||||
class FiltersEditor extends React.Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="card lookup-filters-editor">
|
||||
<h2>Route server</h2>
|
||||
<RouteserversSelect available={this.props.availableSources}
|
||||
applied={this.props.appliedSources} />
|
||||
|
||||
<h2>Neighbor</h2>
|
||||
<PeersFilterSelect available={this.props.availableAsns}
|
||||
applied={this.props.appliedAsns} />
|
||||
|
||||
<h2>Communities</h2>
|
||||
<CommunitiesSelect available={this.props.availableCommunities}
|
||||
applied={this.props.appliedCommunities} />
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state) => ({
|
||||
available: state.lookup.filtersAvailable,
|
||||
applied: state.lookup.filtersApplied,
|
||||
|
||||
availableSources: state.lookup.filtersAvailable[FILTER_GROUP_SOURCES].filters,
|
||||
appliedSources: state.lookup.filtersApplied[FILTER_GROUP_SOURCES].filters,
|
||||
|
||||
availableAsns: state.lookup.filtersAvailable[FILTER_GROUP_ASNS].filters,
|
||||
appliedAsns: state.lookup.filtersApplied[FILTER_GROUP_ASNS].filters,
|
||||
|
||||
availableCommunities: {
|
||||
communities: state.lookup.filtersAvailable[FILTER_GROUP_COMMUNITIES].filters,
|
||||
ext: state.lookup.filtersAvailable[FILTER_GROUP_EXT_COMMUNITIES].filters,
|
||||
large: state.lookup.filtersAvailable[FILTER_GROUP_LARGE_COMMUNITIES].filters,
|
||||
},
|
||||
appliedCommunities: {
|
||||
communities: state.lookup.filtersApplied[FILTER_GROUP_COMMUNITIES].filters,
|
||||
ext: state.lookup.filtersApplied[FILTER_GROUP_EXT_COMMUNITIES].filters,
|
||||
large: state.lookup.filtersApplied[FILTER_GROUP_LARGE_COMMUNITIES].filters,
|
||||
},
|
||||
|
||||
})
|
||||
)(FiltersEditor);
|
||||
|
@ -6,6 +6,7 @@ import PageHeader from 'components/page-header'
|
||||
|
||||
import Lookup from 'components/lookup'
|
||||
import LookupSummary from 'components/lookup/results-summary'
|
||||
import LookupFilters from 'components/lookup/filters'
|
||||
|
||||
import Content from 'components/content'
|
||||
|
||||
@ -22,6 +23,7 @@ class _LookupView extends React.Component {
|
||||
</div>
|
||||
<div className="col-aside-details col-lg-3 col-md-12">
|
||||
<LookupSummary />
|
||||
<LookupFilters />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -13,12 +13,23 @@ import {LOAD_RESULTS_REQUEST,
|
||||
|
||||
const LOCATION_CHANGE = '@@router/LOCATION_CHANGE'
|
||||
|
||||
const initialFilterState = [
|
||||
{"key": "sources", "filters": []},
|
||||
{"key": "asns", "filters": []},
|
||||
{"key": "communities", "filters": []},
|
||||
{"key": "ext_communities", "filters": []},
|
||||
{"key": "large_communities", "filters": []},
|
||||
];
|
||||
|
||||
const initialState = {
|
||||
query: "",
|
||||
queryValue: "",
|
||||
|
||||
anchor: "",
|
||||
|
||||
filtersAvailable: initialFilterState,
|
||||
filtersApplied: initialFilterState,
|
||||
|
||||
routesImported: [],
|
||||
routesFiltered: [],
|
||||
|
||||
@ -91,6 +102,10 @@ const _loadQueryResult = function(state, payload) {
|
||||
routesImported: imported.routes,
|
||||
routesFiltered: filtered.routes,
|
||||
|
||||
// Filters available
|
||||
filtersAvailable: results.filters_available,
|
||||
filtersApplied: results.filters_applied,
|
||||
|
||||
// Pagination
|
||||
pageImported: imported.pagination.page,
|
||||
pageFiltered: filtered.pagination.page,
|
||||
@ -101,7 +116,6 @@ const _loadQueryResult = function(state, payload) {
|
||||
totalRoutesImported: imported.pagination.total_results,
|
||||
totalRoutesFiltered: filtered.pagination.total_results,
|
||||
|
||||
|
||||
// Statistics
|
||||
queryDurationMs: results.request_duration_ms,
|
||||
totalRoutes: imported.pagination.total_results + filtered.pagination.total_results
|
||||
|
@ -2,28 +2,7 @@
|
||||
import React from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import {resolveCommunity} from './utils'
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Expand variables in string:
|
||||
* "Test AS$0 rejects $2"
|
||||
* will expand with [23, 42, 123] to
|
||||
* "Test AS23 rejects 123"
|
||||
*/
|
||||
function _expandVars(str, vars) {
|
||||
if (!str) {
|
||||
return str; // We don't have to do anything.
|
||||
}
|
||||
|
||||
var res = str;
|
||||
vars.map((v, i) => {
|
||||
res = res.replace(`$${i}`, v);
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
import {makeReadableCommunity} from './utils'
|
||||
|
||||
/*
|
||||
* Make style tags
|
||||
@ -36,12 +15,15 @@ function _makeStyleTags(community) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Render community label
|
||||
*/
|
||||
class Label extends React.Component {
|
||||
render() {
|
||||
|
||||
// Lookup communities
|
||||
const readableCommunityLabel = resolveCommunity(this.props.communities, this.props.community);
|
||||
const readableCommunity = _expandVars(readableCommunityLabel, this.props.community);
|
||||
const readableCommunity = makeReadableCommunity(
|
||||
this.props.communities,
|
||||
this.props.community);
|
||||
const key = this.props.community.join(":");
|
||||
|
||||
let cls = 'label label-bgp-community ';
|
||||
|
@ -72,4 +72,31 @@ export function isRejectCandidate(rejectCommunities, route) {
|
||||
return (resolved.length > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand variables in string:
|
||||
* "Test AS$0 rejects $2"
|
||||
* will expand with [23, 42, 123] to
|
||||
* "Test AS23 rejects 123"
|
||||
*/
|
||||
export function expandVars(str, vars) {
|
||||
if (!str) {
|
||||
return str; // We don't have to do anything.
|
||||
}
|
||||
|
||||
var res = str;
|
||||
vars.map((v, i) => {
|
||||
res = res.replace(`$${i}`, v);
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
export function makeReadableCommunity(communities, community) {
|
||||
const label = resolveCommunity(communities, community);
|
||||
return expandVars(label, community);
|
||||
}
|
||||
|
||||
export function communityRepr(community) {
|
||||
return community.join(":");
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user