conjur-asset-ui-beta 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/TODO.md +0 -23
- data/app/build/js/app.js +72652 -50337
- data/app/package.json +2 -0
- data/app/src/actions.js +56 -3
- data/app/src/app.js +7 -3
- data/app/src/clients/graph.js +24 -0
- data/app/src/clients/members.js +3 -3
- data/app/src/components/audit/table.js +9 -0
- data/app/src/components/custom/view.js +26 -14
- data/app/src/components/dashboard/activity.js +24 -4
- data/app/src/components/generic/foldable_audit_section.js +17 -0
- data/app/src/components/generic/role_link.js +2 -1
- data/app/src/components/graph/graph.js +421 -0
- data/app/src/components/graph/graph.less +39 -0
- data/app/src/components/group/view.js +31 -14
- data/app/src/components/host/activity.js +24 -4
- data/app/src/components/host/executors.js +83 -0
- data/app/src/components/host/updaters.js +83 -0
- data/app/src/components/host/view.js +46 -14
- data/app/src/components/layer/view.js +30 -13
- data/app/src/components/policy/view.js +23 -14
- data/app/src/components/search/search.js +21 -7
- data/app/src/components/user/activity.js +25 -4
- data/app/src/components/user/view.js +30 -13
- data/app/src/components/variable/activity.js +25 -4
- data/app/src/components/variable/fetchers.js +1 -1
- data/app/src/components/variable/updaters.js +1 -1
- data/app/src/components/variable/view.js +24 -13
- data/app/src/constants.js +4 -2
- data/app/src/stores/graph_store.js +55 -0
- data/app/src/stores/host_store.js +12 -1
- data/app/src/stores/route_store.js +7 -5
- data/app/src/stores/search_store.js +11 -6
- data/conjur-asset-ui.gemspec +1 -1
- data/lib/conjur-asset-ui-version.rb +1 -1
- metadata +9 -3
| @@ -0,0 +1,421 @@ | |
| 1 | 
            +
            'use strict';
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require('./graph.less');
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            var React = require('react'),
         | 
| 6 | 
            +
                Router = require('react-router'),
         | 
| 7 | 
            +
                Fluxxor = require('fluxxor'),
         | 
| 8 | 
            +
                FluxMixin = Fluxxor.FluxMixin(React),
         | 
| 9 | 
            +
                StoreWatchMixin = Fluxxor.StoreWatchMixin,
         | 
| 10 | 
            +
                dagre = require('dagre-d3'),
         | 
| 11 | 
            +
                d3 = require('d3'),
         | 
| 12 | 
            +
                forEach = require('lodash/collection/forEach');
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            var RoleLink = require('../generic/role_link');
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            // helpers
         | 
| 17 | 
            +
            function _translate(x,y) {
         | 
| 18 | 
            +
                return 'translate(' + x + ',' + y + ')';
         | 
| 19 | 
            +
            }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            function _findParent(elt, predicate) {
         | 
| 22 | 
            +
                while (elt && !predicate(elt)) {
         | 
| 23 | 
            +
                    elt = elt.parentElement;
         | 
| 24 | 
            +
                }
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                if (!elt) {
         | 
| 27 | 
            +
                    throw new Error('couldn\'t find parent');
         | 
| 28 | 
            +
                }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                return elt;
         | 
| 31 | 
            +
            }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            function _normalizeGraph(graph, invert) {
         | 
| 34 | 
            +
                var result = [];
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                graph.forEach(function(edge) {
         | 
| 37 | 
            +
                    var parent = edge.parent || edge[0],
         | 
| 38 | 
            +
                        child  = edge.child  || edge[1];
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    var newEdge = invert ? {child: parent, parent: child} : {parent: parent, child: child};
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    result.push(newEdge);
         | 
| 43 | 
            +
                });
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                return result;
         | 
| 46 | 
            +
            }
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            function _isInternal(roleid) {
         | 
| 49 | 
            +
                return roleid.indexOf('@') >= 0;
         | 
| 50 | 
            +
            }
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            /**
         | 
| 53 | 
            +
             * Displays a digraph representing a role tree.  Usage:
         | 
| 54 | 
            +
             *
         | 
| 55 | 
            +
             * <RoleGraph margin={...} ref='roleGraph'/>
         | 
| 56 | 
            +
             *
         | 
| 57 | 
            +
             * ...
         | 
| 58 | 
            +
             *
         | 
| 59 | 
            +
             * // show a graph, which should be an array of {parent, child} objects.
         | 
| 60 | 
            +
             * this.refs.roleGraph.setState({graph: res.graph})
         | 
| 61 | 
            +
             */
         | 
