Skip to content

Commit

Permalink
Improve data normalizer and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
safchain committed Oct 15, 2019
1 parent c2d3f0b commit d2d2172
Show file tree
Hide file tree
Showing 9 changed files with 332 additions and 146 deletions.
101 changes: 91 additions & 10 deletions assets/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ var config = {

return attrs
},
"nodeTabTitle": function (node) {
return node.data.Name.substring(0, 8)
},
"groupBy": function (node) {
return node.data.Type && node.data.Type !== "host" ? node.data.Type : null
},
Expand All @@ -99,8 +102,9 @@ var config = {
"data.MAC",
"data.Name"
],
"dataFields": {
"Captures": {
"nodeDataFields": [
{
field: "Captures",
expanded: false,
normalizer: function (data) {
for (let capture of data) {
Expand All @@ -109,12 +113,89 @@ var config = {
return data
}
},
"Injections": { expanded: false },
"IPV4": { expanded: true },
"IPV6": { expanded: true },
"Features": { expanded: false },
"FDB": { expanded: false },
"Neighbors": { expanded: false },
"RoutingTables": { expanded: false }
}
{
field: "Injections",
expanded: false
},
{
field: "IPV4",
expanded: true
},
{
field: "IPV6",
expanded: true
},
{
field: "Metric",
title: "Total metrics",
expanded: false,
normalizer: function (data) {
return {
RxPackets: data.RxPackets.toLocaleString(),
RxBytes: prettyBytes(data.RxBytes),
TxPackets: data.TxPackets.toLocaleString(),
TxBytes: prettyBytes(data.TxBytes),
Last: new Date(data.Last).toLocaleString()
}
}
},
{
field: "LastUpdateMetric",
title: "Last update metrics",
expanded: false,
normalizer: function (data) {
return {
RxPackets: data.RxPackets.toLocaleString(),
RxBytes: prettyBytes(data.RxBytes),
TxPackets: data.TxPackets.toLocaleString(),
TxBytes: prettyBytes(data.TxBytes),
Start: new Date(data.Start).toLocaleString(),
Last: new Date(data.Last).toLocaleString()
}
}
},
{
field: "Features",
expanded: false
},
{
field: "FDB",
expanded: false
},
{
field: "Neighbors",
expanded: false
},
{
field: "RoutingTables",
title: "Routing tables",
expanded: false,
normalizer: function (data) {
var rows = []
for (let table of data) {
if (!table.Routes) {
continue
}
for (let route of table.Routes) {
if (!route.NextHops) {
continue
}
for (let nh of route.NextHops) {
rows.push({
ID: table.ID,
Src: table.Src,
Protocol: route["Protocol"],
Prefix: route["Prefix"],
Priority: nh["Priority"],
IP: nh["IP"],
IfIndex: nh["IfIndex"]
})
}
}
}

return rows
}
}
]
}
14 changes: 11 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
"@material-ui/core": "^4.3.2",
"@material-ui/icons": "^4.2.1",
"@material-ui/lab": "^4.0.0-alpha.26",
"@types/chai": "^4.2.3",
"@types/mocha": "^5.2.7",
"@types/react": "^16.9.2",
"@types/react-dom": "^16.9.0",
"@types/styled-jsx": "^2.2.8",
"awesome-typescript-loader": "^5.2.1",
"chai": "^4.2.0",
"clsx": "^1.0.4",
"copy-webpack-plugin": "^5.0.4",
"css-loader": "^3.2.0",
Expand All @@ -29,8 +32,10 @@
"html-webpack-plugin": "^3.2.0",
"image-webpack-loader": "^5.0.0",
"lodash.deburr": "^4.1.0",
"mocha": "^6.2.1",
"mui-datatables": "^2.10.3",
"notistack": "^0.8.9",
"nyc": "^14.1.1",
"react": "^16.8.6",
"react-docgen": "^4.1.1",
"react-dom": "^16.8.6",
Expand All @@ -42,13 +47,16 @@
"source-map-loader": "^0.2.4",
"style-loader": "^1.0.0",
"ts-debounce": "^1.0.0",
"typescript": "^3.5.3",
"ts-node": "^8.4.1",
"typescript": "^3.6.4",
"webpack": "^4.39.2",
"webpack-cli": "^3.3.7",
"webpack-dev-server": "^3.8.0"
},
"scripts": {
"start": "webpack-dev-server --mode development --host 0.0.0.0 --open",
"build": "webpack --mode production"
"build": "webpack --mode production",
"test": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' mocha -r ts-node/register tests/**/*.test.ts",
"coverage": "nyc -r lcov -e .ts -x \"*.test.ts\" npm run test"
}
}
}
10 changes: 5 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ class App extends React.Component<Props, State> {
}
return (
<Tab className="tab" icon={<span className={className}>{config.nodeAttrs(d).icon}</span>}
key={"tab-" + i} label={d.id.split("-", 2)[0] + "-..."} {...a11yProps(i)} />
key={"tab-" + i} label={<span className={classes.tabTitle}>{config.nodeTabTitle(d)}</span>} {...a11yProps(i)} />
)
})
}
Expand All @@ -300,11 +300,11 @@ class App extends React.Component<Props, State> {
const FieldViewer = (props: any) => {
var result = [] as any

for (let field in config.dataFields) {
for (let cfg of config.nodeDataFields) {
result.push(
<DataViewer key={field} classes={classes} title={field}
defaultExpanded={config.dataFields[field].expanded} data={props.data[field]}
normalizer={config.dataFields[field].normalizer}/>
<DataViewer key={cfg.field} classes={classes} title={cfg.title || cfg.field}
defaultExpanded={cfg.expanded} data={props.data[cfg.field]}
normalizer={cfg.normalizer} />
)
}
return result
Expand Down
178 changes: 178 additions & 0 deletions src/DataNormalizer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* Copyright (C) 2019 Sylvain Afchain
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

export class Result {
private _columns: Array<string>
private _rows: Array<Map<string, any>>

private colIndexes: Map<string, number>

constructor() {
this._columns = new Array<string>()
this._rows = new Array<Map<string, any>>()

this.colIndexes = new Map<string, number>()
}

addColumn(name: string) {
let index = this.colIndexes.get(name)
if (index === undefined) {
this.colIndexes.set(name, this.columns.length)

this._columns.push(name)
}
}

columnIndex(name: string): number | undefined {
return this.colIndexes.get(name)
}

newRow(): Map<string, any> {
return new Map<string, any>()
}

addRow(row: Map<string, any>) {
if (row.size) {
for (let key of row.keys()) {
this.addColumn(key)
}

this._rows.push(row)
}
}

get rows(): Array<Array<any>> {
var rows = new Array<Array<any>>()
for (let row of this._rows) {
let plain = new Array<any>()
for (let i = 0; i != this.colIndexes.size; i++) {
plain[i] = ""
}

row.forEach((value, key) => {
var index = this.columnIndex(key)
if (index === undefined) {
return
}
plain[index] = value
})

rows.push(plain)
}

return rows
}

get columns(): Array<string> {
return this._columns
}
}

export class DataNormalizer {

normalizer: ((any) => any) | null

constructor(normalizer?: (any) => any) {
this.normalizer = normalizer || null
}

private normalizeMap(data: any, result: Result) {
for (let attr in data) {
let row = result.newRow()

let value = data[attr]
switch (typeof value) {
case "object":
break
case "boolean":
row.set("Key", attr)
row.set("Value", value ? "true" : "false")
break
default:
row.set("Key", attr)
row.set("Value", value)
break
}

result.addRow(row)
}

return result
}

private normalizeScalarArray(data: any, result: Result) {
for (let value of data) {
let row = result.newRow()

if (typeof value === "boolean") {
row.set("Value", value ? "true" : "false")
} else {
row.set("Value", value)
}
result.addRow(row)
}

return result
}

private normalizeObjectArray(data: any, result: Result) {
for (let value of data) {
let row = result.newRow()

for (let attr in value) {
var type = typeof value[attr]
if (type === "boolean") {
row.set(attr, value ? "true" : "false")
} if (type === "string" || type === "number") {
row.set(attr, value[attr] === null ? "" : value[attr])
}
}

result.addRow(row)
}

return result
}

private normalizeArray(data: any, result: Result) {
if ((data as Array<any>).some(value => typeof value === "object")) {
return this.normalizeObjectArray(data, result)
}

this.normalizeScalarArray(data, result)
}

private normalizeData(data: any, result: Result) {
if (Array.isArray(data)) {
this.normalizeArray(data, result)
} else if (typeof data === "object") {
this.normalizeMap(data, result)
}
}

normalize(data: any): Result {
var result = new Result()

if (this.normalizer) {
data = this.normalizer(data)
}

this.normalizeData(data, result)

return result
}
}
Loading

0 comments on commit d2d2172

Please sign in to comment.