diff --git a/client/app/scripts/components/node-details/node-details-table.js b/client/app/scripts/components/node-details/node-details-table.js index 062c82029..7164d471b 100644 --- a/client/app/scripts/components/node-details/node-details-table.js +++ b/client/app/scripts/components/node-details/node-details-table.js @@ -70,18 +70,7 @@ export default class NodeDetailsTable extends React.Component { renderHeaders() { if (this.props.nodes && this.props.nodes.length > 0) { - let headers = [{id: 'label', label: this.props.label}]; - // gather header labels from metrics and metadata - const nodeValues = this.props.nodes.map(this.getValuesForNode); - headers = headers.concat(this.props.columns.map(column => { - // look for a node that has the column label - const valuesWithField = nodeValues.find(values => values[column] !== undefined); - if (valuesWithField) { - return {id: column, label: valuesWithField[column].label}; - } - // fall back on column id as label - return {id: column, label: column}; - })); + const headers = [{id: 'label', label: this.props.label}].concat(this.props.columns); const defaultSortBy = this.getDefaultSortBy(); return ( @@ -114,8 +103,8 @@ export default class NodeDetailsTable extends React.Component { renderValues(node) { const fields = this.getValuesForNode(node); - return this.props.columns.map(col => { - const field = fields[col]; + return this.props.columns.map(({id}) => { + const field = fields[id]; if (field) { if (field.valueType === 'metadata') { return ( diff --git a/render/detailed/node.go b/render/detailed/node.go index f3f5504bd..3960ef0ca 100644 --- a/render/detailed/node.go +++ b/render/detailed/node.go @@ -83,11 +83,11 @@ var ( topologyID string NodeSummaryGroup }{ - {report.Host, NodeSummaryGroup{TopologyID: "hosts", Label: "Hosts", Columns: []string{host.CPUUsage, host.MemoryUsage}}}, - {report.Pod, NodeSummaryGroup{TopologyID: "pods", Label: "Pods", Columns: []string{}}}, - {report.Container, NodeSummaryGroup{TopologyID: "containers", Label: "Containers", Columns: []string{docker.CPUTotalUsage, docker.MemoryUsage}}}, - {report.Process, NodeSummaryGroup{TopologyID: "processes", Label: "Processes", Columns: []string{process.PID, process.CPUUsage, process.MemoryUsage}}}, - {report.ContainerImage, NodeSummaryGroup{TopologyID: "containers-by-image", Label: "Container Images", Columns: []string{render.ContainersKey}}}, + {report.Host, NodeSummaryGroup{TopologyID: "hosts", Label: "Hosts", Columns: []Column{host.CPUUsage, host.MemoryUsage}}}, + {report.Pod, NodeSummaryGroup{TopologyID: "pods", Label: "Pods"}}, + {report.Container, NodeSummaryGroup{TopologyID: "containers", Label: "Containers", Columns: []Column{docker.CPUTotalUsage, docker.MemoryUsage}}}, + {report.Process, NodeSummaryGroup{TopologyID: "processes", Label: "Processes", Columns: []Column{process.PID, process.CPUUsage, process.MemoryUsage}}}, + {report.ContainerImage, NodeSummaryGroup{TopologyID: "containers-by-image", Label: "Container Images", Columns: []Column{render.ContainersKey}}}, } ) diff --git a/render/detailed/node_test.go b/render/detailed/node_test.go index 7c97a7f3f..a897ebbed 100644 --- a/render/detailed/node_test.go +++ b/render/detailed/node_test.go @@ -85,19 +85,19 @@ func TestMakeDetailedHostNode(t *testing.T) { { Label: "Containers", TopologyID: "containers", - Columns: []string{docker.CPUTotalUsage, docker.MemoryUsage}, + Columns: []detailed.Column{docker.CPUTotalUsage, docker.MemoryUsage}, Nodes: []detailed.NodeSummary{containerNodeSummary}, }, { Label: "Processes", TopologyID: "processes", - Columns: []string{process.PID, process.CPUUsage, process.MemoryUsage}, + Columns: []detailed.Column{process.PID, process.CPUUsage, process.MemoryUsage}, Nodes: []detailed.NodeSummary{process1NodeSummary, process2NodeSummary}, }, { Label: "Container Images", TopologyID: "containers-by-image", - Columns: []string{render.ContainersKey}, + Columns: []detailed.Column{render.ContainersKey}, Nodes: []detailed.NodeSummary{containerImageNodeSummary}, }, }, @@ -152,7 +152,7 @@ func TestMakeDetailedContainerNode(t *testing.T) { { Label: "Processes", TopologyID: "processes", - Columns: []string{process.PID, process.CPUUsage, process.MemoryUsage}, + Columns: []detailed.Column{process.PID, process.CPUUsage, process.MemoryUsage}, Nodes: []detailed.NodeSummary{ { ID: fmt.Sprintf("process:%s:%s", "server.hostname.com", fixture.ServerPID), diff --git a/render/detailed/summary.go b/render/detailed/summary.go index 07c3f6e74..58ae87b6e 100644 --- a/render/detailed/summary.go +++ b/render/detailed/summary.go @@ -1,6 +1,7 @@ package detailed import ( + "encoding/json" "fmt" "github.com/weaveworks/scope/probe/docker" @@ -16,7 +17,7 @@ type NodeSummaryGroup struct { Label string `json:"label"` Nodes []NodeSummary `json:"nodes"` TopologyID string `json:"topologyId"` - Columns []string `json:"columns"` + Columns []Column `json:"columns"` } // Copy returns a value copy of the NodeSummaryGroup @@ -32,6 +33,25 @@ func (g NodeSummaryGroup) Copy() NodeSummaryGroup { return result } +// Column provides special json serialization for column ids, so they include +// their label for the frontend. +type Column string + +// MarshalJSON serializes a column to json +func (c Column) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]string{"id": string(c), "label": Label(string(c))}) +} + +// UnmarshalJSON deserializes a column from json +func (c *Column) UnmarshalJSON(b []byte) error { + m := map[string]string{} + if err := json.Unmarshal(b, &m); err != nil { + return err + } + *c = Column(m["id"]) + return nil +} + // NodeSummary is summary information about a child for a Node. type NodeSummary struct { ID string `json:"id"`