| 62 | 
            +
            var RoleGraph = React.createClass({
         | 
| 63 | 
            +
                mixins: [Router.State, Router.Navigation],
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                /*
         | 
| 66 | 
            +
                 * React Lifecycle methods
         | 
| 67 | 
            +
                 */
         | 
| 68 | 
            +
                render() {
         | 
| 69 | 
            +
                    return (
         | 
| 70 | 
            +
                      <div className="role-graph-container"></div>
         | 
| 71 | 
            +
                    );
         | 
| 72 | 
            +
                },
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                getDefaultProps() {
         | 
| 75 | 
            +
                    return {
         | 
| 76 | 
            +
                        defaultOptions: {
         | 
| 77 | 
            +
                            margin: {
         | 
| 78 | 
            +
                                top: 20,
         | 
| 79 | 
            +
                                right: 20,
         | 
| 80 | 
            +
                                bottom: 20,
         | 
| 81 | 
            +
                                left: 20
         | 
| 82 | 
            +
                            }
         | 
| 83 | 
            +
                        }
         | 
| 84 | 
            +
                    };
         | 
| 85 | 
            +
                },
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                getInitialState() {
         | 
| 88 | 
            +
                    return {
         | 
| 89 | 
            +
                        ready: false
         | 
| 90 | 
            +
                    };
         | 
| 91 | 
            +
                },
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                componentDidMount() {
         | 
| 94 | 
            +
                    this.ctx = {};
         | 
| 95 | 
            +
                    this.el = this.getDOMNode();
         | 
| 96 | 
            +
                    this._propsToState(this.props);
         | 
| 97 | 
            +
                },
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                componentWillReceiveProps(nextProps) {
         | 
| 100 | 
            +
                    this._propsToState(nextProps);
         | 
| 101 | 
            +
                },
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                componentDidUpdate() {
         | 
| 104 | 
            +
                    if (this.state.ready === false) {
         | 
| 105 | 
            +
                        return;
         | 
| 106 | 
            +
                    }
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                    if (this.ctx.svg === undefined) {
         | 
| 109 | 
            +
                        this._create();
         | 
| 110 | 
            +
                    }
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                    if (this.state.graph !== undefined) {
         | 
| 113 | 
            +
                        this._update();
         | 
| 114 | 
            +
                    }
         | 
| 115 | 
            +
                },
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                /**
         | 
| 118 | 
            +
                 * Internal methods
         | 
| 119 | 
            +
                 */
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                /**
         | 
| 122 | 
            +
                 * Create non-data bound components.  May be called whenever state.ready is true
         | 
| 123 | 
            +
                 * @private
         | 
| 124 | 
            +
                 */
         | 
| 125 | 
            +
                _create() {
         | 
| 126 | 
            +
                    this.ctx.svg = d3.select(this.el)
         | 
| 127 | 
            +
                        .append('svg')
         | 
| 128 | 
            +
                        .attr('class', 'role-graph-svg');
         | 
| 129 | 
            +
                    this.ctx.inner = this.ctx.svg.append('g');
         | 
| 130 | 
            +
                },
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                /**
         | 
| 133 | 
            +
                 *  Render the graph represented by this.state.graph.  This may be
         | 
| 134 | 
            +
                 *  called whenever this.state.graph is defined.  this.state.graph
         | 
| 135 | 
            +
                 *  must be an array of {child:'child-id', parent:'parent-id'}
         | 
| 136 | 
            +
                 *  objects.
         | 
| 137 | 
            +
                 *
         | 
| 138 | 
            +
                 * @private
         | 
| 139 | 
            +
                 */
         | 
| 140 | 
            +
                _update() {
         | 
| 141 | 
            +
                    var svg = this.ctx.svg;
         | 
| 142 | 
            +
                    var graph = this._prepareD3Graph();
         | 
| 143 | 
            +
                    var inner = this.ctx.inner;
         | 
| 144 | 
            +
                    var render = new dagre.render();
         | 
| 145 | 
            +
                    render(inner, graph);
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    // Move and resize the SVG to match the width and height of
         | 
| 148 | 
            +
                    // the graph.  Here, we use this.state.margin to compute the
         | 
| 149 | 
            +
                    // position of the <g/> element containing the graph within the
         | 
| 150 | 
            +
                    // root <svg> element, and the size of both the container and
         | 
| 151 | 
            +
                    // the svg.
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                    var margin = this.state.margin;
         | 
| 154 | 
            +
                    var gWidth  = graph.graph().width,
         | 
| 155 | 
            +
                        gHeight = graph.graph().height;
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                    // total width/height are gWidth/gHeight + margin
         | 
| 158 | 
            +
                    var totalHeight = gHeight + margin.top + margin.bottom;
         | 
| 159 | 
            +
                    var totalWidth  = gWidth  + margin.left  + margin.right;
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                    var svgSize = this._computeSvgSize(gWidth, gHeight);
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                    // resize the svg
         | 
| 164 | 
            +
                    svg.attr('height', svgSize.height)
         | 
| 165 | 
            +
                        .attr('width', svgSize.width);
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                    // resize the inner <g> and translate it to the top left point of the
         | 
| 168 | 
            +
                    // margin.
         | 
| 169 | 
            +
                    inner.attr('height', gHeight)
         | 
| 170 | 
            +
                        .attr('width', gWidth)
         | 
| 171 | 
            +
                        .attr('transform',_translate(margin.left, margin.top));
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                    // Set up zoom support
         | 
| 174 | 
            +
                    var zoom = d3.behavior.zoom().on('zoom', function() {
         | 
| 175 | 
            +
                        inner.attr('transform', 'translate(' + d3.event.translate + ')' +
         | 
| 176 | 
            +
                                   'scale(' + d3.event.scale + ')');
         | 
| 177 | 
            +
                    });
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                    // scale to initial scale
         | 
| 180 | 
            +
                    zoom.scale(svgSize.scale)
         | 
| 181 | 
            +
                        .translate([(svgSize.width - gWidth * svgSize.scale) / 2, (svgSize.height - gHeight * svgSize.scale) / 2]) // center it
         | 
| 182 | 
            +
                        .event(svg);
         | 
| 183 | 
            +
                    svg.call(zoom);
         | 
| 184 | 
            +
                },
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                /**
         | 
| 187 | 
            +
                 * Return width and height attributes that will fit in our element (or fit the
         | 
| 188 | 
            +
                 * graph if our element isn't specifying a width and height), and a scale
         | 
| 189 | 
            +
                 * to apply to the graph to fit it in the svg, based on the given graph
         | 
| 190 | 
            +
                 * width and height.
         | 
| 191 | 
            +
                 * @param graphWidth Width, in pixels, of the graph
         | 
| 192 | 
            +
                 * @param graphHeight Height, in pixels, of the graph
         | 
| 193 | 
            +
                 * @return {{width:number, height: number, scale: number}}
         | 
| 194 | 
            +
                 *
         | 
| 195 | 
            +
                 * @private
         | 
| 196 | 
            +
                 */
         | 
| 197 | 
            +
             | 
| 198 | 
            +
                _computeSvgSize(graphWidth, graphHeight) {
         | 
| 199 | 
            +
                    var eltWidth = +this.el.offsetWidth,
         | 
| 200 | 
            +
                        eltHeight = +this.el.offsetHeight,
         | 
| 201 | 
            +
                        width = graphWidth,
         | 
| 202 | 
            +
                        height = graphHeight;
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                    if (!isNaN(eltWidth) && eltWidth !== 0) {
         | 
| 205 | 
            +
                        width = Math.min(graphWidth, eltWidth);
         | 
| 206 | 
            +
                    }
         | 
| 207 | 
            +
             | 
| 208 | 
            +
                    if (!isNaN(eltHeight) && eltHeight !== 0) {
         | 
| 209 | 
            +
                        height = Math.min(graphHeight, eltHeight);
         | 
| 210 | 
            +
                    }
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                    if (this.props.width) {
         | 
| 213 | 
            +
                        width = Math.max(this.props.width, width);
         | 
| 214 | 
            +
                    }
         | 
| 215 | 
            +
             | 
| 216 | 
            +
                    if (this.props.height) {
         | 
| 217 | 
            +
                        height = Math.max(this.props.height, height);
         | 
| 218 | 
            +
                    }
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                    var scale = Math.min(width, height) / Math.max(graphWidth, graphHeight);
         | 
| 221 | 
            +
             | 
| 222 | 
            +
                    if (scale > 1) {
         | 
| 223 | 
            +
                        scale *= .9; // leave some room at the edges when the graph is smaller than the element.
         | 
| 224 | 
            +
                    }
         | 
| 225 | 
            +
             | 
| 226 | 
            +
                    return {width: width, height: height, scale: scale};
         | 
| 227 | 
            +
                },
         | 
| 228 | 
            +
             | 
| 229 | 
            +
                // creates an HTML role-link for the role.
         | 
| 230 | 
            +
                _createRoleLabel(roleid) {
         | 
| 231 | 
            +
                    return React.withContext(this.context, function() {
         | 
| 232 | 
            +
                        var roleLink = <RoleLink id={roleid} simple={true} />;
         | 
| 233 | 
            +
                        return React.renderComponentToString(roleLink);
         | 
| 234 | 
            +
                    });
         | 
| 235 | 
            +
                },
         | 
| 236 | 
            +
             | 
| 237 | 
            +
                /**
         | 
| 238 | 
            +
                 * Build a graphlib Graph object from the graph data returned by
         | 
| 239 | 
            +
                 * the conjur api.
         | 
| 240 | 
            +
                 *
         | 
| 241 | 
            +
                 * May be called whenever this.state.graph is defined.
         | 
| 242 | 
            +
                 * @returns {degreD3.graphlib.Graph}
         | 
| 243 | 
            +
                 * @private
         | 
| 244 | 
            +
                 */
         | 
| 245 | 
            +
                _prepareD3Graph() {
         | 
| 246 | 
            +
                    var self = this,
         | 
| 247 | 
            +
                        gr = this.state.graph,
         | 
| 248 | 
            +
                        invert = this.state.invertGraph == undefined ? true : this.state.invertGraph;
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                    gr = _normalizeGraph(gr, invert);
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                    // remove internal roles from the graph,
         | 
| 253 | 
            +
                    // unless the role being shown is internal (presently this is
         | 
| 254 | 
            +
                    // impossible in the UI, but we might as well check in case this changes.
         | 
| 255 | 
            +
                    if (!_isInternal(this.state.roleid)) {
         | 
| 256 | 
            +
                        gr = gr.filter(function(edge) {
         | 
| 257 | 
            +
                            return !(_isInternal(edge.parent) || _isInternal(edge.child));
         | 
| 258 | 
            +
                        });
         | 
| 259 | 
            +
                    }
         | 
| 260 | 
            +
             | 
| 261 | 
            +
                    // graph properties go here
         | 
| 262 | 
            +
                    var graph = this.graph = new dagre.graphlib.Graph().setGraph({});
         | 
| 263 | 
            +
             | 
| 264 | 
            +
                    var nodeSet = {},
         | 
| 265 | 
            +
                        edgeSet = {};
         | 
| 266 | 
            +
             | 
| 267 | 
            +
                    function put(members, id) {
         | 
| 268 | 
            +
                        if (members[id]) {
         | 
| 269 | 
            +
                            return false;
         | 
| 270 | 
            +
                        }
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                        members[id] = true;
         | 
| 273 | 
            +
                        return true;
         | 
| 274 | 
            +
                    }
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                    function addNode(node) {
         | 
| 277 | 
            +
                        if (put(nodeSet, node)) {
         | 
| 278 | 
            +
                            graph.setNode(node, {
         | 
| 279 | 
            +
                                label: self._createRoleLabel(node),
         | 
| 280 | 
            +
                                labelType: 'html'
         | 
| 281 | 
            +
                            });
         | 
| 282 | 
            +
                        }
         | 
| 283 | 
            +
                    }
         | 
| 284 | 
            +
             | 
| 285 | 
            +
                    function addEdge(edge) {
         | 
| 286 | 
            +
                        var parent = edge.parent,
         | 
| 287 | 
            +
                            child = edge.child,
         | 
| 288 | 
            +
                            edgeId = parent + '' + child; // poor mans hash code :-(
         | 
| 289 | 
            +
             | 
| 290 | 
            +
                        addNode(parent);
         | 
| 291 | 
            +
                        addNode(child);
         | 
| 292 | 
            +
             | 
| 293 | 
            +
                        if (put(edgeSet, edgeId)) {
         | 
| 294 | 
            +
                            // edge properties defined here
         | 
| 295 | 
            +
                            graph.setEdge(parent, child,{});
         | 
| 296 | 
            +
                        }
         | 
| 297 | 
            +
                    }
         | 
| 298 | 
            +
             | 
| 299 | 
            +
                    forEach(gr, addEdge);
         | 
| 300 | 
            +
             | 
| 301 | 
            +
                    var selfNode = graph.node(this.state.roleid);
         | 
| 302 | 
            +
             | 
| 303 | 
            +
                    if (selfNode !== undefined) {
         | 
| 304 | 
            +
                        selfNode['class'] = 'current-role';
         | 
| 305 | 
            +
                    }
         | 
| 306 | 
            +
             | 
| 307 | 
            +
                    return graph;
         | 
| 308 | 
            +
                },
         | 
| 309 | 
            +
             | 
| 310 | 
            +
                /**
         | 
| 311 | 
            +
                 * Transfer some props to our state, and determine whether we're ready to
         | 
| 312 | 
            +
                 * call _create.
         | 
| 313 | 
            +
                 * @param props contains a 'margin' object.
         | 
| 314 | 
            +
                 * @private
         | 
| 315 | 
            +
                 */
         | 
| 316 | 
            +
                _propsToState(props) {
         | 
| 317 | 
            +
                    var margin = props.margin ? props.margin : this.props.defaultOptions.margin;
         | 
| 318 | 
            +
             | 
| 319 | 
            +
                    var state = {
         | 
| 320 | 
            +
                        ready: margin !== undefined && this.props.roleid !== undefined,
         | 
| 321 | 
            +
                        margin: margin,
         | 
| 322 | 
            +
                        invertGraph: this.props.invertGraph,
         | 
| 323 | 
            +
                        roleid: this.props.roleid
         | 
| 324 | 
            +
                    };
         | 
| 325 | 
            +
             | 
| 326 | 
            +
                    if (props.graph !== undefined) {
         | 
| 327 | 
            +
                        state.graph = props.graph;
         | 
| 328 | 
            +
                    }
         | 
| 329 | 
            +
             | 
| 330 | 
            +
                    this.setState(state);
         | 
| 331 | 
            +
                }
         | 
| 332 | 
            +
            });
         | 
| 333 | 
            +
             | 
| 334 | 
            +
            module.exports = React.createClass({
         | 
| 335 | 
            +
                displayName: 'RoleGraph',
         | 
| 336 | 
            +
             | 
| 337 | 
            +
                mixins: [FluxMixin, StoreWatchMixin('graph')],
         | 
| 338 | 
            +
             | 
| 339 | 
            +
                getStateFromFlux() {
         | 
| 340 | 
            +
                    var flux = this.getFlux(),
         | 
| 341 | 
            +
                        graph = flux.store('graph').getGraph(this.props.kind, this.props.id);
         | 
| 342 | 
            +
             | 
| 343 | 
            +
                    return graph;
         | 
| 344 | 
            +
                },
         | 
| 345 | 
            +
             | 
| 346 | 
            +
                render() {
         | 
| 347 | 
            +
                    if (this.state.loading) {
         | 
| 348 | 
            +
                        return (
         | 
| 349 | 
            +
                          <div className="rg-container">
         | 
| 350 | 
            +
                            <h2>Role Graph</h2>
         | 
| 351 | 
            +
                            Loading
         | 
| 352 | 
            +
                          </div>
         | 
| 353 | 
            +
                        );
         | 
| 354 | 
            +
                    }
         | 
| 355 | 
            +
             | 
| 356 | 
            +
                    if (this.state.data.length === 0) {
         | 
| 357 | 
            +
                        return (
         | 
| 358 | 
            +
                          <div className="rg-container">
         | 
| 359 | 
            +
                            <h2>Role Graph</h2>
         | 
| 360 | 
            +
                            No data exists
         | 
| 361 | 
            +
                          </div>
         | 
| 362 | 
            +
                        );
         | 
| 363 | 
            +
                    }
         | 
| 364 | 
            +
             | 
| 365 | 
            +
                    return this._renderGraph();
         | 
| 366 | 
            +
                },
         | 
| 367 | 
            +
             | 
| 368 | 
            +
                componentDidMount() {
         | 
| 369 | 
            +
                    var actions = this.getFlux().actions;
         | 
| 370 | 
            +
             | 
| 371 | 
            +
                    actions.graph.load(this.props.kind, this.props.id);
         | 
| 372 | 
            +
                },
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                _renderGraph() {
         | 
| 375 | 
            +
                    var size = this._computeGraphSize(),
         | 
| 376 | 
            +
                        invert = this.props.invertGraph === undefined ? true : this.props.invertGraph,
         | 
| 377 | 
            +
                        roleid = conjur.app.configuration.account + ':' + this.props.kind + ':' + this.props.id;
         | 
| 378 | 
            +
             | 
| 379 | 
            +
                    return (
         | 
| 380 | 
            +
                      <div className="rg-container loaded">
         | 
| 381 | 
            +
                        <h2>Role Graph</h2>
         | 
| 382 | 
            +
                        <RoleGraph graph={this.state.data}
         | 
| 383 | 
            +
                                   width={size.width}
         | 
| 384 | 
            +
                                   height={size.height}
         | 
| 385 | 
            +
                                   roleid={roleid}
         | 
| 386 | 
            +
                                   invertGraph={invert} />
         | 
| 387 | 
            +
                      </div>
         | 
| 388 | 
            +
                    );
         | 
| 389 | 
            +
                },
         | 
| 390 | 
            +
             | 
| 391 | 
            +
                _computeGraphSize() {
         | 
| 392 | 
            +
                    var el = this.getDOMNode(),
         | 
| 393 | 
            +
                        width = this.props.width,
         | 
| 394 | 
            +
                        height = this.props.height;
         | 
| 395 | 
            +
             | 
| 396 | 
            +
                    if (isNaN(width)) {
         | 
| 397 | 
            +
                        width = +el.offsetWidth;
         | 
| 398 | 
            +
                    }
         | 
| 399 | 
            +
             | 
| 400 | 
            +
                    if (isNaN(height)) {
         | 
| 401 | 
            +
                        height = +el.offsetHeight;
         | 
| 402 | 
            +
                    }
         | 
| 403 | 
            +
             | 
| 404 | 
            +
                    if (isNaN(width) || isNaN(height)) {
         | 
| 405 | 
            +
                        throw new Error('can\'t determine width and height.  ' +
         | 
| 406 | 
            +
                                        'Perhaps you should set width and height props on your RoleGraphContainer.');
         | 
| 407 | 
            +
                    }
         | 
| 408 | 
            +
             | 
| 409 | 
            +
                    // we can set height ourselves but width should be computed from the parent.
         | 
| 410 | 
            +
                    if (width == 0) {
         | 
| 411 | 
            +
                        width = _findParent(el, function(e) {
         | 
| 412 | 
            +
                            return e.offsetWidth !== 0;
         | 
| 413 | 
            +
                        }).offsetWidth; // _findParent is a partial function, if no such parent exists it will throw an Error.
         | 
| 414 | 
            +
                    }
         | 
| 415 | 
            +
             | 
| 416 | 
            +
                    return {
         | 
| 417 | 
            +
                        width: width,
         | 
| 418 | 
            +
                        height: height
         | 
| 419 | 
            +
                    };
         | 
| 420 | 
            +
                }
         | 
| 421 | 
            +
            });
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            // MUST BE done in BEM style!!
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            .role-graph {
         | 
| 4 | 
            +
                height: 300px;
         | 
| 5 | 
            +
            }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            .role-graph-svg {
         | 
| 8 | 
            +
                border: 1px solid #CCC;
         | 
| 9 | 
            +
            }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            .rg-container {
         | 
| 12 | 
            +
                width: 100%;
         | 
| 13 | 
            +
            }
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            .rg-container .loaded {
         | 
| 16 | 
            +
                min-height: 500px;
         | 
| 17 | 
            +
            }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            /** SVG role graph styles **/
         | 
| 20 | 
            +
            .node rect {
         | 
| 21 | 
            +
                stroke: #333;
         | 
| 22 | 
            +
                fill: #fff;
         | 
| 23 | 
            +
            }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            svg .label {
         | 
| 26 | 
            +
                font-weight: normal;
         | 
| 27 | 
            +
                height: auto;
         | 
| 28 | 
            +
                width: auto;
         | 
| 29 | 
            +
            }
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            .edgePath path {
         | 
| 32 | 
            +
                stroke: #333;
         | 
| 33 | 
            +
                fill: #333;
         | 
| 34 | 
            +
                stroke-width: 1.5px;
         | 
| 35 | 
            +
            }
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            g.current-role > rect {
         | 
| 38 | 
            +
                fill: #7F7;
         | 
| 39 | 
            +
            }
         | 
| @@ -3,8 +3,7 @@ | |
| 3 3 | 
             
            var React = require('react'),
         | 
| 4 4 | 
             
                Fluxxor = require('fluxxor'),
         | 
| 5 5 | 
             
                FluxMixin = Fluxxor.FluxMixin(React),
         | 
| 6 | 
            -
                StoreWatchMixin = Fluxxor.StoreWatchMixin | 
| 7 | 
            -
                Router = require('react-router');
         | 
| 6 | 
            +
                StoreWatchMixin = Fluxxor.StoreWatchMixin;
         | 
| 8 7 |  | 
| 9 8 | 
             
            var TabbedArea = require('react-bootstrap/lib/TabbedArea'),
         | 
| 10 9 | 
             
                TabPane = require('react-bootstrap/lib/TabPane');
         | 
| @@ -12,6 +11,7 @@ var TabbedArea = require('react-bootstrap/lib/TabbedArea'), | |
| 12 11 | 
             
            var TabMixin = require('../generic/tab_mixin'),
         | 
| 13 12 | 
             
                Breadcrumbs = require('../generic/breadcrumbs'),
         | 
| 14 13 | 
             
                RoleLink = require('../generic/role_link'),
         | 
| 14 | 
            +
                RoleGraph = require('../graph/graph'),
         | 
| 15 15 | 
             
                utils = require('../../utils');
         | 
| 16 16 |  | 
| 17 17 | 
             
            var AuditTable = require('../audit/table');
         | 
| @@ -22,22 +22,45 @@ var compact = require('lodash/array/compact'), | |
| 22 22 | 
             
            module.exports = React.createClass({
         | 
| 23 23 | 
             
                displayName: 'GroupView',
         | 
| 24 24 |  | 
| 25 | 
            -
                mixins: [FluxMixin, StoreWatchMixin('audit', 'group', 'resources' | 
| 25 | 
            +
                mixins: [FluxMixin, StoreWatchMixin('audit', 'group', 'resources', 'route'), TabMixin],
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                askForNewData(state) {
         | 
| 28 | 
            +
                    var actions = this.getFlux().actions;
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    actions.audit.loadForRole('group', state.id);
         | 
| 31 | 
            +
                    actions.audit.loadForResource('group', state.id);
         | 
| 32 | 
            +
                    actions.group.load(state.id);
         | 
| 33 | 
            +
                    actions.resources.loadOne('group', state.id);
         | 
| 34 | 
            +
                },
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                componentDidMount() {
         | 
| 37 | 
            +
                    this.askForNewData(this.state);
         | 
| 38 | 
            +
                },
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                componentWillUpdate(nextProps, nextState) {
         | 
| 41 | 
            +
                    if (this.state.id !== nextState.id) {
         | 
| 42 | 
            +
                        this.askForNewData(nextState);
         | 
| 43 | 
            +
                    }
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    return true;
         | 
| 46 | 
            +
                },
         | 
| 26 47 |  | 
| 27 48 | 
             
                getStateFromFlux() {
         | 
| 28 49 | 
             
                    var flux = this.getFlux(),
         | 
| 29 | 
            -
                        id =  | 
| 50 | 
            +
                        id = flux.store('route').getParam('id'),
         | 
| 30 51 | 
             
                        audit = flux.store('audit').getEventFor('group', id),
         | 
| 31 52 | 
             
                        data = flux.store('group').getData(),
         | 
| 32 53 | 
             
                        resource = flux.store('resources').getResource('group', id);
         | 
| 33 54 |  | 
| 34 55 | 
             
                    var ret = {
         | 
| 56 | 
            +
                        id: id,
         | 
| 35 57 | 
             
                        loading: data.loading,
         | 
| 36 58 | 
             
                        group: data.group,
         | 
| 37 59 | 
             
                        owned: data.owned,
         | 
| 38 60 | 
             
                        roles: data.roles,
         | 
| 39 61 | 
             
                        owner: resource.data.owner,
         | 
| 40 62 | 
             
                        annotations: resource.data.annotations,
         | 
| 63 | 
            +
                        resource: resource.data,
         | 
| 41 64 | 
             
                        members: data.members,
         | 
| 42 65 | 
             
                        resources: data.resources,
         | 
| 43 66 | 
             
                        audit: audit.data
         | 
| @@ -121,17 +144,11 @@ module.exports = React.createClass({ | |
| 121 144 | 
             
                        <TabbedArea defaultActiveKey="overview">
         | 
| 122 145 | 
             
                          {tabs}
         | 
| 123 146 | 
             
                        </TabbedArea>
         | 
| 147 | 
            +
                        <hr />
         | 
| 148 | 
            +
                        <RoleGraph height="400"
         | 
| 149 | 
            +
                                   kind="group"
         | 
| 150 | 
            +
                                   id={this.state.id} />
         | 
| 124 151 | 
             
                      </div>
         | 
| 125 152 | 
             
                    );
         | 
| 126 | 
            -
                },
         | 
| 127 | 
            -
             | 
| 128 | 
            -
                componentDidMount() {
         | 
| 129 | 
            -
                    var actions = this.getFlux().actions,
         | 
| 130 | 
            -
                        id = unescape(this.getParams().id);
         | 
| 131 | 
            -
             | 
| 132 | 
            -
                    actions.audit.loadForRole('group', id);
         | 
| 133 | 
            -
                    actions.audit.loadForResource('group', id);
         | 
| 134 | 
            -
                    actions.group.load(id);
         | 
| 135 | 
            -
                    actions.resources.loadOne('group', id);
         | 
| 136 153 | 
             
                }
         | 
| 137 154 | 
             
            });
         | 
| @@ -76,17 +76,37 @@ module.exports = React.createClass({ | |
| 76 76 |  | 
| 77 77 | 
             
                    return null;
         | 
| 78 78 | 
             
                },
         | 
| 79 | 
            +
                
         | 
| 80 | 
            +
                getInitialState() {
         | 
| 81 | 
            +
                    return {showActivity: false};
         | 
| 82 | 
            +
                },
         | 
| 83 | 
            +
               
         | 
| 84 | 
            +
                toggleActivity(e) {
         | 
| 85 | 
            +
                    e.preventDefault();
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    this.setState({showActivity: !this.state.showActivity});
         | 
| 88 | 
            +
                },
         | 
| 79 89 |  | 
| 80 90 | 
             
                render() {
         | 
| 81 91 | 
             
                    var data = this.getData(this.props.audit);
         | 
| 92 | 
            +
                    var activity_body = [];
         | 
| 93 | 
            +
                
         | 
| 94 | 
            +
                    var msg = this.state.showActivity ? 'Hide' : 'Show graph';
         | 
| 95 | 
            +
                   
         | 
| 96 | 
            +
                    if (this.state.showActivity) {
         | 
| 97 | 
            +
                        activity_body.push(
         | 
| 98 | 
            +
                            <div className="b-host-activity__graph">
         | 
| 99 | 
            +
                              <Chart options={this.props.options}
         | 
| 100 | 
            +
                                     data={data} />
         | 
| 101 | 
            +
                            </div>
         | 
| 102 | 
            +
                        );
         | 
| 103 | 
            +
                    }
         | 
| 82 104 |  | 
| 83 105 | 
             
                    return (
         | 
| 84 106 | 
             
                      <div className="b-host-activity">
         | 
| 85 107 | 
             
                        <h2>Activity<Refresh show={this.props.isLoading} /></h2>
         | 
| 86 | 
            -
                        < | 
| 87 | 
            -
             | 
| 88 | 
            -
                                 data={data} />
         | 
| 89 | 
            -
                        </div>
         | 
| 108 | 
            +
                        <a href="#" onClick={this.toggleActivity}>{msg}</a>
         | 
| 109 | 
            +
                        {activity_body}
         | 
| 90 110 | 
             
                      </div>
         | 
| 91 111 | 
             
                    );
         | 
| 92 112 | 
             
                }
         | 
| @@ -0,0 +1,83 @@ | |
| 1 | 
            +
            'use strict';
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            var React = require('react'),
         | 
| 4 | 
            +
                take = require('lodash/array/take');
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            var RoleLink = require('../generic/role_link'),
         | 
| 7 | 
            +
                Refresh = require('../refresh/refresh');
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module.exports = React.createClass({
         | 
| 10 | 
            +
                displayName: 'HostExecutors',
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                propTypes: {
         | 
| 13 | 
            +
                    data: React.PropTypes.array,
         | 
| 14 | 
            +
                    isLoading: React.PropTypes.bool
         | 
| 15 | 
            +
                },
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                getDefaultProps() {
         | 
| 18 | 
            +
                    return {
         | 
| 19 | 
            +
                        data: [],
         | 
| 20 | 
            +
                        isLoading: false
         | 
| 21 | 
            +
                    };
         | 
| 22 | 
            +
                },
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                getInitialState() {
         | 
| 25 | 
            +
                    return {showAll: false};
         | 
| 26 | 
            +
                },
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                handleClick(e) {
         | 
| 29 | 
            +
                    e.preventDefault();
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    this.setState({showAll: !this.state.showAll});
         | 
| 32 | 
            +
                },
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                getItems() {
         | 
| 35 | 
            +
                    var items = this.props.data;
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    if (!this.state.showAll) {
         | 
| 38 | 
            +
                        items = take(items, 5);
         | 
| 39 | 
            +
                    }
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    return items.map((e) => {
         | 
| 42 | 
            +
                        return (
         | 
| 43 | 
            +
                          <li className="list-group-item list-group-item-noborder">
         | 
| 44 | 
            +
                            <RoleLink id={e} />
         | 
| 45 | 
            +
                          </li>
         | 
| 46 | 
            +
                        );
         | 
| 47 | 
            +
                    });
         | 
| 48 | 
            +
                },
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                render() {
         | 
| 51 | 
            +
                    var items = this.getItems(),
         | 
| 52 | 
            +
                        msg = this.state.showAll ? 'Show top 5' : 'See all',
         | 
| 53 | 
            +
                        empty = items.length === 0,
         | 
| 54 | 
            +
                        lessThan5 = items.length <= 5,
         | 
| 55 | 
            +
                        body = [(<h2>Executors<Refresh show={this.props.isLoading} /></h2>)];
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    if (!empty) {
         | 
| 58 | 
            +
                        body.push(
         | 
| 59 | 
            +
                          <ul className="b-host-executors__list list-unstyled list-group">
         | 
| 60 | 
            +
                            {items}
         | 
| 61 | 
            +
                          </ul>
         | 
| 62 | 
            +
                        );
         | 
| 63 | 
            +
                    } else {
         | 
| 64 | 
            +
                        body.push(
         | 
| 65 | 
            +
                          <p>There is no executors</p>
         | 
| 66 | 
            +
                        );
         | 
| 67 | 
            +
                    }
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    if (!lessThan5) {
         | 
| 70 | 
            +
                        body.push(
         | 
| 71 | 
            +
                          <div className="b-host-executors__toggle">
         | 
| 72 | 
            +
                            <a href="#" onClick={this.handleClick}>{msg}</a>
         | 
| 73 | 
            +
                          </div>
         | 
| 74 | 
            +
                        );
         | 
| 75 | 
            +
                    }
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    return (
         | 
| 78 | 
            +
                      <div className="b-host-executors">
         | 
| 79 | 
            +
                        {body}
         | 
| 80 | 
            +
                      </div>
         | 
| 81 | 
            +
                    );
         | 
| 82 | 
            +
                }
         | 
| 83 | 
            +
            });
         |