diff options
69 files changed, 3143 insertions, 2861 deletions
@@ -1,4 +1,4 @@ -Copyright © 2019 Orange Intellectual Property. All rights reserved. +Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ To start the frontend just run the following command: The UI will be available on http://localhost:3000 -In order to start the webservices run the App.java from your Java IDE (the service runs on localhost port 8080). +In order to start the webservices run the App.java from your Java IDE (the service runs on localhost port 8080). ## Building docker images @@ -22,7 +22,7 @@ In order to start the webservices run the App.java from your Java IDE (the servi ## Running GraphGraph locally -You have to start a local instance (i.e. localhost) of schema-service. Afterwards build the GraphGraph JAR file with: +You have to start a local instance (i.e. localhost) of schema-service. Afterwards build the GraphGraph JAR file with: ### `mvn clean install` @@ -30,6 +30,6 @@ Go into the target directory and run the JAR file with -d switch: ### `java -jar graphgraph-X.Y.jar -d` -where X.Y is the current version. For more options run the help via: +where X.Y is the current version. For more options run the help via: -### `java -jar graphgraph-X.Y.jar -h`
\ No newline at end of file +### `java -jar graphgraph-X.Y.jar -h` diff --git a/graphgraph-fe/package.json b/graphgraph-fe/package.json index eaae97a..a124d60 100644 --- a/graphgraph-fe/package.json +++ b/graphgraph-fe/package.json @@ -1,41 +1,41 @@ { - "name": "graphgraph-fe", - "version": "0.0.1", - "private": true, - "dependencies": { - "bootstrap-css-only": "3.3.7", - "d3": "5.7.0", - "eslint-config-react-app": "3.0.6", - "react": "^16.8.6", - "react-table": "6.10.0", - "react-bootstrap": "0.32.4", - "react-dom": "^16.8.6", - "react-numeric-input": "2.2.3", - "react-scripts": "2.1.1", - "reactjs-popup": "1.3.2", - "underscore": "1.9.1" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": "react-app" - }, - "browserslist": [ - ">0.2%", - "not dead", - "not ie <= 11", - "not op_mini all" - ], - "devDependencieseslint": { - "eslint": "^5.6.0", - "eslint-config-standard": "^12.0.0", - "eslint-plugin-import": "^2.17.2", - "eslint-plugin-node": "^8.0.1", - "eslint-plugin-promise": "^4.1.1", - "eslint-plugin-standard": "^4.0.0" - } + "name": "graphgraph-fe", + "version": "0.0.1", + "private": true, + "dependencies": { + "bootstrap-css-only": "3.3.7", + "d3": "5.7.0", + "eslint-config-react-app": "3.0.6", + "react": "^16.8.6", + "react-table": "6.10.0", + "react-bootstrap": "0.32.4", + "react-dom": "^16.8.6", + "react-numeric-input": "2.2.3", + "react-scripts": "2.1.1", + "reactjs-popup": "1.3.2", + "underscore": "1.9.1" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ], + "devDependencieseslint": { + "eslint": "^5.6.0", + "eslint-config-standard": "^12.0.0", + "eslint-plugin-import": "^2.17.2", + "eslint-plugin-node": "^8.0.1", + "eslint-plugin-promise": "^4.1.1", + "eslint-plugin-standard": "^4.0.0" + } } diff --git a/graphgraph-fe/src/App.css b/graphgraph-fe/src/App.css deleted file mode 100644 index 74c1327..0000000 --- a/graphgraph-fe/src/App.css +++ /dev/null @@ -1,53 +0,0 @@ -.App { -width: 100%; -height: 100%; -position: absolute; -top: 0; -left: 0; -} - -.App-logo { - animation: App-logo-spin infinite 20s linear; - height: 40vmin; -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -.root { -width: 100%; -height: 100%; -position: absolute; -top: 0; -left: 0; -} - -.graph-area{ - border-style: solid; - border-color: darkgray; - border-width: 1px 0 1px 0; - position: relative; - width: 100%; - height: 70%; -} diff --git a/graphgraph-fe/src/App.js b/graphgraph-fe/src/App.js deleted file mode 100644 index 788aca1..0000000 --- a/graphgraph-fe/src/App.js +++ /dev/null @@ -1,124 +0,0 @@ -import React from 'react' -import './App.css' -import Graph from './Graph' -import GraphSettingsMenu from './GraphSettingsMenu' -import GraphInfoMenu from './GraphInfoMenu' -import _ from 'underscore' -import { nodeProperty, edgeProperty } from './requests' -import * as constants from './constants' - -var emptyState = { - selectedSchema: '', // currently selected schema - graph: { - nodeNames: [], // names of nodes - edges: [], // edges (each edge has source, target, type and a list of key value properties for tooltip) - paths: [] // all paths between start node and end node - }, - displayedProperties: [], // properties of currently thing (edge or node) - nodeStates: {}, // possible states CLICKED - the currently selected node PATH - currently displayed path - pathIndex: 0, // array index to paths i.e. which path from paths is currently displayed - selectedEdge: { source: 'none', target: 'none' } // defines currently selected edge like like a js object - {source: "pserver", target: "vserver"} -} - -class App extends React.Component { - constructor (props, context) { - super(props, context) - this.graphdata = this.graphdata.bind(this) - this.changePaths = this.changePaths.bind(this) - this.loadNodeProperties = this.loadNodeProperties.bind(this) - this.loadEdgeProperties = this.loadEdgeProperties.bind(this) - this.computeNodeStatesFromPath = this.computeNodeStatesFromPath.bind(this) - this.computeNodeStates = this.computeNodeStates.bind(this) - - this.state = emptyState - } - - loadNodeProperties (nodeName) { - var s = this.state - fetch(nodeProperty(s.selectedSchema, nodeName)) - .then(response => response.json()) - .then(p => { - s['displayedProperties'] = p - s['nodeStates'] = this.computeNodeStates(s['pathIndex']) - // select node - s['nodeStates'][nodeName] = constants.CLICKED - // unselect edge - s['selectedEdge']['source'] = '' - s['selectedEdge']['target'] = '' - this.setState(s) - }) - } - - loadEdgeProperties (source, target) { - var s = this.state - fetch(edgeProperty(s.selectedSchema, source, target)) - .then(response => response.json()) - .then(p => { - s['displayedProperties'] = p - // select edge - s['selectedEdge']['source'] = source - s['selectedEdge']['target'] = target - // unselect node - s['nodeStates'] = this.computeNodeStates(s['pathIndex']) - this.setState(s) - }) - } - - graphdata (data, selectedGraphSchema, graphFingerprint) { - var s = this.state - s['selectedSchema'] = selectedGraphSchema - s['graphFingerprint'] = graphFingerprint - s['graph'] = data - // TODO this should be handled more gracefully ... - if (_.isEmpty(data.edges)) { - alert('The graph has no edges, nothing to display') - } - s['displayedProperties'] = data.startNodeProperties - if (_.isArray(data.paths) && !_.isEmpty(data.paths)) { - s['paths'] = data.paths - s['nodeStates'] = this.computeNodeStatesFromPath(data.paths[0]) - s['pathIndex'] = 0 - } else { - s['paths'] = [] - s['nodeStates'] = {} - } - this.setState(s) - return data - } - - computeNodeStatesFromPath (path) { - return _.reduce(path, (acc, node) => { - acc[node.id] = constants.PATH - return acc - }, {}) - } - - computeNodeStates (pathIndex) { - return this.computeNodeStatesFromPath(this.state.paths[pathIndex]) - } - - changePaths (pathIndex, selectedNode) { - var s = this.state - s['pathIndex'] = pathIndex - this.setState(s) - this.loadNodeProperties(selectedNode) - } - - render () { - let n = _.invert(this.state.nodeStates)[constants.CLICKED] - - let selectedNode = _.isUndefined(n) ? '' : n - return ( - <div className="App"> - <GraphSettingsMenu graphData={this.graphdata} nodePropsLoader={this.loadNodeProperties} selectedNode={selectedNode}/> - <div className='graph-area'> - <Graph graphFingerprint={this.state.graphFingerprint} edgePropsLoader={this.loadEdgeProperties} selectedEdge={this.state.selectedEdge} nodes={this.state.graph.nodeNames} edges={this.state.graph.edges} nodeStates={this.state.nodeStates} nodePropsLoader={this.loadNodeProperties}/> - </div> - - <GraphInfoMenu pathCallback={this.changePaths} paths={ this.state.graph.paths } nodeProperties={ this.state.displayedProperties} /> - </div> - ) - } -} - -export default App diff --git a/graphgraph-fe/src/App.test.js b/graphgraph-fe/src/App.test.js deleted file mode 100644 index 4bf1935..0000000 --- a/graphgraph-fe/src/App.test.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import App from './App' - -it('renders without crashing', () => { - const div = document.createElement('div') - ReactDOM.render(<App />, div) - ReactDOM.unmountComponentAtNode(div) -}) diff --git a/graphgraph-fe/src/DownloadExport.js b/graphgraph-fe/src/DownloadExport.js deleted file mode 100644 index b031773..0000000 --- a/graphgraph-fe/src/DownloadExport.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react' -import { Button } from 'react-bootstrap' -import { exportSchema } from './requests' - - -class DownloadExport extends React.Component { - constructor (props, context) { - super(props, context) - this.download = this.download.bind(this) - } - - download() { - - setTimeout(() => { - const response = { - file: exportSchema(this.props.schemaVersion), - }; - window.open(response.file); - }, 100); - } - - render() { - return ( - <Button onClick={this.download}>Download as XMI</Button> - ); - } -} - -export default DownloadExport diff --git a/graphgraph-fe/src/Graph.js b/graphgraph-fe/src/Graph.js deleted file mode 100644 index c4b1aa0..0000000 --- a/graphgraph-fe/src/Graph.js +++ /dev/null @@ -1,342 +0,0 @@ -import React from 'react' -import * as d3 from 'd3' -import './Graph.css' -import _ from 'underscore' -import * as constants from './constants' - -const CLICKED_COLOR = 'blue' -const PATH_COLOR = 'red' -const EDGE_MOUSE_OVER_COLOR = 'yellow' -const EDGE_NORMAL_COLOR = '#ccc' -const EDGE_LABEL_COLOR = 'black' -const NODE_NORMAL_COLOR = '#E3E3E3' -const NODE_LABEL_COLOR = 'black' -const NODE_MOUSE_OVER_COLOR = '#F6F6F6' -const NODE_BORDER_COLOR = 'lightgray' - -// variable holds state in order to determine if graph should be redrawn completely -// it breaks the react concept, so a better approach is needed -var graphFingerprint = '' - -var htmlfyProperties = function (properties) { - return "<div class='d3-tip'>" + (_.reduce(properties, (html, e) => { return html + "<span style='color: lightgray'>" + e.propertyName + ':</span> <span>' + e.propertyValue + '</span><br/>' }, '')) + '</div>' -} - -var mouseOverEdge = function (edge, div) { - div.transition() - .duration(20) - .style('opacity', 0.9) - div.html(htmlfyProperties(edge.tooltipProperties)) - .style('left', `${d3.event.pageX}px`) - .style('top', `${d3.event.pageY - 28}px`) -} - -var mouseOutEdge = function (edge, div) { - div.transition() - .duration(6000) - .style('opacity', 0) -} - -var addEdgePaths = function (links, g) { - d3.select('body').append('div') - .attr('class', 'tooltip') - .style('opacity', 0) - g.selectAll('.edgepath') - .data(links) - .enter() - .append('path') - .attr('d', d => `M ${d.source.x} ${d.source.y} L ${d.target.x} ${d.target.y}`) - .attr('class', 'edgepath') - .attr('fill-opacity', 0) - .attr('stroke-opacity', 0) - .attr('fill', EDGE_LABEL_COLOR) - .attr('id', (d, i) => `edgepath${i}`) -} - -var chooseColor = function (state) { - if (state === constants.CLICKED) { - return CLICKED_COLOR - } - - if (state === constants.PATH) { - return PATH_COLOR - } - - return NODE_NORMAL_COLOR -} - -var redrawNodeColors = function (nodeStates, selectedEdge) { - d3.selectAll('svg').selectAll('g').selectAll('circle').style('fill', n => chooseColor(nodeStates[n.id])) - d3.selectAll('svg').selectAll('g').selectAll('line').style('stroke', EDGE_NORMAL_COLOR).attr('oldStroke', EDGE_NORMAL_COLOR) - d3.selectAll('svg').selectAll('g').selectAll('line').filter(edge => edge.source.id === selectedEdge.source && edge.target.id === selectedEdge.target).attr('oldStroke', CLICKED_COLOR).style('stroke', CLICKED_COLOR) -} - -var addEdgeLabels = function (links, g, div) { - var edgelabels = g.selectAll('.edgelabel') - .data(links) - .enter() - .append('text') - .attr('class', 'edgelabel') - .attr('text-anchor', 'middle') - .attr('dx', 200) - .attr('dy', 0) - .attr('font-size', '22px') - .attr('id', (d, i) => `edgelabel${i}`) - - edgelabels.append('textPath') - .attr('xlink:href', (d, i) => `#edgepath${i}`) - .text((d, i) => d.type) -} - -var addNodeLabels = function (nodes, g) { - g.selectAll('.nodelabel') - .data(nodes) - .enter() - .append('text') - .attr('x', d => d.x - 14) - .attr('y', d => d.y - 17) - .attr('class', 'nodelabel') - .attr('fill', NODE_LABEL_COLOR) - .attr('font-size', '32px') - .text(d => d.id) - .on('mouseenter', onNodeLabelMouseOver) - .on('mouseout', onNodeLabelMouseOut) -} - -var addLinks = function (links, g, div, edgePropsLoader) { - let ss = _.filter(links, l => l.source.id === l.target.id) - - let selfLinks = _.isUndefined(ss) ? [] : ss - - g.selectAll('ellipse') - .data(selfLinks) - .enter().append('ellipse') - .attr('fill-opacity', 0) - .attr('rx', d => 100) - .attr('ry', d => 16) - .attr('cx', d => d.target.x + 80) - .attr('cy', d => d.target.y) - .style('stroke', NODE_BORDER_COLOR) - .attr('stroke-width', 5) - .on('click', edge => edgePropsLoader(edge.source.id, edge.target.id)) - .on('mouseenter', function (edge) { - mouseOverEdge(edge, div) - d3.select(this) - .transition() - .attr('oldStroke', EDGE_NORMAL_COLOR) - .duration(10) - .style('stroke', EDGE_MOUSE_OVER_COLOR) - }) - .on('mouseleave', function (edge) { - mouseOutEdge(edge, div) - var strokeColor = d3.select(this).attr('oldStroke') - d3.select(this) - .transition() - .duration(300) - .style('stroke', strokeColor) - }) - - g.selectAll('.edgelooplabel') - .data(selfLinks) - .enter() - .append('text') - .attr('x', d => d.source.x + 35) - .attr('y', d => d.source.y + 50) - .attr('class', 'edgelooplabel') - .attr('fill', NODE_LABEL_COLOR) - .attr('font-size', '22px') - .text(d => d.type) - - g.selectAll('line') - .data(links) - .enter().append('line') - .attr('stroke-width', 5) - .attr('x1', d => d.source.x) - .attr('y1', d => d.source.y) - .attr('x2', d => d.target.x) - .attr('y2', d => d.target.y) - .attr('id', (d, i) => `edge${i}`) - .attr('marker-end', 'url(#arrowhead)') - .style('stroke', EDGE_NORMAL_COLOR) - .on('click', edge => edgePropsLoader(edge.source.id, edge.target.id)) - .on('mouseenter', function (edge) { - mouseOverEdge(edge, div) - d3.select(this) - .transition() - .attr('oldStroke', EDGE_NORMAL_COLOR) - .duration(10) - .style('stroke', EDGE_MOUSE_OVER_COLOR) - }) - .on('mouseleave', function (edge) { - mouseOutEdge(edge, div) - var strokeColor = d3.select(this).attr('oldStroke') - d3.select(this) - .transition() - .duration(300) - .style('stroke', strokeColor) - }) -} - -var addMarkers = function (g, svg) { - g.append('defs').append('marker') - .attr('id', 'arrowhead') - .attr('viewBox', '-0 -5 10 10') - .attr('refX', '20') - .attr('refY', '0') - .attr('orient', 'auto') - .attr('markerWidth', '4') - .attr('markerHeight', '4') - .attr('xoverflow', 'visible') - .append('svg:path') - .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', EDGE_NORMAL_COLOR) - .attr('stroke', EDGE_NORMAL_COLOR) - - var zoomHandler = d3.zoom() - .on('zoom', _ => g.attr('transform', d3.event.transform)) - - zoomHandler(svg) - zoomHandler.translateTo(svg, -7000, -4000) - zoomHandler.scaleTo(svg, 0.08) -} - -var drawGraph = function (nodes, links, g, simulation, svg, addNodes, edgePropsLoader) { - for (var i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())); i < n; ++i) { - simulation.tick() - } - - var div = d3.select('body').append('div') - .attr('class', 'tooltip') - .style('opacity', 0) - - addLinks(links, g, div, edgePropsLoader) - addNodes(nodes, g) - addNodeLabels(nodes, g) - addEdgePaths(links, g) - addEdgeLabels(links, g, div) - addMarkers(g, svg) -} - -var prepareLinks = function (nodes, links) { - var result = [] - links.forEach(e => { - var sourceNode = nodes.filter(n => n.id === e.source)[0] - var targetNode = nodes.filter(n => n.id === e.target)[0] - - result.push({ - source: sourceNode, - target: targetNode, - type: e.type, - tooltipProperties: e.tooltipProperties - }) - }) - - return result -} - -var createSimulation = function (nodes, links) { - return d3.forceSimulation(nodes) - .force('charge', d3.forceManyBody().strength(-201)) - .force('link', d3.forceLink(links).distance(1200).strength(1).iterations(400)) - .force('collision', d3.forceCollide().radius(d => 310)) - .force('x', d3.forceX()) - .force('y', d3.forceY()) - .stop() -} - -var createSvg = function () { - var svg = d3.select('#graph').append('svg').attr('height', '100%').attr('width', '100%') - var g = svg.append('g') - - return { 'g': g, 'svg': svg } -} - -var onNodeLabelMouseOut = function () { - d3.select(this) - .transition() - .duration(600) - .attr('font-size', '32px') -} - -var onNodeLabelMouseOver = function () { - d3.select(this) - .transition() - .duration(200) - .attr('font-size', '232px') -} - -var onNodeMouseOver = function () { - var oldFill = d3.select(this).style('fill') - d3.select(this) - .transition() - .duration(200) - .attr('r', 31) - .attr('oldFill', oldFill) - .style('fill', NODE_MOUSE_OVER_COLOR) -} - -var onNodeMouseOut = function (nodeStates) { - var oldFill = d3.select(this).attr('oldFill') - d3.select(this) - .transition() - .duration(300) - .attr('r', 23) - .style('fill', oldFill) -} - -class Graph extends React.Component { - onNodeClick (x) { - // on mouse out the node will change color read from 'oldFill' attribute - d3.selectAll('svg').selectAll('g').selectAll('circle').filter(c => c.id === x.id).attr('oldFill', CLICKED_COLOR) - this.props.nodePropsLoader(x.id) - } - - addNodes (nodes, g) { - g.selectAll('circle') - .data(nodes) - .enter().append('circle') - .attr('cx', d => d.x) - .attr('cy', d => d.y) - .style('fill', n => { - return chooseColor(this.props.nodeStates[n.id]) - }) - .style('stroke', NODE_BORDER_COLOR) - .attr('stroke-width', 3) - .attr('r', 23) - .on('click', this.onNodeClick) - .on('mouseover', onNodeMouseOver) - .on('mouseout', onNodeMouseOut) - } - - reCreateGraph () { - d3.select('#graph').selectAll('*').remove() - - var nodes = this.props.nodes - var links = prepareLinks(this.props.nodes, this.props.edges) - var o = createSvg() - var simulation = createSimulation(nodes, links) - - drawGraph(nodes, links, o.g, simulation, o.svg, this.addNodes, this.props.edgePropsLoader) - } - - constructor (props, context) { - super(props, context) - - this.reCreateGraph = this.reCreateGraph.bind(this) - this.addNodes = this.addNodes.bind(this) - this.onNodeClick = this.onNodeClick.bind(this) - } - - render () { - if (this.props.graphFingerprint !== graphFingerprint) { - this.reCreateGraph() - graphFingerprint = this.props.graphFingerprint - } else { - redrawNodeColors(this.props.nodeStates, this.props.selectedEdge) - } - - return (<div id="graph"/>) - } -} - -export default Graph diff --git a/graphgraph-fe/src/GraphHops.css b/graphgraph-fe/src/GraphHops.css deleted file mode 100644 index b6207eb..0000000 --- a/graphgraph-fe/src/GraphHops.css +++ /dev/null @@ -1,9 +0,0 @@ - -.hops-input{ -display: flex; -flex-direction: column; -} - -.hops-input-field{ -width: 190px; -} diff --git a/graphgraph-fe/src/GraphHops.js b/graphgraph-fe/src/GraphHops.js deleted file mode 100644 index de7a4cc..0000000 --- a/graphgraph-fe/src/GraphHops.js +++ /dev/null @@ -1,70 +0,0 @@ -import React from 'react' -import { Label } from 'react-bootstrap' -import NumericInput from 'react-numeric-input' -import './GraphHops.css' - -var createNumInput = function (label, callback, current) { - return ( - <div> - <Label>{label}</Label> - <NumericInput onChange={callback} min={1} max={500} value={current} className="hops-input-field" /> - </div> - ) -} - -class GraphHops extends React.Component { - constructor (props) { - super(props) - - this.state = { - value: this.props.defaultValue - } - let p = props.parentHops - let c = props.cousinHops - let ch = props.childHops - - this.onChangeParent = (e) => this._onChangeParent(e) - this.onChangeCousin = (e) => this._onChangeCousin(e) - this.onChangeChild = (e) => this._onChangeChild(e) - this.onChange = (hopsName, num) => this._onChange(hopsName, num) - this.state = { parentHops: p, childHops: ch, cousinHops: c } - } - - _onChange (hopsName, num) { - var s = this.state - s[hopsName] = num - this.setState(s) - this.props.updateHops(this.state.parentHops, this.state.cousinHops, this.state.childHops) - } - - _onChangeParent (e) { - this.onChange('parentHops', e) - } - - _onChangeCousin (e) { - this.onChange('cousinHops', e) - } - - _onChangeChild (e) { - this.onChange('childHops', e) - } - - render () { - if (this.props.edgeFilter === 'Edgerules'){ - return ( - <div className="hops-input"> - {createNumInput('edgerule hops', this.onChangeCousin, this.state.cousinHops)} - </div> - ) - } - - return ( - <div className="hops-input"> - {createNumInput('parent hops', this.onChangeParent, this.state.parentHops)} - {createNumInput('child hops', this.onChangeChild, this.state.childHops)} - </div> - ) - } -} - -export default GraphHops diff --git a/graphgraph-fe/src/GraphInfoMenu.js b/graphgraph-fe/src/GraphInfoMenu.js deleted file mode 100644 index d001d50..0000000 --- a/graphgraph-fe/src/GraphInfoMenu.js +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react' -import './GraphInfoMenu.css' -import PathBreadCrumb from './PathBreadCrumb' -import _ from 'underscore' -import ReactTable from "react-table"; -import "react-table/react-table.css"; - -class GraphInfoMenu extends React.Component { - - render () { - var paths = this.props.paths - var callback = this.props.pathCallback - var showPaths = _.isArray(paths) && !_.isEmpty(paths) - var breadcrumbs = _.map(paths, (path, i) => <PathBreadCrumb key={i} index={i} pathCallback={callback} path={path}/>) - return ( - <div className="node-property-list"> - <div className="fixed-height-container" style={{ display: showPaths ? 'block' : 'none' }}> - <p className='path-heading'>Paths</p> - {breadcrumbs} - </div> - <div className="kv-table datatable"> - <ReactTable pageSizeOptions={[4, 25]} data={this.props.nodeProperties} - columns={[ - { - Header: "Attribute", - accessor: "propertyName", - minWidth: 40 - }, - { - Header: "Type", - accessor: "type", - minWidth: 70 - }, - { - Header: "Description", - accessor: "description", - minWidth: 260 - }, - { - id: "Key", - Header: "Key", - accessor: p => p.key ? "yes" : "", - minWidth: 20 - }, - { - id: "Index", - Header: "Index", - accessor: p => p.index ? "yes" : "", - minWidth: 20 - }, - { - id: "Required", - Header: "Required", - accessor: p => p.required ? "yes" : "", - minWidth: 20 - } - ]} - defaultPageSize={4} - className="-striped -highlight" - /> - </div> - </div> - ) - } -} - -export default GraphInfoMenu diff --git a/graphgraph-fe/src/GraphSettings.js b/graphgraph-fe/src/GraphSettings.js deleted file mode 100644 index d511068..0000000 --- a/graphgraph-fe/src/GraphSettings.js +++ /dev/null @@ -1,239 +0,0 @@ -import React from 'react' -import _ from 'underscore' -import { DropdownButton, MenuItem, Label } from 'react-bootstrap' -import './GraphSettings.css' -import Popup from './PopupSettings' -import ValidationModal from './ValidationModal' -import DownloadExport from './DownloadExport' -import { validateSchema, pathGraph, basicGraph, schemas, nodeNames } from './requests' - -var emptyState = { - schemaProblems: [], - nodeNames: [], - fromNode: '', - graph: { - nodeNames: [], - edges: [] - }, - showHops: false, - enableDestinationNode: false, - toNode: '', - edgeFilter: 'Edgerules', - hops: { - parents: 1, - cousin: 1, - child: 1 - }, - selectedSchema: '' -} - -class GraphSettings extends React.Component { - constructor (props, context) { - super(props, context) - this.onChangeStartNode = this.onChangeStartNode.bind(this) - this.onSelectNode = this.onSelectNode.bind(this) - this.selectSchema = this.selectSchema.bind(this) - this.onChangeToNode = this.onChangeToNode.bind(this) - this.loadInitialGraph = this.loadInitialGraph.bind(this) - this.updateHops = this.updateHops.bind(this) - this.changeEdgeFilter = this.changeEdgeFilter.bind(this) - this.graphFingerprint = this.graphFingerprint.bind(this) - this.state = emptyState - } - // this serves as a config 'fingerprint' to know if the d3 visualisation should be redrawn from scratch or just updated - graphFingerprint (schema, from, to, parents, cousin, child, edgeFilter) { - return `${schema}:${from}:${to}:${parents}:${cousin}:${child}:${edgeFilter}` - } - - loadInitialGraph (startNode, endNode, parentHops, cousinHops, childHops, edgeFilter) { - if (this.state.selectedSchema === '' || startNode === 'none') { - var s = this.state - s['edgeFilter'] = edgeFilter - this.setState(s) - return - } - if (startNode === 'all') { - endNode = 'none' - } - - let requestUri = endNode === 'none' - ? basicGraph(this.state.selectedSchema, startNode, parentHops, cousinHops, childHops, edgeFilter) : pathGraph(this.state.selectedSchema, startNode, endNode, edgeFilter) - - fetch(requestUri) - .then(response => response.json()) - .then(g => { - let schema = this.state.selectedSchema - - let f = this.graphFingerprint(schema, startNode, endNode, parentHops, cousinHops, childHops, edgeFilter) - this.props.graphData(g, this.state.selectedSchema, f) - return g - }) - .then(g => { - var s = this.state - s['hops']['parents'] = parentHops - s['hops']['cousin'] = cousinHops - s['hops']['child'] = childHops - s['fromNode'] = startNode - s['toNode'] = endNode - s['graph'] = g - s['edgeFilter'] = edgeFilter - s['showHops'] = endNode === 'none' && startNode !== 'none' && startNode !== 'all' - s['enableDestinationNode'] = startNode !== 'none' && startNode !== 'all' - this.setState(s) - - if (startNode !== 'all') { - this.onSelectNode(startNode) - } - }) - } - - selectSchema (schema) { - var s = this.state - s['selectedSchema'] = schema - fetch(nodeNames(schema, s['edgeFilter'])) - .then(response => response.json()) - .then(nodeNames => { - s['fromNode'] = s['toNode'] = 'none' - s['nodeNames'] = nodeNames - this.setState(s) - }) - fetch(validateSchema(schema)) - .then(response => response.json()) - .then(p => { - s['schemaProblems'] = p.problems - this.setState(s) - }) - } - - changeEdgeFilter (edgeFilter) { - fetch(nodeNames(this.state.selectedSchema, edgeFilter)) - .then(response => response.json()) - .then(nodeNames => { - let s = this.state - s['edgeFilter'] = edgeFilter - s['fromNode'] = s['toNode'] = 'none' - s['nodeNames'] = nodeNames - this.setState(s) - }) - this.loadInitialGraph( - this.state.fromNode, - this.state.toNode, - this.state.hops.parents, - this.state.hops.cousin, - this.state.hops.child, - edgeFilter - ) - } - - updateHops (parentHops, cousinHops, childHops) { - this.loadInitialGraph( - this.state.fromNode, - this.state.toNode, - parentHops, - cousinHops, - childHops, - this.state.edgeFilter) - } - - onChangeToNode (eventKey) { - this.loadInitialGraph(this.state.fromNode, - eventKey, - this.state.hops.parents, - this.state.hops.cousin, - this.state.hops.child, - this.state.edgeFilter) - } - - onSelectNode (eventKey) { - this.props.nodePropsLoader(eventKey) - } - - onChangeStartNode (eventKey) { - this.loadInitialGraph(eventKey, this.state.toNode, - this.state.hops.parents, - this.state.hops.cousin, - this.state.hops.child, - this.state.edgeFilter) - } - - componentDidMount () { - fetch(schemas()) - .then(response => response.json()) - .then(schemas => { - let s = this.state - s['schemas'] = schemas - this.setState(s) - }) - } - - render () { - var schemas = _.map(this.state.schemas, (x, k) => <MenuItem key={k} eventKey={x}>{x}</MenuItem>) - - var items = _.map(this.state.nodeNames, (x, k) => <MenuItem key={k} eventKey={x.id}>{x.id}</MenuItem>) - let sortedNames = _.sortBy(this.state.graph.nodeNames, 'id') - var currentNodeNames = _.map(sortedNames, (x, k) => <MenuItem key={k} eventKey={x.id}>{x.id}</MenuItem>) - - var fromItems = items.slice() - fromItems.unshift(<MenuItem key='divider' divider />) - fromItems.unshift(<MenuItem key='all' eventKey='all'>all</MenuItem>) - - items.unshift(<MenuItem key='anotherdivider' divider />) - items.unshift(<MenuItem key='none' eventKey='none'>none</MenuItem>) - - let edgeFilterItems = [ - <MenuItem key='Edgerules' eventKey='Edgerules'>Edgerules</MenuItem>, - <MenuItem key='Parents' eventKey='Parents'>Parent-child (OXM structure)</MenuItem>, - ] - return ( - <div> - <div className="graph-menu"> - <div className="startendnode-dropdown"> - <div> - <Label>Schemas</Label> - <DropdownButton className="schemas-dropdown" onSelect={this.selectSchema} id="schemas" title={this.state.selectedSchema}> - {schemas} - </DropdownButton> - </div> - <div className="source-dropdown-div"> - <Label>Source Node</Label> - <DropdownButton className="node-dropdown" onSelect={this.onChangeStartNode} id="namesFrom" title={this.state.fromNode}> - {fromItems} - </DropdownButton> - </div> - <div> - <Label>Destination Node</Label> - <DropdownButton disabled={!this.state.enableDestinationNode} className="node-dropdown" onSelect={this.onChangeToNode} id="namesTo" title={this.state.toNode}> - {items} - </DropdownButton> - </div> - <div className="source-dropdown-div"> - <Label>Edge filter</Label> - <DropdownButton className="node-dropdown" onSelect={this.changeEdgeFilter} id="filterEdge" title={this.state.edgeFilter}> - {edgeFilterItems} - </DropdownButton> - </div> - <div className="source-dropdown-div"> - <Label>Selected Node</Label> - <DropdownButton className="node-dropdown" onSelect={this.onSelectNode} id="selectedNode" title={this.props.selectedNode}> - {currentNodeNames} - </DropdownButton> - </div> - - <Popup isDisabled={!this.state.showHops} edgeFilter={this.state.edgeFilter} parentHops={this.state.hops.parents} childHops={this.state.hops.child} cousinHops={this.state.hops.cousin} updateHops={this.updateHops}/> - <div className="modal-button"> - <ValidationModal schemaProblems={this.state.schemaProblems}/> - </div> - - <div className="modal-button"> - <DownloadExport schemaVersion={this.state.selectedSchema}/> - </div> - - </div> - - </div> - </div> - ) - } -} - -export default GraphSettings diff --git a/graphgraph-fe/src/GraphSettingsMenu.js b/graphgraph-fe/src/GraphSettingsMenu.js deleted file mode 100644 index 5ce1e12..0000000 --- a/graphgraph-fe/src/GraphSettingsMenu.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react' -import GraphSettings from './GraphSettings' -import { Navbar, Nav } from 'react-bootstrap' -import './GraphSettingsMenu.css' - -class GraphSettingsMenu extends React.Component { - render () { - return ( - <Navbar className='navbar-adjust'> - <Navbar.Header> - <Navbar.Brand> - <a href="https://gerrit.onap.org/r/gitweb?p=aai/graphgraph.git">GraphGraph</a> - </Navbar.Brand> - </Navbar.Header> - <Nav className="mr-auto"> - <Navbar.Collapse className='mr-sm-2'> - <GraphSettings selectedNode={this.props.selectedNode} graphData={this.props.graphData} nodePropsLoader={this.props.nodePropsLoader} /> - </Navbar.Collapse> -</Nav> - </Navbar>) - } -} - -export default GraphSettingsMenu diff --git a/graphgraph-fe/src/PathBreadCrumb.js b/graphgraph-fe/src/PathBreadCrumb.js deleted file mode 100644 index a2c508a..0000000 --- a/graphgraph-fe/src/PathBreadCrumb.js +++ /dev/null @@ -1,26 +0,0 @@ -import _ from 'underscore' -import React from 'react' -import { Breadcrumb } from 'react-bootstrap' - -class PathBreadCrumb extends React.Component { - constructor (props, context) { - super(props, context) - this.pathSelected = this.pathSelected.bind(this) - } - - pathSelected (evt) { - evt.preventDefault() - // the data is only piggyback riding on the "target" property .. not nice but works - this.props.pathCallback(this.props.index, evt.target.getAttribute('target')) - } - - render () { - var path = this.props.path - var callback = this.pathSelected - var items = _.map(path, (item, i) => <Breadcrumb.Item key={i} target={item.id} onClick={callback}> {item.id} </Breadcrumb.Item>) - - return (<Breadcrumb>{items}</Breadcrumb>) - } -} - -export default PathBreadCrumb diff --git a/graphgraph-fe/src/PopupSettings.js b/graphgraph-fe/src/PopupSettings.js deleted file mode 100644 index cb8a533..0000000 --- a/graphgraph-fe/src/PopupSettings.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react' -import Popup from 'reactjs-popup' -import './PopupSettings.css' -import GraphHops from './GraphHops' - -class PopupMenu extends React.Component { - render () { - return ( - <Popup trigger={<button className='settings-button' disabled={this.props.isDisabled}>Hops</button>} position="bottom right"> - {close => ( - <div> - <GraphHops edgeFilter={this.props.edgeFilter} parentHops={this.props.parentHops} childHops={this.props.childHops} cousinHops={this.props.cousinHops} updateHops={this.props.updateHops} /> - <button - type="button" - className="link-button, close" - onClick={close}> - × - </button> - </div> - )} - </Popup> - - ) - } -} - -export default PopupMenu diff --git a/graphgraph-fe/src/ValidationModal.js b/graphgraph-fe/src/ValidationModal.js deleted file mode 100644 index 8bf1989..0000000 --- a/graphgraph-fe/src/ValidationModal.js +++ /dev/null @@ -1,49 +0,0 @@ -import _ from 'underscore' -import React from 'react' -import './ValidationModal.css' -import { Button, Modal, ListGroup, ListGroupItem } from 'react-bootstrap' - - -class ValidationModal extends React.Component { - constructor(...args) { - super(...args); - this.state = { showModal: false }; - - this.close = () => { - this.setState({ showModal: false }); - }; - - this.open = () => { - this.setState({ showModal: true }); - }; - } - - renderBackdrop(props) { - return <div {...props} className="modal-backdrop" />; - } - - render() { - var problems = this.props.schemaProblems - var items = _.map(problems, (problem, i) => <ListGroupItem key={i}> {problem} </ListGroupItem>) - return ( - <div> - <Button onClick={this.open}>Validate schema</Button> - <Modal - onHide={this.close} - className="modal-validator" - aria-labelledby="modal-label" - show={this.state.showModal} - renderBackdrop={this.renderBackdrop} - > - <div className="modal-list"> - <ListGroup> - {items} - </ListGroup> - </div> - </Modal> - </div> - ); - } -} - -export default ValidationModal diff --git a/graphgraph-fe/src/app.css b/graphgraph-fe/src/app.css new file mode 100644 index 0000000..8c39ae0 --- /dev/null +++ b/graphgraph-fe/src/app.css @@ -0,0 +1,53 @@ +.App { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; +} + +.App-logo { + animation: App-logo-spin infinite 20s linear; + height: 40vmin; +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.root { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; +} + +.graph-area{ + border-style: solid; + border-color: darkgray; + border-width: 1px 0 1px 0; + position: relative; + width: 100%; + height: 70%; +} diff --git a/graphgraph-fe/src/app.js b/graphgraph-fe/src/app.js new file mode 100644 index 0000000..30eb3d8 --- /dev/null +++ b/graphgraph-fe/src/app.js @@ -0,0 +1,152 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +import React from 'react'; +import './app.css'; +import Graph from './graph.js'; +import GraphSettingsMenu from './graph_settings_menu.js'; +import GraphInfoMenu from './graph_info_menu.js'; +import _ from 'underscore'; +import { nodeProperty, edgeProperty } from './requests.js'; +import * as constants from './constants.js'; + +var emptyState = { + // currently selected schema + selectedSchema: '', + graph: { + // names of nodes + nodeNames: [], + // edges (each edge has source, target, type and a list of key value properties for tooltip) + edges: [], + // all paths between start node and end node + paths: [] + }, + // properties of currently thing (edge or node) + displayedProperties: [], + // possible states: + // CLICKED - the currently selected node + // PATH - currently displayed path + nodeStates: {}, + // array index to paths i.e. which path from paths is currently displayed + pathIndex: 0, + // defines currently selected edge like a js object - {source: "pserver", target: "vserver"} + selectedEdge: { source: 'none', target: 'none' } +}; + +class App extends React.Component { + constructor (props, context) { + super(props, context); + this.graphdata = this.graphdata.bind(this); + this.changePaths = this.changePaths.bind(this); + this.loadNodeProperties = this.loadNodeProperties.bind(this); + this.loadEdgeProperties = this.loadEdgeProperties.bind(this); + this.computeNodeStatesFromPath = this.computeNodeStatesFromPath.bind(this); + this.computeNodeStates = this.computeNodeStates.bind(this); + this.state = emptyState; + } + + loadNodeProperties (nodeName) { + var s = this.state; + fetch(nodeProperty(s.selectedSchema, nodeName)) + .then(response => response.json()) + .then(p => { + s['displayedProperties'] = p; + s['nodeStates'] = this.computeNodeStates(s['pathIndex']); + // select node + s['nodeStates'][nodeName] = constants.CLICKED; + // unselect edge + s['selectedEdge']['source'] = ''; + s['selectedEdge']['target'] = ''; + this.setState(s); + }); + } + + loadEdgeProperties (source, target) { + var s = this.state; + fetch(edgeProperty(s.selectedSchema, source, target)) + .then(response => response.json()) + .then(p => { + s['displayedProperties'] = p; + // select edge + s['selectedEdge']['source'] = source; + s['selectedEdge']['target'] = target; + // unselect node + s['nodeStates'] = this.computeNodeStates(s['pathIndex']); + this.setState(s); + }); + } + + graphdata (data, selectedGraphSchema, graphFingerprint) { + var s = this.state; + s['selectedSchema'] = selectedGraphSchema; + s['graphFingerprint'] = graphFingerprint; + s['graph'] = data; + // TODO this should be handled more gracefully ... + if (_.isEmpty(data.edges)) { + alert('The graph has no edges, nothing to display'); + } + s['displayedProperties'] = data.startNodeProperties; + if (_.isArray(data.paths) && !_.isEmpty(data.paths)) { + s['paths'] = data.paths; + s['nodeStates'] = this.computeNodeStatesFromPath(data.paths[0]); + s['pathIndex'] = 0; + } else { + s['paths'] = []; + s['nodeStates'] = {}; + } + this.setState(s); + return data; + } + + computeNodeStatesFromPath (path) { + return _.reduce(path, (acc, node) => { + acc[node.id] = constants.PATH; + return acc; + }, {}); + } + + computeNodeStates (pathIndex) { + return this.computeNodeStatesFromPath(this.state.paths[pathIndex]); + } + + changePaths (pathIndex, selectedNode) { + var s = this.state; + s['pathIndex'] = pathIndex; + this.setState(s); + this.loadNodeProperties(selectedNode); + } + + render () { + let n = _.invert(this.state.nodeStates)[constants.CLICKED]; + + let selectedNode = _.isUndefined(n) ? '' : n; + return ( + <div className="App"> + <GraphSettingsMenu graphData={this.graphdata} nodePropsLoader={this.loadNodeProperties} selectedNode={selectedNode}/> + <div className='graph-area'> + <Graph graphFingerprint={this.state.graphFingerprint} edgePropsLoader={this.loadEdgeProperties} selectedEdge={this.state.selectedEdge} nodes={this.state.graph.nodeNames} edges={this.state.graph.edges} nodeStates={this.state.nodeStates} nodePropsLoader={this.loadNodeProperties}/> + </div> + <GraphInfoMenu pathCallback={this.changePaths} paths={ this.state.graph.paths } nodeProperties={ this.state.displayedProperties}/> + </div> + ); + } +} + +export default App; diff --git a/graphgraph-fe/src/app.test.js b/graphgraph-fe/src/app.test.js new file mode 100644 index 0000000..893bcbb --- /dev/null +++ b/graphgraph-fe/src/app.test.js @@ -0,0 +1,29 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './app.js'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(<App/>, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/graphgraph-fe/src/constants.js b/graphgraph-fe/src/constants.js index 6076e6f..9181850 100644 --- a/graphgraph-fe/src/constants.js +++ b/graphgraph-fe/src/constants.js @@ -1,2 +1,22 @@ -export const CLICKED = 'clicked' -export const PATH = 'on-path' +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +export const CLICKED = 'clicked'; +export const PATH = 'on-path'; diff --git a/graphgraph-fe/src/download_export.js b/graphgraph-fe/src/download_export.js new file mode 100644 index 0000000..21b2829 --- /dev/null +++ b/graphgraph-fe/src/download_export.js @@ -0,0 +1,43 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +import React from 'react'; +import { Button } from 'react-bootstrap'; +import { exportSchema } from './requests.js'; + +class DownloadExport extends React.Component { + constructor (props, context) { + super(props, context); + this.download = this.download.bind(this); + } + + download() { + setTimeout(() => { + const response = { file: exportSchema(this.props.schemaVersion) }; + window.open(response.file); + }, 100); + } + + render() { + return <Button onClick={this.download}>Download as XMI</Button>; + } +} + +export default DownloadExport; diff --git a/graphgraph-fe/src/Graph.css b/graphgraph-fe/src/graph.css index 1e5c7d8..0c9460f 100644 --- a/graphgraph-fe/src/Graph.css +++ b/graphgraph-fe/src/graph.css @@ -17,4 +17,3 @@ color: #fff; border-radius: 2px; } - diff --git a/graphgraph-fe/src/graph.js b/graphgraph-fe/src/graph.js new file mode 100644 index 0000000..492959a --- /dev/null +++ b/graphgraph-fe/src/graph.js @@ -0,0 +1,372 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +import React from 'react'; +import * as d3 from 'd3'; +import './graph.css'; +import _ from 'underscore'; +import * as constants from './constants.js'; + +const CLICKED_COLOR = 'blue'; +const PATH_COLOR = 'red'; +const EDGE_MOUSE_OVER_COLOR = 'yellow'; +const EDGE_NORMAL_COLOR = '#ccc'; +const EDGE_LABEL_COLOR = 'black'; +const NODE_NORMAL_COLOR = '#E3E3E3'; +const NODE_LABEL_COLOR = 'black'; +const NODE_MOUSE_OVER_COLOR = '#F6F6F6'; +const NODE_BORDER_COLOR = 'lightgray'; + +// variable holds state in order to determine if graph should be redrawn completely +// it breaks the react concept, so a better approach is needed +var graphFingerprint = ''; + +var htmlfyProperties = function (properties) { + return "<div class='d3-tip'>" + (_.reduce(properties, (html, e) => { + return html + "<span style='color: lightgray'>" + + e.propertyName + ':</span> <span>' + + e.propertyValue + '</span><br/>'; + }, '')) + '</div>'; +}; + +var mouseOverEdge = function (edge, div) { + div.transition() + .duration(20) + .style('opacity', 0.9); + div.html(htmlfyProperties(edge.tooltipProperties)) + .style('left', `${d3.event.pageX}px`) + .style('top', `${d3.event.pageY - 28}px`); +}; + +var mouseOutEdge = function (edge, div) { + div.transition() + .duration(6000) + .style('opacity', 0); +}; + +var addEdgePaths = function (links, g) { + d3.select('body').append('div') + .attr('class', 'tooltip') + .style('opacity', 0); + g.selectAll('.edgepath') + .data(links) + .enter() + .append('path') + .attr('d', d => `M ${d.source.x} ${d.source.y} L ${d.target.x} ${d.target.y}`) + .attr('class', 'edgepath') + .attr('fill-opacity', 0) + .attr('stroke-opacity', 0) + .attr('fill', EDGE_LABEL_COLOR) + .attr('id', (d, i) => `edgepath${i}`); +}; + +var chooseColor = function (state) { + if (state === constants.CLICKED) { + return CLICKED_COLOR; + } + if (state === constants.PATH) { + return PATH_COLOR; + } + return NODE_NORMAL_COLOR; +}; + +var redrawNodeColors = function (nodeStates, selectedEdge) { + d3.selectAll('svg').selectAll('g').selectAll('circle') + .style('fill', n => chooseColor(nodeStates[n.id])); + d3.selectAll('svg').selectAll('g').selectAll('line') + .style('stroke', EDGE_NORMAL_COLOR).attr('oldStroke', EDGE_NORMAL_COLOR); + d3.selectAll('svg').selectAll('g').selectAll('line') + .filter(edge => edge.source.id === selectedEdge.source && edge.target.id === selectedEdge.target) + .attr('oldStroke', CLICKED_COLOR) + .style('stroke', CLICKED_COLOR); +}; + +var addEdgeLabels = function (links, g, div) { + var edgelabels = g.selectAll('.edgelabel') + .data(links) + .enter() + .append('text') + .attr('class', 'edgelabel') + .attr('text-anchor', 'middle') + .attr('dx', 200) + .attr('dy', 0) + .attr('font-size', '22px') + .attr('id', (d, i) => `edgelabel${i}`); + + edgelabels.append('textPath') + .attr('xlink:href', (d, i) => `#edgepath${i}`) + .text((d, i) => d.type); +}; + +var addNodeLabels = function (nodes, g) { + g.selectAll('.nodelabel') + .data(nodes) + .enter() + .append('text') + .attr('x', d => d.x - 14) + .attr('y', d => d.y - 17) + .attr('class', 'nodelabel') + .attr('fill', NODE_LABEL_COLOR) + .attr('font-size', '32px') + .text(d => d.id) + .on('mouseenter', onNodeLabelMouseOver) + .on('mouseout', onNodeLabelMouseOut); +}; + +var addLinks = function (links, g, div, edgePropsLoader) { + let ss = _.filter(links, l => l.source.id === l.target.id); + + let selfLinks = _.isUndefined(ss) ? [] : ss; + + g.selectAll('ellipse') + .data(selfLinks) + .enter().append('ellipse') + .attr('fill-opacity', 0) + .attr('rx', d => 100) + .attr('ry', d => 16) + .attr('cx', d => d.target.x + 80) + .attr('cy', d => d.target.y) + .style('stroke', NODE_BORDER_COLOR) + .attr('stroke-width', 5) + .on('click', edge => edgePropsLoader(edge.source.id, edge.target.id)) + .on('mouseenter', function (edge) { + mouseOverEdge(edge, div); + d3.select(this) + .transition() + .attr('oldStroke', EDGE_NORMAL_COLOR) + .duration(10) + .style('stroke', EDGE_MOUSE_OVER_COLOR); + }) + .on('mouseleave', function (edge) { + mouseOutEdge(edge, div); + var strokeColor = d3.select(this).attr('oldStroke'); + d3.select(this) + .transition() + .duration(300) + .style('stroke', strokeColor); + }); + + g.selectAll('.edgelooplabel') + .data(selfLinks) + .enter() + .append('text') + .attr('x', d => d.source.x + 35) + .attr('y', d => d.source.y + 50) + .attr('class', 'edgelooplabel') + .attr('fill', NODE_LABEL_COLOR) + .attr('font-size', '22px') + .text(d => d.type); + + g.selectAll('line') + .data(links) + .enter().append('line') + .attr('stroke-width', 5) + .attr('x1', d => d.source.x) + .attr('y1', d => d.source.y) + .attr('x2', d => d.target.x) + .attr('y2', d => d.target.y) + .attr('id', (d, i) => `edge${i}`) + .attr('marker-end', 'url(#arrowhead)') + .style('stroke', EDGE_NORMAL_COLOR) + .on('click', edge => edgePropsLoader(edge.source.id, edge.target.id)) + .on('mouseenter', function (edge) { + mouseOverEdge(edge, div); + d3.select(this) + .transition() + .attr('oldStroke', EDGE_NORMAL_COLOR) + .duration(10) + .style('stroke', EDGE_MOUSE_OVER_COLOR); + }) + .on('mouseleave', function (edge) { + mouseOutEdge(edge, div); + var strokeColor = d3.select(this).attr('oldStroke'); + d3.select(this) + .transition() + .duration(300) + .style('stroke', strokeColor); + }); +}; + +var addMarkers = function (g, svg) { + g.append('defs').append('marker') + .attr('id', 'arrowhead') + .attr('viewBox', '-0 -5 10 10') + .attr('refX', '20') + .attr('refY', '0') + .attr('orient', 'auto') + .attr('markerWidth', '4') + .attr('markerHeight', '4') + .attr('xoverflow', 'visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', EDGE_NORMAL_COLOR) + .attr('stroke', EDGE_NORMAL_COLOR); + + var zoomHandler = d3.zoom() + .on('zoom', _ => g.attr('transform', d3.event.transform)); + + zoomHandler(svg); + zoomHandler.translateTo(svg, -7000, -4000); + zoomHandler.scaleTo(svg, 0.08); +}; + +var drawGraph = function (nodes, links, g, simulation, svg, addNodes, edgePropsLoader) { + for (var i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())); + i < n; ++i) { + simulation.tick(); + } + + var div = d3.select('body').append('div') + .attr('class', 'tooltip') + .style('opacity', 0); + + addLinks(links, g, div, edgePropsLoader); + addNodes(nodes, g); + addNodeLabels(nodes, g); + addEdgePaths(links, g); + addEdgeLabels(links, g, div); + addMarkers(g, svg); +}; + +var prepareLinks = function (nodes, links) { + var result = []; + links.forEach(e => { + var sourceNode = nodes.filter(n => n.id === e.source)[0]; + var targetNode = nodes.filter(n => n.id === e.target)[0]; + + result.push({ + source: sourceNode, + target: targetNode, + type: e.type, + tooltipProperties: e.tooltipProperties + }); + }); + + return result; +}; + +var createSimulation = function (nodes, links) { + return d3.forceSimulation(nodes) + .force('charge', d3.forceManyBody().strength(-201)) + .force('link', d3.forceLink(links).distance(1200).strength(1).iterations(400)) + .force('collision', d3.forceCollide().radius(d => 310)) + .force('x', d3.forceX()) + .force('y', d3.forceY()) + .stop(); +}; + +var createSvg = function () { + var svg = d3.select('#graph').append('svg').attr('height', '100%').attr('width', '100%'); + var g = svg.append('g'); + + return { 'g': g, 'svg': svg }; +}; + +var onNodeLabelMouseOut = function () { + d3.select(this) + .transition() + .duration(600) + .attr('font-size', '32px'); +}; + +var onNodeLabelMouseOver = function () { + d3.select(this) + .transition() + .duration(200) + .attr('font-size', '232px'); +}; + +var onNodeMouseOver = function () { + var oldFill = d3.select(this).style('fill'); + d3.select(this) + .transition() + .duration(200) + .attr('r', 31) + .attr('oldFill', oldFill) + .style('fill', NODE_MOUSE_OVER_COLOR); +}; + +var onNodeMouseOut = function (nodeStates) { + var oldFill = d3.select(this).attr('oldFill'); + d3.select(this) + .transition() + .duration(300) + .attr('r', 23) + .style('fill', oldFill); +}; + +class Graph extends React.Component { + onNodeClick (x) { + // on mouse out the node will change color read from 'oldFill' attribute + d3.selectAll('svg').selectAll('g').selectAll('circle') + .filter(c => c.id === x.id) + .attr('oldFill', CLICKED_COLOR); + this.props.nodePropsLoader(x.id); + } + + addNodes (nodes, g) { + g.selectAll('circle') + .data(nodes) + .enter().append('circle') + .attr('cx', d => d.x) + .attr('cy', d => d.y) + .style('fill', n => { + return chooseColor(this.props.nodeStates[n.id]); + }) + .style('stroke', NODE_BORDER_COLOR) + .attr('stroke-width', 3) + .attr('r', 23) + .on('click', this.onNodeClick) + .on('mouseover', onNodeMouseOver) + .on('mouseout', onNodeMouseOut); + } + + reCreateGraph () { + d3.select('#graph').selectAll('*').remove(); + + var nodes = this.props.nodes; + var links = prepareLinks(this.props.nodes, this.props.edges); + var o = createSvg(); + var simulation = createSimulation(nodes, links); + + drawGraph(nodes, links, o.g, simulation, o.svg, this.addNodes, this.props.edgePropsLoader); + } + + constructor (props, context) { + super(props, context); + + this.reCreateGraph = this.reCreateGraph.bind(this); + this.addNodes = this.addNodes.bind(this); + this.onNodeClick = this.onNodeClick.bind(this); + } + + render () { + if (this.props.graphFingerprint !== graphFingerprint) { + this.reCreateGraph(); + graphFingerprint = this.props.graphFingerprint; + } else { + redrawNodeColors(this.props.nodeStates, this.props.selectedEdge); + } + + return <div id="graph"/>; + } +} + +export default Graph; diff --git a/graphgraph-fe/src/graph_hops.css b/graphgraph-fe/src/graph_hops.css new file mode 100644 index 0000000..474b661 --- /dev/null +++ b/graphgraph-fe/src/graph_hops.css @@ -0,0 +1,8 @@ +.hops-input{ + display: flex; + flex-direction: column; +} + +.hops-input-field{ + width: 190px; +} diff --git a/graphgraph-fe/src/graph_hops.js b/graphgraph-fe/src/graph_hops.js new file mode 100644 index 0000000..da98db2 --- /dev/null +++ b/graphgraph-fe/src/graph_hops.js @@ -0,0 +1,79 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +import React from 'react'; +import { Label } from 'react-bootstrap'; +import NumericInput from 'react-numeric-input'; +import './graph_hops.css'; + +var createNumInput = function (label, callback, current) { + return ( + <div> + <Label>{label}</Label> + <NumericInput onChange={callback} min={1} max={500} value={current} className="hops-input-field"/> + </div> + ); +}; + +class GraphHops extends React.Component { + constructor (props) { + super(props); + + this.state = { value: this.props.defaultValue }; + let p = props.parentHops; + let c = props.cousinHops; + let ch = props.childHops; + + this.onChangeParent = (e) => this._onChangeParent(e); + this.onChangeCousin = (e) => this._onChangeCousin(e); + this.onChangeChild = (e) => this._onChangeChild(e); + this.onChange = (hopsName, num) => this._onChange(hopsName, num); + this.state = { parentHops: p, childHops: ch, cousinHops: c }; + } + + _onChange (hopsName, num) { + var s = this.state; + s[hopsName] = num; + this.setState(s); + this.props.updateHops(this.state.parentHops, this.state.cousinHops, this.state.childHops); + } + + _onChangeParent (e) { + this.onChange('parentHops', e); + } + + _onChangeCousin (e) { + this.onChange('cousinHops', e); + } + + _onChangeChild (e) { + this.onChange('childHops', e); + } + + render () { + if (this.props.edgeFilter === 'Edgerules') { + return <div className="hops-input">{createNumInput('edgerule hops', this.onChangeCousin, this.state.cousinHops)}</div>; + } + + return <div className="hops-input">{createNumInput('parent hops', this.onChangeParent, this.state.parentHops)} {createNumInput('child hops', this.onChangeChild, this.state.childHops)}</div>; + } +} + +export default GraphHops; diff --git a/graphgraph-fe/src/GraphInfoMenu.css b/graphgraph-fe/src/graph_info_menu.css index 24a2719..6182210 100644 --- a/graphgraph-fe/src/GraphInfoMenu.css +++ b/graphgraph-fe/src/graph_info_menu.css @@ -6,15 +6,15 @@ } .pagination { - margin: 0 12px 0 20px !important; + margin: 0 12px 0 20px !important; } .fixed-height-container { overflow: scroll; float:top; height: 200px; - width:40%; - padding:3px; + width:40%; + padding:3px; background:white; } @@ -70,4 +70,3 @@ margin-right: 0; margin-left: 0; } - diff --git a/graphgraph-fe/src/graph_info_menu.js b/graphgraph-fe/src/graph_info_menu.js new file mode 100644 index 0000000..66ffbba --- /dev/null +++ b/graphgraph-fe/src/graph_info_menu.js @@ -0,0 +1,81 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +import React from 'react'; +import './graph_info_menu.css'; +import PathBreadcrumb from './path_breadcrumb.js'; +import _ from 'underscore'; +import ReactTable from "react-table"; +import "react-table/react-table.css"; + +class GraphInfoMenu extends React.Component { + render () { + var paths = this.props.paths; + var callback = this.props.pathCallback; + var showPaths = _.isArray(paths) && !_.isEmpty(paths); + var breadcrumbs = _.map( + paths, (path, i) => <PathBreadcrumb key={i} index={i} pathCallback={callback} path={path}/>); + return ( + <div className="node-property-list"> + <div className="fixed-height-container" style={{ display: showPaths ? 'block' : 'none' }}><p className='path-heading'>Paths</p>{breadcrumbs}</div> + <div className="kv-table datatable"> + <ReactTable pageSizeOptions={[4, 25]} data={this.props.nodeProperties} columns={[ + { + Header: "Attribute", + accessor: "propertyName", + minWidth: 40 + }, + { + Header: "Type", + accessor: "type", + minWidth: 70 + }, + { + Header: "Description", + accessor: "description", + minWidth: 260 + }, + { + id: "Key", + Header: "Key", + accessor: p => p.key ? "yes" : "", + minWidth: 20 + }, + { + id: "Index", + Header: "Index", + accessor: p => p.index ? "yes" : "", + minWidth: 20 + }, + { + id: "Required", + Header: "Required", + accessor: p => p.required ? "yes" : "", + minWidth: 20 + } + ]} defaultPageSize={4} className="-striped -highlight" + /> + </div> + </div> + ); + } +} + +export default GraphInfoMenu; diff --git a/graphgraph-fe/src/GraphSettings.css b/graphgraph-fe/src/graph_settings.css index 6fb4550..c30cda9 100644 --- a/graphgraph-fe/src/GraphSettings.css +++ b/graphgraph-fe/src/graph_settings.css @@ -29,7 +29,7 @@ } -.modal-button +.modal-button { padding-top: 20px; margin: 0; diff --git a/graphgraph-fe/src/graph_settings.js b/graphgraph-fe/src/graph_settings.js new file mode 100644 index 0000000..c8f03f1 --- /dev/null +++ b/graphgraph-fe/src/graph_settings.js @@ -0,0 +1,256 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +import React from 'react'; +import _ from 'underscore'; +import { DropdownButton, MenuItem, Label } from 'react-bootstrap'; +import './graph_settings.css'; +import Popup from './popup_settings.js'; +import ValidationModal from './validation_modal.js'; +import DownloadExport from './download_export.js'; +import { validateSchema, pathGraph, basicGraph, schemas, nodeNames } from './requests.js'; + +var emptyState = { + schemaProblems: [], + nodeNames: [], + fromNode: '', + graph: { + nodeNames: [], + edges: [] + }, + showHops: false, + enableDestinationNode: false, + toNode: '', + edgeFilter: 'Edgerules', + hops: { + parents: 1, + cousin: 1, + child: 1 + }, + selectedSchema: '' +}; + +class GraphSettings extends React.Component { + constructor (props, context) { + super(props, context); + this.onChangeStartNode = this.onChangeStartNode.bind(this); + this.onSelectNode = this.onSelectNode.bind(this); + this.selectSchema = this.selectSchema.bind(this); + this.onChangeToNode = this.onChangeToNode.bind(this); + this.loadInitialGraph = this.loadInitialGraph.bind(this); + this.updateHops = this.updateHops.bind(this); + this.changeEdgeFilter = this.changeEdgeFilter.bind(this); + this.graphFingerprint = this.graphFingerprint.bind(this); + this.state = emptyState; + } + + /* this serves as a config 'fingerprint' to know if the d3 visualisation + should be redrawn from scratch or just updated */ + graphFingerprint (schema, from, to, parents, cousin, child, edgeFilter) { + return `${schema}:${from}:${to}:${parents}:${cousin}:${child}:${edgeFilter}`; + } + + loadInitialGraph (startNode, endNode, parentHops, cousinHops, childHops, edgeFilter) { + if (this.state.selectedSchema === '' || startNode === 'none') { + var s = this.state; + s['edgeFilter'] = edgeFilter; + this.setState(s); + return; + } + if (startNode === 'all') { + endNode = 'none'; + } + + let requestUri = endNode === 'none' + ? basicGraph( + this.state.selectedSchema, startNode, parentHops, cousinHops, childHops, edgeFilter) + : pathGraph( + this.state.selectedSchema, startNode, endNode, edgeFilter); + + fetch(requestUri) + .then(response => response.json()) + .then(g => { + let schema = this.state.selectedSchema; + let f = this.graphFingerprint( + schema, startNode, endNode, parentHops, cousinHops, childHops, edgeFilter); + this.props.graphData(g, this.state.selectedSchema, f); + return g; + }) + .then(g => { + var s = this.state; + s['hops']['parents'] = parentHops; + s['hops']['cousin'] = cousinHops; + s['hops']['child'] = childHops; + s['fromNode'] = startNode; + s['toNode'] = endNode; + s['graph'] = g; + s['edgeFilter'] = edgeFilter; + s['showHops'] = endNode === 'none' && startNode !== 'none' && startNode !== 'all'; + s['enableDestinationNode'] = startNode !== 'none' && startNode !== 'all'; + this.setState(s); + + if (startNode !== 'all') { + this.onSelectNode(startNode); + } + }); + } + + selectSchema (schema) { + var s = this.state; + s['selectedSchema'] = schema; + fetch(nodeNames(schema, s['edgeFilter'])) + .then(response => response.json()) + .then(nodeNames => { + s['fromNode'] = s['toNode'] = 'none'; + s['nodeNames'] = nodeNames; + this.setState(s); + }); + fetch(validateSchema(schema)) + .then(response => response.json()) + .then(p => { + s['schemaProblems'] = p.problems; + this.setState(s); + }); + } + + changeEdgeFilter (edgeFilter) { + fetch(nodeNames(this.state.selectedSchema, edgeFilter)) + .then(response => response.json()) + .then(nodeNames => { + let s = this.state; + s['edgeFilter'] = edgeFilter; + s['fromNode'] = s['toNode'] = 'none'; + s['nodeNames'] = nodeNames; + this.setState(s); + }); + this.loadInitialGraph( + this.state.fromNode, + this.state.toNode, + this.state.hops.parents, + this.state.hops.cousin, + this.state.hops.child, + edgeFilter + ); + } + + updateHops (parentHops, cousinHops, childHops) { + this.loadInitialGraph( + this.state.fromNode, + this.state.toNode, + parentHops, + cousinHops, + childHops, + this.state.edgeFilter + ); + } + + onChangeToNode (eventKey) { + this.loadInitialGraph( + this.state.fromNode, + eventKey, + this.state.hops.parents, + this.state.hops.cousin, + this.state.hops.child, + this.state.edgeFilter + ); + } + + onSelectNode (eventKey) { + this.props.nodePropsLoader(eventKey); + } + + onChangeStartNode (eventKey) { + this.loadInitialGraph( + eventKey, + this.state.toNode, + this.state.hops.parents, + this.state.hops.cousin, + this.state.hops.child, + this.state.edgeFilter + ); + } + + componentDidMount () { + fetch(schemas()) + .then(response => response.json()) + .then(schemas => { + let s = this.state; + s['schemas'] = schemas; + this.setState(s); + }); + } + + render () { + var schemas = _.map(this.state.schemas, (x, k) => <MenuItem key={k} eventKey={x}>{x}</MenuItem>); + + var items = _.map(this.state.nodeNames, (x, k) => <MenuItem key={k} eventKey={x.id}>{x.id}</MenuItem>); + let sortedNames = _.sortBy(this.state.graph.nodeNames, 'id'); + var currentNodeNames = _.map(sortedNames, (x, k) => <MenuItem key={k} eventKey={x.id}>{x.id}</MenuItem>); + + var fromItems = items.slice(); + fromItems.unshift(<MenuItem key='divider' divider/>); + fromItems.unshift(<MenuItem key='all' eventKey='all'>all</MenuItem>); + + items.unshift(<MenuItem key='anotherdivider' divider/>); + items.unshift(<MenuItem key='none' eventKey='none'>none</MenuItem>); + + let edgeFilterItems = [ + <MenuItem key='Edgerules' eventKey='Edgerules'>Edgerules</MenuItem>, + <MenuItem key='Parents' eventKey='Parents'>Parent-child (OXM structure)</MenuItem>, + ]; + return ( + <div> + <div className="graph-menu"> + <div className="startendnode-dropdown"> + <div> + <Label>Schemas</Label> + <DropdownButton className="schemas-dropdown" onSelect={this.selectSchema} id="schemas" title={this.state.selectedSchema}>{schemas}</DropdownButton> + </div> + <div className="source-dropdown-div"> + <Label>Source Node</Label> + <DropdownButton className="node-dropdown" onSelect={this.onChangeStartNode} id="namesFrom" title={this.state.fromNode}>{fromItems}</DropdownButton> + </div> + <div> + <Label>Destination Node</Label> + <DropdownButton disabled={!this.state.enableDestinationNode} className="node-dropdown" onSelect={this.onChangeToNode} id="namesTo" title={this.state.toNode}>{items}</DropdownButton> + </div> + <div className="source-dropdown-div"> + <Label>Edge filter</Label> + <DropdownButton className="node-dropdown" onSelect={this.changeEdgeFilter} id="filterEdge" title={this.state.edgeFilter}>{edgeFilterItems}</DropdownButton> + </div> + <div className="source-dropdown-div"> + <Label>Selected Node</Label> + <DropdownButton className="node-dropdown" onSelect={this.onSelectNode} id="selectedNode" title={this.props.selectedNode}>{currentNodeNames}</DropdownButton> + </div> + <Popup isDisabled={!this.state.showHops} edgeFilter={this.state.edgeFilter} parentHops={this.state.hops.parents} childHops={this.state.hops.child} cousinHops={this.state.hops.cousin} updateHops={this.updateHops}/> + <div className="modal-button"> + <ValidationModal schemaProblems={this.state.schemaProblems}/> + </div> + <div className="modal-button"> + <DownloadExport schemaVersion={this.state.selectedSchema}/> + </div> + </div> + </div> + </div> + ); + } +} + +export default GraphSettings; diff --git a/graphgraph-fe/src/GraphSettingsMenu.css b/graphgraph-fe/src/graph_settings_menu.css index 466d07d..718dfa0 100644 --- a/graphgraph-fe/src/GraphSettingsMenu.css +++ b/graphgraph-fe/src/graph_settings_menu.css @@ -1,12 +1,11 @@ - .navbar.navbar-adjust{ -margin-bottom: 0px; + margin-bottom: 0px; } .navbar-adjust .container { -margin-left: 0; + margin-left: 0; } .navbar-adjust .container .navbar-header { -margin-right: 250px; + margin-right: 250px; } diff --git a/graphgraph-fe/src/graph_settings_menu.js b/graphgraph-fe/src/graph_settings_menu.js new file mode 100644 index 0000000..05def48 --- /dev/null +++ b/graphgraph-fe/src/graph_settings_menu.js @@ -0,0 +1,45 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +import React from 'react'; +import GraphSettings from './graph_settings.js'; +import { Navbar, Nav } from 'react-bootstrap'; +import './graph_settings_menu.css'; + +class GraphSettingsMenu extends React.Component { + render () { + return ( + <Navbar className='navbar-adjust'> + <Navbar.Header> + <Navbar.Brand> + <a href="https://gerrit.onap.org/r/gitweb?p=aai/graphgraph.git">GraphGraph</a> + </Navbar.Brand> + </Navbar.Header> + <Nav className="mr-auto"> + <Navbar.Collapse className='mr-sm-2'> + <GraphSettings selectedNode={this.props.selectedNode} graphData={this.props.graphData} nodePropsLoader={this.props.nodePropsLoader}/> + </Navbar.Collapse> + </Nav> + </Navbar> + ); + } +} + +export default GraphSettingsMenu; diff --git a/graphgraph-fe/src/index.css b/graphgraph-fe/src/index.css index cee5f34..cf5eae8 100644 --- a/graphgraph-fe/src/index.css +++ b/graphgraph-fe/src/index.css @@ -1,14 +1,14 @@ body { - margin: 0; - padding: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + margin: 0; + padding: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + monospace; } diff --git a/graphgraph-fe/src/index.js b/graphgraph-fe/src/index.js index bdd3da2..4269967 100644 --- a/graphgraph-fe/src/index.js +++ b/graphgraph-fe/src/index.js @@ -1,13 +1,33 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import './index.css' -import App from './App' -import * as serviceWorker from './serviceWorker' -import 'bootstrap-css-only/css/bootstrap.css' +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ -ReactDOM.render(<App />, document.getElementById('root')) +import React from 'react'; +import ReactDOM from 'react-dom'; +import './index.css'; +import App from './app.js'; +import * as serviceWorker from './service_worker.js'; +import 'bootstrap-css-only/css/bootstrap.css'; + +ReactDOM.render(<App/>, document.getElementById('root')); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: http://bit.ly/CRA-PWA -serviceWorker.unregister() +serviceWorker.unregister(); diff --git a/graphgraph-fe/src/path_breadcrumb.js b/graphgraph-fe/src/path_breadcrumb.js new file mode 100644 index 0000000..6ed9d16 --- /dev/null +++ b/graphgraph-fe/src/path_breadcrumb.js @@ -0,0 +1,46 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +import _ from 'underscore'; +import React from 'react'; +import { Breadcrumb } from 'react-bootstrap'; + +class PathBreadcrumb extends React.Component { + constructor (props, context) { + super(props, context); + this.pathSelected = this.pathSelected.bind(this); + } + + pathSelected (evt) { + evt.preventDefault(); + // the data is only piggyback riding on the "target" property .. not nice but works + this.props.pathCallback(this.props.index, evt.target.getAttribute('target')); + } + + render () { + var path = this.props.path; + var callback = this.pathSelected; + var items = _.map(path, (item, i) => <Breadcrumb.Item key={i} target={item.id} onClick={callback}> {item.id} </Breadcrumb.Item>); + + return <Breadcrumb>{items}</Breadcrumb>; + } +} + +export default PathBreadcrumb; diff --git a/graphgraph-fe/src/PopupSettings.css b/graphgraph-fe/src/popup_settings.css index f80e264..2a548af 100644 --- a/graphgraph-fe/src/PopupSettings.css +++ b/graphgraph-fe/src/popup_settings.css @@ -13,4 +13,3 @@ text-decoration: none; font-size: 25px; } - diff --git a/graphgraph-fe/src/popup_settings.js b/graphgraph-fe/src/popup_settings.js new file mode 100644 index 0000000..2430ab2 --- /dev/null +++ b/graphgraph-fe/src/popup_settings.js @@ -0,0 +1,40 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +import React from 'react'; +import Popup from 'reactjs-popup'; +import './popup_settings.css'; +import GraphHops from './graph_hops.js'; + +class PopupMenu extends React.Component { + render () { + return ( + <Popup trigger={<button className='settings-button' disabled={this.props.isDisabled}>Hops</button>} position="bottom right"> + {close => ( + <div> + <GraphHops edgeFilter={this.props.edgeFilter} parentHops={this.props.parentHops} childHops={this.props.childHops} cousinHops={this.props.cousinHops} updateHops={this.props.updateHops}/> + <button type="button" className="link-button, close" onClick={close}>×</button> + </div> + )}</Popup> + ); + } +} + +export default PopupMenu; diff --git a/graphgraph-fe/src/requests.js b/graphgraph-fe/src/requests.js index 8a86e0c..ae41f78 100644 --- a/graphgraph-fe/src/requests.js +++ b/graphgraph-fe/src/requests.js @@ -1,35 +1,55 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + const host = window.location.hostname; const port = window.location.port; const protocol = window.location.protocol; export function schemas () { - return `${protocol}//${host}:${port}/schemas` -} + return `${protocol}//${host}:${port}/schemas`; +}; export function validateSchema (schema) { - return `${protocol}//${host}:${port}/schemas/${schema}/validation` -} + return `${protocol}//${host}:${port}/schemas/${schema}/validation`; +}; export function exportSchema (schema) { - return `${protocol}//${host}:${port}/schemas/${schema}/xmiexport` -} + return `${protocol}//${host}:${port}/schemas/${schema}/xmiexport`; +}; export function nodeNames (schema, edgeFilter) { - return `${protocol}//${host}:${port}/schemas/${schema}/nodes?edgeFilter=${edgeFilter}` -} + return `${protocol}//${host}:${port}/schemas/${schema}/nodes?edgeFilter=${edgeFilter}`; +}; export function basicGraph (schema, node, parentHops, cousinHops, childHops, edgeFilter) { - return `${protocol}//${host}:${port}/schemas/${schema}/graph/basic?node=${node}&parentHops=${parentHops}&cousinHops=${cousinHops}&childHops=${childHops}&edgeFilter=${edgeFilter}` -} + return `${protocol}//${host}:${port}/schemas/${schema}/graph/basic?node=${node}&parentHops=${parentHops}&cousinHops=${cousinHops}&childHops=${childHops}&edgeFilter=${edgeFilter}`; +}; export function pathGraph (schema, fromNode, toNode, edgeFilter) { - return `${protocol}//${host}:${port}/schemas/${schema}/graph/paths?fromNode=${fromNode}&toNode=${toNode}&edgeFilter=${edgeFilter}` -} + return `${protocol}//${host}:${port}/schemas/${schema}/graph/paths?fromNode=${fromNode}&toNode=${toNode}&edgeFilter=${edgeFilter}`; +}; export function nodeProperty (schema, node) { - return `${protocol}//${host}:${port}/schemas/${schema}/nodes/${node}` -} + return `${protocol}//${host}:${port}/schemas/${schema}/nodes/${node}`; +}; export function edgeProperty (schema, fromNode, toNode) { - return `${protocol}//${host}:${port}/schemas/${schema}/edges?fromNode=${fromNode}&toNode=${toNode}` -} + return `${protocol}//${host}:${port}/schemas/${schema}/edges?fromNode=${fromNode}&toNode=${toNode}`; +}; diff --git a/graphgraph-fe/src/serviceWorker.js b/graphgraph-fe/src/serviceWorker.js deleted file mode 100644 index 5c6ead6..0000000 --- a/graphgraph-fe/src/serviceWorker.js +++ /dev/null @@ -1,135 +0,0 @@ -// This optional code is used to register a service worker. -// register() is not called by default. - -// This lets the app load faster on subsequent visits in production, and gives -// it offline capabilities. However, it also means that developers (and users) -// will only see deployed updates on subsequent visits to a page, after all the -// existing tabs open on the page have been closed, since previously cached -// resources are updated in the background. - -// To learn more about the benefits of this model and instructions on how to -// opt-in, read http://bit.ly/CRA-PWA - -const isLocalhost = Boolean( - window.location.hostname === 'localhost' || - // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || - // 127.0.0.1/8 is considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) -) - -export function register (config) { - if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href) - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebook/create-react-app/issues/2374 - return - } - - window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` - - if (isLocalhost) { - // This is running on localhost. Let's check if a service worker still exists or not. - checkValidServiceWorker(swUrl, config) - - // Add some additional logging to localhost, pointing developers to the - // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit http://bit.ly/CRA-PWA' - ) - }) - } else { - // Is not localhost. Just register service worker - registerValidSW(swUrl, config) - } - }) - } -} - -function registerValidSW (swUrl, config) { - navigator.serviceWorker - .register(swUrl) - .then(registration => { - registration.onupdatefound = () => { - const installingWorker = registration.installing - if (installingWorker == null) { - return - } - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the updated precached content has been fetched, - // but the previous service worker will still serve the older - // content until all client tabs are closed. - console.log( - 'New content is available and will be used when all ' + - 'tabs for this page are closed. See http://bit.ly/CRA-PWA.' - ) - - // Execute callback - if (config && config.onUpdate) { - config.onUpdate(registration) - } - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log('Content is cached for offline use.') - - // Execute callback - if (config && config.onSuccess) { - config.onSuccess(registration) - } - } - } - } - } - }) - .catch(error => { - console.error('Error during service worker registration:', error) - }) -} - -function checkValidServiceWorker (swUrl, config) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl) - .then(response => { - // Ensure service worker exists, and that we really are getting a JS file. - const contentType = response.headers.get('content-type') - if ( - response.status === 404 || - (contentType != null && contentType.indexOf('javascript') === -1) - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { - registration.unregister().then(() => { - window.location.reload() - }) - }) - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl, config) - } - }) - .catch(() => { - console.log( - 'No internet connection found. App is running in offline mode.' - ) - }) -} - -export function unregister () { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.ready.then(registration => { - registration.unregister() - }) - } -} diff --git a/graphgraph-fe/src/service_worker.js b/graphgraph-fe/src/service_worker.js new file mode 100644 index 0000000..0ac740b --- /dev/null +++ b/graphgraph-fe/src/service_worker.js @@ -0,0 +1,146 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +// This optional code is used to register a service worker. +// register() is not called by default. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read http://bit.ly/CRA-PWA + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) +); + +export function register (config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit http://bit.ly/CRA-PWA' + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW (swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log('New content is available and will be used when all ' + + 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker (swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log('No internet connection found. App is running in offline mode.'); + }); +} + +export function unregister () { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +}; diff --git a/graphgraph-fe/src/ValidationModal.css b/graphgraph-fe/src/validation_modal.css index 56b4567..4fe12a9 100644 --- a/graphgraph-fe/src/ValidationModal.css +++ b/graphgraph-fe/src/validation_modal.css @@ -1,7 +1,7 @@ .modal-content { -height: 100%; -width: 100%; + height: 100%; + width: 100%; } .modal-validator @@ -16,9 +16,9 @@ width: 100%; backgroundColor: 'white'; boxShadow: '0 5px 15px rgba(0,0,0,.5)'; padding: 0; -} +} -.modal-backdrop +.modal-backdrop { position: 'fixed'; zIndex: 1040; diff --git a/graphgraph-fe/src/validation_modal.js b/graphgraph-fe/src/validation_modal.js new file mode 100644 index 0000000..1944c36 --- /dev/null +++ b/graphgraph-fe/src/validation_modal.js @@ -0,0 +1,58 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +import _ from 'underscore'; +import React from 'react'; +import './validation_modal.css'; +import { Button, Modal, ListGroup, ListGroupItem } from 'react-bootstrap'; + +class ValidationModal extends React.Component { + constructor(...args) { + super(...args); + this.state = { showModal: false }; + this.close = () => { + this.setState({ showModal: false }); + }; + this.open = () => { + this.setState({ showModal: true }); + }; + } + + renderBackdrop(props) { + return <div {...props} className="modal-backdrop"/>; + } + + render() { + var problems = this.props.schemaProblems; + var items = _.map(problems, (problem, i) => <ListGroupItem key={i}>{problem}</ListGroupItem>); + return ( + <div> + <Button onClick={this.open}>Validate schema</Button> + <Modal onHide={this.close} className="modal-validator" aria-labelledby="modal-label" show={this.state.showModal} renderBackdrop={this.renderBackdrop}> + <div className="modal-list"> + <ListGroup>{items}</ListGroup> + </div> + </Modal> + </div> + ); + } +} + +export default ValidationModal; @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> @@ -46,9 +46,9 @@ </dependencyManagement> <dependencies> <dependency> - <groupId>ch.qos.logback</groupId> - <artifactId>logback-classic</artifactId> -</dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> @@ -186,82 +186,82 @@ </includes> </resource> </resources> - <plugins> - <plugin> - <groupId>com.github.eirslett</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.6</version> - <executions> - <execution> - <id>install node</id> - <goals> - <goal>install-node-and-npm</goal> - </goals> - <phase>generate-resources</phase> - <configuration> - <workingDirectory>${project.basedir}/graphgraph-fe</workingDirectory> - <nodeVersion>v10.10.0</nodeVersion> - </configuration> - </execution> - <execution> - <id>npm install</id> - <goals> - <goal>npm</goal> - </goals> - <phase>generate-resources</phase> - <configuration> - <workingDirectory>${project.basedir}/graphgraph-fe</workingDirectory> - <arguments>install</arguments> - </configuration> - </execution> - <execution> - <id>npm build</id> - <goals> - <goal>npm</goal> - </goals> - <phase>generate-resources</phase> - <configuration> - <workingDirectory>${project.basedir}/graphgraph-fe</workingDirectory> - <arguments>run build</arguments> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <artifactId>maven-resources-plugin</artifactId> - <version>3.1.0</version> - <executions> - <execution> - <id>copy-resources-ui</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory>${project.build.directory}/classes/public</outputDirectory> - <resources> - <resource> - <directory>${project.basedir}/graphgraph-fe/build</directory> - <filtering>false</filtering> - </resource> - </resources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-maven-plugin</artifactId> - <version>2.1.2.RELEASE</version> - <executions> - <execution> - <goals> - <goal>repackage</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> + <plugins> + <plugin> + <groupId>com.github.eirslett</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.6</version> + <executions> + <execution> + <id>install node</id> + <goals> + <goal>install-node-and-npm</goal> + </goals> + <phase>generate-resources</phase> + <configuration> + <workingDirectory>${project.basedir}/graphgraph-fe</workingDirectory> + <nodeVersion>v10.10.0</nodeVersion> + </configuration> + </execution> + <execution> + <id>npm install</id> + <goals> + <goal>npm</goal> + </goals> + <phase>generate-resources</phase> + <configuration> + <workingDirectory>${project.basedir}/graphgraph-fe</workingDirectory> + <arguments>install</arguments> + </configuration> + </execution> + <execution> + <id>npm build</id> + <goals> + <goal>npm</goal> + </goals> + <phase>generate-resources</phase> + <configuration> + <workingDirectory>${project.basedir}/graphgraph-fe</workingDirectory> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>3.1.0</version> + <executions> + <execution> + <id>copy-resources-ui</id> + <phase>prepare-package</phase> + <goals> + <goal>copy-resources</goal> + </goals> + <configuration> + <outputDirectory>${project.build.directory}/classes/public</outputDirectory> + <resources> + <resource> + <directory>${project.basedir}/graphgraph-fe/build</directory> + <filtering>false</filtering> + </resource> + </resources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <version>2.1.2.RELEASE</version> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> </build> <!-- Start of ONAP Specific Repositories --> <repositories> @@ -1,5 +1,5 @@ -#!/bin/sh -cd ./graphgraph-fe/ +#!/bin/sh +cd ./graphgraph-fe/ npm run build cd .. -mvn clean spring-boot:run +mvn clean spring-boot:run diff --git a/src/main/java/org/onap/aai/graphgraph/App.java b/src/main/java/org/onap/aai/graphgraph/App.java index ed1bb9a..1c6bc61 100644 --- a/src/main/java/org/onap/aai/graphgraph/App.java +++ b/src/main/java/org/onap/aai/graphgraph/App.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph; @@ -40,32 +40,35 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication -public class App{ +public class App { + public static EdgeIngestor edgeIngestor; public static Map<String, MoxyLoader> moxyLoaders = new HashMap<>(); // TODO // this should be used properly within Spring as this is a 'static' workaround due // to some initialization issues. By all means feel free to improve and move it to Spring - public static void loadSchemes(ConfigurableApplicationContext context){ + public static void loadSchemes(ConfigurableApplicationContext context) { String version; for (int i = 10; i < 19; i++) { version = "v" + i; - moxyLoaders.put(version, new MoxyLoader(new SchemaVersion(version), (NodeIngestor) context.getBean("nodeIngestor")) ); + moxyLoaders.put(version, new MoxyLoader( + new SchemaVersion(version), (NodeIngestor) context.getBean("nodeIngestor")) + ); } } - public static void main( String[] args ) throws IOException { + public static void main(String[] args) throws IOException { ArgumentParser parser = new ArgumentParser().parseArguments(args); - if (parser.isPrintHelp()){ + if (parser.isPrintHelp()) { parser.printHelp(); return; } - SpringApplication app = new SpringApplication(App.class); + SpringApplication app = new SpringApplication(App.class); - if (parser.isRunLocally()){ + if (parser.isRunLocally()) { copyKeystore(app); } @@ -74,7 +77,7 @@ public class App{ loadSchemes(context); edgeIngestor = (EdgeIngestor) context.getBean("edgeIngestor"); - if (parser.shoudGenerateUml()){ + if (parser.shouldGenerateUrl()) { writeExportedModel(exportModel(parser.getSchemaVersion())); System.exit(0); } @@ -83,9 +86,10 @@ public class App{ private static void copyKeystore(SpringApplication app) throws IOException { Path path = Paths.get("etc/auth/aai_keystore"); if (Files.notExists(path)) { - FileUtils.copyInputStreamToFile(Objects - .requireNonNull(app.getClassLoader().getResourceAsStream("etc/auth/aai_keystore")), - path.toFile()); + FileUtils.copyInputStreamToFile( + Objects.requireNonNull(app.getClassLoader().getResourceAsStream("etc/auth/aai_keystore")), + path.toFile() + ); } } } diff --git a/src/main/java/org/onap/aai/graphgraph/ArgumentParser.java b/src/main/java/org/onap/aai/graphgraph/ArgumentParser.java index 4405723..e156e39 100644 --- a/src/main/java/org/onap/aai/graphgraph/ArgumentParser.java +++ b/src/main/java/org/onap/aai/graphgraph/ArgumentParser.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph; @@ -24,51 +24,50 @@ import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.Option; public class ArgumentParser { - @Option(name = "-g", usage = "generates schema model for Papyrus and exits where XY is the version", metaVar = "vXY") - private String schemaVersion; - - @Option(name = "-d", usage = "connect to dev version of schema-service (use JAR bundled keystore)") - private boolean runLocally; - @Option(name = "-h", usage = "print help and exit") - private boolean printHelp; + CmdLineParser parser; + @Option(name = "-g", usage = "generates schema model for Papyrus and exits where XY is the version", + metaVar = "vXY") + private String schemaVersion; + @Option(name = "-d", usage = "connect to dev version of schema-service (use JAR bundled keystore)") + private boolean runLocally; + @Option(name = "-h", usage = "print help and exit") + private boolean printHelp; - CmdLineParser parser; + public ArgumentParser parseArguments(String[] args) { + parser = new CmdLineParser(this); - public ArgumentParser parseArguments(String [] args){ - parser = new CmdLineParser(this); + try { + parser.parseArgument(args); + } catch (CmdLineException e) { + System.err.println(e.getMessage()); + parser.printUsage(System.err); + } - try { - parser.parseArgument(args); - } catch( CmdLineException e ) { - System.err.println(e.getMessage()); - parser.printUsage(System.err); + return this; } - return this; - } - - public String getSchemaVersion() { - return schemaVersion; - } + public String getSchemaVersion() { + return schemaVersion; + } - public void setSchemaVersion(String schemaVersion) { - this.schemaVersion = schemaVersion; - } + public void setSchemaVersion(String schemaVersion) { + this.schemaVersion = schemaVersion; + } - public boolean shoudGenerateUml(){ - return schemaVersion != null; - } + public boolean shouldGenerateUrl() { + return schemaVersion != null; + } - public boolean isRunLocally() { - return runLocally; - } + public boolean isRunLocally() { + return runLocally; + } - public boolean isPrintHelp() { - return printHelp; - } + public boolean isPrintHelp() { + return printHelp; + } - public void printHelp(){ - parser.printUsage(System.out); - } + public void printHelp() { + parser.printUsage(System.out); + } } diff --git a/src/main/java/org/onap/aai/graphgraph/Config.java b/src/main/java/org/onap/aai/graphgraph/Config.java index 1d4152b..bbc082f 100644 --- a/src/main/java/org/onap/aai/graphgraph/Config.java +++ b/src/main/java/org/onap/aai/graphgraph/Config.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph; @@ -31,9 +31,9 @@ import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = { - "org.onap.aai.config", - "org.onap.aai.setup", - "org.onap.aai.graphgraph" + "org.onap.aai.config", + "org.onap.aai.setup", + "org.onap.aai.graphgraph" }) public class Config { @@ -41,7 +41,7 @@ public class Config { String schemaVersions; @Bean - SchemaRepository createSchemaRepository(){ + SchemaRepository createSchemaRepository() { List<SchemaReader> readers = new LinkedList<>(); for (String s : schemaVersions.split(",")) { readers.add(new BasicSchemaReader(s)); @@ -50,4 +50,3 @@ public class Config { } } - diff --git a/src/main/java/org/onap/aai/graphgraph/CorsFilter.java b/src/main/java/org/onap/aai/graphgraph/CorsFilter.java index 448a2ea..ab97255 100644 --- a/src/main/java/org/onap/aai/graphgraph/CorsFilter.java +++ b/src/main/java/org/onap/aai/graphgraph/CorsFilter.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph; @@ -30,15 +30,17 @@ import org.springframework.web.filter.OncePerRequestFilter; @Component public class CorsFilter extends OncePerRequestFilter { - @Override - protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, - final FilterChain filterChain) throws ServletException, IOException { - response.addHeader("Access-Control-Allow-Origin", "*"); - response.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH, HEAD"); - response.addHeader("Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); - response.addHeader("Access-Control-Expose-Headers", "Access-Control-Allow-Origin, Access-Control-Allow-Credentials"); - response.addHeader("Access-Control-Allow-Credentials", "true"); - response.addIntHeader("Access-Control-Max-Age", 10); - filterChain.doFilter(request, response); - } -}
\ No newline at end of file + @Override + protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, + final FilterChain filterChain) throws ServletException, IOException { + response.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH, HEAD"); + response.addHeader("Access-Control-Allow-Headers", + "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); + response.addHeader("Access-Control-Expose-Headers", + "Access-Control-Allow-Origin, Access-Control-Allow-Credentials"); + response.addHeader("Access-Control-Allow-Credentials", "true"); + response.addIntHeader("Access-Control-Max-Age", 10); + filterChain.doFilter(request, response); + } +} diff --git a/src/main/java/org/onap/aai/graphgraph/ModelExporter.java b/src/main/java/org/onap/aai/graphgraph/ModelExporter.java index 36070db..047bd92 100644 --- a/src/main/java/org/onap/aai/graphgraph/ModelExporter.java +++ b/src/main/java/org/onap/aai/graphgraph/ModelExporter.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph; @@ -54,214 +54,221 @@ import org.onap.aai.setup.SchemaVersion; public class ModelExporter { - private static final String AAIMODEL_UML_FILENAME = "aaimodel.uml"; - private static final String VELOCITY_TEMPLATE_FILENAME = "model_export.vm"; - private static final boolean OXM_ENABLED = false; - private static final String camelCaseRegex = "(?=[A-Z][a-z])"; - private static Map<String, Introspector> allEntities; - private static Multimap<String, EdgeRule> getEdgeRules(String schemaVersion) { - try { - Multimap<String, EdgeRule> allRules = App.edgeIngestor.getAllRules(new SchemaVersion(schemaVersion)); - allEntities = App.moxyLoaders.get(schemaVersion).getAllObjects(); - if (OXM_ENABLED) { - addOxmRelationships(allRules, allEntities); - } - return allRules; - } catch (EdgeRuleNotFoundException e) { - e.printStackTrace(); - } - - return null; - } + private static final String AAIMODEL_UML_FILENAME = "aaimodel.uml"; + private static final String VELOCITY_TEMPLATE_FILENAME = "model_export.vm"; + private static final boolean OXM_ENABLED = false; + private static final String camelCaseRegex = "(?=[A-Z][a-z])"; + private static Map<String, Introspector> allEntities; - private static void addOxmRelationships(Multimap<String, EdgeRule> allRules, - Map<String, Introspector> allEntities) { - for (Entry<String, Introspector> currentParent : allEntities.entrySet()) { - currentParent.getValue().getProperties().stream() - .filter(v -> allEntities.containsKey(v)) - .filter(v -> !currentParent.getKey().equals(v)) - .forEach(v -> { - String key = currentParent.getKey() + "|" + v; - if (!allRules.containsKey(key)) { - allRules.put(key, createEdgeRule(currentParent.getKey(), v)); + private static Multimap<String, EdgeRule> getEdgeRules(String schemaVersion) { + try { + Multimap<String, EdgeRule> allRules = App.edgeIngestor.getAllRules(new SchemaVersion(schemaVersion)); + allEntities = App.moxyLoaders.get(schemaVersion).getAllObjects(); + if (OXM_ENABLED) { + addOxmRelationships(allRules, allEntities); } - }); + return allRules; + } catch (EdgeRuleNotFoundException e) { + e.printStackTrace(); + } + + return null; } - } - private static EdgeRule createEdgeRule(String parent, String child) { - Map<String, String> edgeRuleProps = new HashMap<>(); - edgeRuleProps.put(EdgeField.FROM.toString(), child); - edgeRuleProps.put(EdgeField.TO.toString(), parent); - edgeRuleProps.put(EdgeField.DIRECTION.toString(), Direction.OUT.toString()); //TODO check direction - edgeRuleProps.put(EdgeField.LABEL.toString(), "OXM Parent-Child"); - edgeRuleProps.put(EdgeField.MULTIPLICITY.toString(), MultiplicityRule.MANY2ONE.toString()); - edgeRuleProps.put(EdgeField.DEFAULT.toString(), Boolean.toString(false)); - edgeRuleProps.put(EdgeField.PRIVATE.toString(), Boolean.toString(false)); - edgeRuleProps.put(EdgeField.DELETE_OTHER_V.toString(), DirectionNotation.DIRECTION.toString()); - edgeRuleProps.put(EdgeField.PREVENT_DELETE.toString(), DirectionNotation.DIRECTION.toString()); - edgeRuleProps.put(EdgeField.CONTAINS.toString(), DirectionNotation.DIRECTION.toString()); - edgeRuleProps.put(EdgeField.DESCRIPTION.toString(), "fake edgerule representing parent-child"); - return new EdgeRule(edgeRuleProps); - } + private static void addOxmRelationships( + Multimap<String, EdgeRule> allRules, + Map<String, Introspector> allEntities) { + for (Entry<String, Introspector> currentParent : allEntities.entrySet()) { + currentParent.getValue().getProperties().stream() + .filter(v -> allEntities.containsKey(v)) + .filter(v -> !currentParent.getKey().equals(v)) + .forEach(v -> { + String key = currentParent.getKey() + "|" + v; + if (!allRules.containsKey(key)) { + allRules.put(key, createEdgeRule(currentParent.getKey(), v)); + } + }); + } + } - static String exportModel(String schemaVersion) { - Map<String, Introspector> allObjects = App.moxyLoaders.get(schemaVersion).getAllObjects(); - Template t = initVelocity(); - VelocityContext context = populateVelocityContext(schemaVersion, allObjects); - StringWriter writer = new StringWriter(); - t.merge( context, writer ); - return writer.toString(); - } + private static EdgeRule createEdgeRule(String parent, String child) { + Map<String, String> edgeRuleProps = new HashMap<>(); + edgeRuleProps.put(EdgeField.FROM.toString(), child); + edgeRuleProps.put(EdgeField.TO.toString(), parent); + edgeRuleProps.put(EdgeField.DIRECTION.toString(), Direction.OUT.toString()); //TODO check direction + edgeRuleProps.put(EdgeField.LABEL.toString(), "OXM Parent-Child"); + edgeRuleProps.put(EdgeField.MULTIPLICITY.toString(), MultiplicityRule.MANY2ONE.toString()); + edgeRuleProps.put(EdgeField.DEFAULT.toString(), Boolean.toString(false)); + edgeRuleProps.put(EdgeField.PRIVATE.toString(), Boolean.toString(false)); + edgeRuleProps.put(EdgeField.DELETE_OTHER_V.toString(), DirectionNotation.DIRECTION.toString()); + edgeRuleProps.put(EdgeField.PREVENT_DELETE.toString(), DirectionNotation.DIRECTION.toString()); + edgeRuleProps.put(EdgeField.CONTAINS.toString(), DirectionNotation.DIRECTION.toString()); + edgeRuleProps.put(EdgeField.DESCRIPTION.toString(), "fake edgerule representing parent-child"); + return new EdgeRule(edgeRuleProps); + } - static void writeExportedModel(String result) { - try { - FileWriter fw = new FileWriter(AAIMODEL_UML_FILENAME); - fw.write(result); - fw.close(); - } catch (IOException e) { - e.printStackTrace(); + static String exportModel(String schemaVersion) { + Map<String, Introspector> allObjects = App.moxyLoaders.get(schemaVersion).getAllObjects(); + Template t = initVelocity(); + VelocityContext context = populateVelocityContext(schemaVersion, allObjects); + StringWriter writer = new StringWriter(); + t.merge(context, writer); + return writer.toString(); } - } - private static VelocityContext populateVelocityContext(String schemaVersion, - Map<String, Introspector> allObjects) { - VelocityContext context = new VelocityContext(); - Multimap<String, EdgeRule> edgeRules = getEdgeRules(schemaVersion); - Set<VelocityEntity> entityList = createEntityList(edgeRules); - Set<VelocityAssociation> associationsList = createVelocityAssociations(entityList, edgeRules); - updateEntities(entityList, associationsList, allObjects); - context.put("entityList", entityList); - context.put("associationList", associationsList); - return context; - } + static void writeExportedModel(String result) { + try { + FileWriter fw = new FileWriter(AAIMODEL_UML_FILENAME); + fw.write(result); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } - private static Template initVelocity() { - VelocityEngine ve = new VelocityEngine(); - ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath"); - ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName()); - ve.init(); - return ve.getTemplate(VELOCITY_TEMPLATE_FILENAME); - } + private static VelocityContext populateVelocityContext( + String schemaVersion, + Map<String, Introspector> allObjects) { + VelocityContext context = new VelocityContext(); + Multimap<String, EdgeRule> edgeRules = getEdgeRules(schemaVersion); + Set<VelocityEntity> entityList = createEntityList(edgeRules); + Set<VelocityAssociation> associationsList = createVelocityAssociations(entityList, edgeRules); + updateEntities(entityList, associationsList, allObjects); + context.put("entityList", entityList); + context.put("associationList", associationsList); + return context; + } - private static void updateEntities(Set<VelocityEntity> entityList, - Set<VelocityAssociation> associationsList, - Map<String, Introspector> allObjects) { - entityList.forEach(e -> { - List<VelocityAssociation> associations = associationsList.stream() - .filter(a -> a.getFromEntityId().equals(e.getId())).collect( - Collectors.toList()); - updateNeighbour(entityList, associations); - String description = allEntities.get(e.getName()).getMetadata(ObjectMetadata.DESCRIPTION); - e.setDescription(StringUtil.isBlank(description) ? "no description is available" : description); - }); + private static Template initVelocity() { + VelocityEngine ve = new VelocityEngine(); + ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath"); + ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName()); + ve.init(); + return ve.getTemplate(VELOCITY_TEMPLATE_FILENAME); + } - entityList.forEach(entity -> entity.setProperties(getPropertiesForEntity(allObjects.get(entity.getName()), entityList))); + private static void updateEntities( + Set<VelocityEntity> entityList, + Set<VelocityAssociation> associationsList, + Map<String, Introspector> allObjects) { + entityList.forEach(e -> { + List<VelocityAssociation> associations = associationsList.stream() + .filter(a -> a.getFromEntityId().equals(e.getId())).collect( + Collectors.toList()); + updateNeighbour(entityList, associations); + String description = allEntities.get(e.getName()).getMetadata(ObjectMetadata.DESCRIPTION); + e.setDescription(StringUtil.isBlank(description) ? "no description is available" : description); + }); - } + entityList.forEach( + entity -> entity.setProperties(getPropertiesForEntity(allObjects.get(entity.getName()), entityList))); - private static void updateNeighbour( - Set<VelocityEntity> entityList, List<VelocityAssociation> associations) { - associations.forEach(ass -> { - VelocityEntity velocityEntity = entityList.stream() - .filter(e -> e.getId().equals(ass.getToEntityId())).findFirst().get(); - velocityEntity.addNeighbours(ass); - }); - } + } - private static Set<VelocityEntityProperty> getPropertiesForEntity(Introspector introspector, - Set<VelocityEntity> entityList) { - return introspector.getProperties().stream() - .map(p -> new VelocityEntityProperty( - p, - introspector.getType(p), - findVelocityEntity(introspector.getType(p), entityList))) - .collect( - Collectors.toSet()); - } + private static void updateNeighbour( + Set<VelocityEntity> entityList, List<VelocityAssociation> associations) { + associations.forEach(ass -> { + VelocityEntity velocityEntity = entityList.stream() + .filter(e -> e.getId().equals(ass.getToEntityId())).findFirst().get(); + velocityEntity.addNeighbours(ass); + }); + } - private static Set<VelocityEntity> createEntityList( - Multimap<String, EdgeRule> edgeRules) { - return Objects.requireNonNull(edgeRules).values().stream() - .flatMap(er -> Stream.of(er.getFrom(), er.getTo())) - .map(VelocityEntity::new) - .collect(Collectors.toSet()); - } + private static Set<VelocityEntityProperty> getPropertiesForEntity( + Introspector introspector, + Set<VelocityEntity> entityList) { + return introspector.getProperties().stream() + .map(p -> new VelocityEntityProperty( + p, + introspector.getType(p), + findVelocityEntity(introspector.getType(p), entityList))) + .collect( + Collectors.toSet()); + } - private static Set<VelocityAssociation> createVelocityAssociations(Set<VelocityEntity> entities, - Multimap<String, EdgeRule> edgeRules) { - return edgeRules.values().stream().flatMap(er -> { - VelocityAssociation out = createVelocityAssociation(entities, er.getFrom(), er.getTo(), - er.getLabel(), er.getMultiplicityRule().name(), er.getContains()); - VelocityAssociation in = createVelocityAssociation(entities, er.getTo(), er.getFrom(), - er.getLabel(), er.getMultiplicityRule().name(), er.getContains()); - switch (er.getDirection()) { - case OUT: - return Stream.of(out); - case IN: - return Stream.of(in); - case BOTH: - return Stream.of(out, in); - default: - return null; - } - }).collect(Collectors.toSet()); - } + private static Set<VelocityEntity> createEntityList( + Multimap<String, EdgeRule> edgeRules) { + return Objects.requireNonNull(edgeRules).values().stream() + .flatMap(er -> Stream.of(er.getFrom(), er.getTo())) + .map(VelocityEntity::new) + .collect(Collectors.toSet()); + } - private static VelocityAssociation createVelocityAssociation(Set<VelocityEntity> entities, - String from, String to, String label, String multiplicity, String contains) { - VelocityEntity fromEntity = entities.stream().filter(ent -> ent.getName().equals(from)) - .findFirst().get(); - VelocityEntity toEntity = entities.stream().filter(ent -> ent.getName().equals(to)).findFirst() - .get(); - switch (contains) { - case "IN": - return new VelocityAssociation( - fromEntity, - toEntity, - String.format("%s - %s (%s)", from, to, shortenLabel(label)), - multiplicity, - true); - case "OUT": - return new VelocityAssociation( - toEntity, - fromEntity, - String.format("%s - %s (%s)", to, from, shortenLabel(label)), - multiplicity.equals("ONE2MANY") ? "MANY2ONE" : multiplicity, - true); - default: - return new VelocityAssociation( - fromEntity, - toEntity, - String.format("%s - %s (%s)", from, to, shortenLabel(label)), - multiplicity, - false); + private static Set<VelocityAssociation> createVelocityAssociations( + Set<VelocityEntity> entities, + Multimap<String, EdgeRule> edgeRules) { + return edgeRules.values().stream().flatMap(er -> { + VelocityAssociation out = createVelocityAssociation(entities, er.getFrom(), er.getTo(), + er.getLabel(), er.getMultiplicityRule().name(), er.getContains()); + VelocityAssociation in = createVelocityAssociation(entities, er.getTo(), er.getFrom(), + er.getLabel(), er.getMultiplicityRule().name(), er.getContains()); + switch (er.getDirection()) { + case OUT: + return Stream.of(out); + case IN: + return Stream.of(in); + case BOTH: + return Stream.of(out, in); + default: + return null; + } + }).collect(Collectors.toSet()); } - } - private static String shortenLabel(String label) { - if (label.contains(".")) { - String[] split = label.split("\\."); - return split[split.length - 1]; + private static VelocityAssociation createVelocityAssociation( + Set<VelocityEntity> entities, String from, String to, String label, String multiplicity, String contains) { + VelocityEntity fromEntity = entities.stream() + .filter(ent -> ent.getName().equals(from)).findFirst().get(); + VelocityEntity toEntity = entities.stream() + .filter(ent -> ent.getName().equals(to)).findFirst().get(); + switch (contains) { + case "IN": + return new VelocityAssociation( + fromEntity, + toEntity, + String.format("%s - %s (%s)", from, to, shortenLabel(label)), + multiplicity, + true); + case "OUT": + return new VelocityAssociation( + toEntity, + fromEntity, + String.format("%s - %s (%s)", to, from, shortenLabel(label)), + multiplicity.equals("ONE2MANY") ? "MANY2ONE" : multiplicity, + true); + default: + return new VelocityAssociation( + fromEntity, + toEntity, + String.format("%s - %s (%s)", from, to, shortenLabel(label)), + multiplicity, + false); + } } - return label; - } + private static String shortenLabel(String label) { + if (label.contains(".")) { + String[] split = label.split("\\."); + return split[split.length - 1]; + } - private static VelocityEntity findVelocityEntity(String entityName, Set<VelocityEntity> entities) { - if (entityName.startsWith("java.lang")){ - return null; + return label; } - if ( ! entityName.startsWith("inventory.aai.onap.org")){ - return null; - } + private static VelocityEntity findVelocityEntity(String entityName, Set<VelocityEntity> entities) { + if (entityName.startsWith("java.lang")) { + return null; + } - String[] split = entityName.split("\\."); - String entityNameRoot = split[split.length - 1]; - final Pattern pattern = Pattern.compile(camelCaseRegex); - final Matcher matcher = pattern.matcher(entityNameRoot.substring(1, entityNameRoot.length())); - String finalEntityNameRoot = (entityNameRoot.charAt(0) + matcher.replaceAll("-")).toLowerCase(); - return entities.stream().filter(e -> e.getName().equals(finalEntityNameRoot)).findFirst().orElse(null); - } -}
\ No newline at end of file + if (!entityName.startsWith("inventory.aai.onap.org")) { + return null; + } + + String[] split = entityName.split("\\."); + String entityNameRoot = split[split.length - 1]; + final Pattern pattern = Pattern.compile(camelCaseRegex); + final Matcher matcher = pattern.matcher(entityNameRoot.substring(1)); + String finalEntityNameRoot = (entityNameRoot.charAt(0) + matcher.replaceAll("-")).toLowerCase(); + return entities.stream().filter(e -> e.getName().equals(finalEntityNameRoot)).findFirst().orElse(null); + } +} diff --git a/src/main/java/org/onap/aai/graphgraph/SchemaResource.java b/src/main/java/org/onap/aai/graphgraph/SchemaResource.java index b38ef7b..577e235 100644 --- a/src/main/java/org/onap/aai/graphgraph/SchemaResource.java +++ b/src/main/java/org/onap/aai/graphgraph/SchemaResource.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph; @@ -43,67 +43,66 @@ public class SchemaResource { SchemaRepository repository; @RequestMapping("/schemas") - public List<String> loadSchemaNames() { + public List<String> loadSchemaNames() { return repository.getAllSchemaNames(); } - @RequestMapping("/schemas/{schema}/nodes") - public List<NodeName> loadVertexNames(@PathVariable("schema") String schemaName, - @RequestParam("edgeFilter") String edgeFilter) { + public List<NodeName> loadVertexNames( + @PathVariable("schema") String schemaName, + @RequestParam("edgeFilter") String edgeFilter) { return repository.getSchemaReader(schemaName).getAllVertexNames(edgeFilter); } @RequestMapping("/schemas/{schema}/nodes/{node}") - public List<NodeProperty> loadProperties(@PathVariable("schema") String schemaName, @PathVariable("node") String node) { + public List<NodeProperty> loadProperties( + @PathVariable("schema") String schemaName, + @PathVariable("node") String node) { return repository.getSchemaReader(schemaName).getVertexProperties(node); } - @RequestMapping("/schemas/{schema}/edges") - public List<Property> loadedgeProperties ( + public List<Property> loadEdgeProperties( @PathVariable("schema") String schemaName, @RequestParam("fromNode") String fromNodeName, @RequestParam("toNode") String toNodeName) { return repository.getSchemaReader(schemaName).getEdgeProperties(fromNodeName, toNodeName, "edgerule"); } - @RequestMapping("/schemas/{schema}/graph/basic") - public Graph loadGraph ( + public Graph loadGraph( @PathVariable("schema") String schemaName, @RequestParam("node") String initialNodeName, @RequestParam("parentHops") Integer parentHops, @RequestParam("cousinHops") Integer cousinHops, @RequestParam("childHops") Integer childHops, - @RequestParam("edgeFilter") String edgeFilter) - { - Graph graph = repository.getSchemaReader(schemaName).getGraph(initialNodeName, parentHops, cousinHops, childHops, edgeFilter); - graph.setPaths(Collections.emptyList()); - return graph; + @RequestParam("edgeFilter") String edgeFilter) { + Graph graph = repository.getSchemaReader(schemaName) + .getGraph(initialNodeName, parentHops, cousinHops, childHops, edgeFilter); + graph.setPaths(Collections.emptyList()); + return graph; } - @RequestMapping("/schemas/{schema}/graph/paths") - public Graph loadGraphWithPaths ( + public Graph loadGraphWithPaths( @PathVariable("schema") String schemaName, @RequestParam("fromNode") String fromNode, @RequestParam("toNode") String toNode, - @RequestParam("edgeFilter") String edgeFilter) - { + @RequestParam("edgeFilter") String edgeFilter) { return repository.getSchemaReader(schemaName).getGraph(fromNode, toNode, edgeFilter); } @RequestMapping("/schemas/{schema}/validation") - public ValidationProblems validateSchema ( @PathVariable("schema") String schemaName) { + public ValidationProblems validateSchema( + @PathVariable("schema") String schemaName) { return new SchemaValidator().validate(schemaName); } @RequestMapping(value = "/schemas/{schema}/xmiexport", produces = MediaType.TEXT_XML_VALUE) @ResponseBody - public String exportSchema ( @PathVariable("schema") String schemaName) { + public String exportSchema( + @PathVariable("schema") String schemaName) { return ModelExporter.exportModel(schemaName); } } - diff --git a/src/main/java/org/onap/aai/graphgraph/SchemaValidator.java b/src/main/java/org/onap/aai/graphgraph/SchemaValidator.java index aa260aa..076c31e 100644 --- a/src/main/java/org/onap/aai/graphgraph/SchemaValidator.java +++ b/src/main/java/org/onap/aai/graphgraph/SchemaValidator.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph; @@ -32,70 +32,74 @@ import org.onap.aai.graphgraph.dto.ValidationProblems; import org.onap.aai.graphgraph.reader.BasicSchemaReader; public class SchemaValidator { - private Graph edgerules; - private Graph oxm; - public ValidationProblems validate(String schemaVersion) { - ValidationProblems validationProblems = new ValidationProblems(); - BasicSchemaReader schema = new BasicSchemaReader(schemaVersion); - oxm = schema.getGraph("all", 0, 0, 0, "Parents"); - edgerules = schema.getGraph("all", 0, 0, 0, "Edgerules"); - checkIfDanglingEdgerules(validationProblems); - checkIfObsoleteOxm(validationProblems); - schema.getSchemaErrors().forEach(validationProblems::addProblem); - return validationProblems; - } + private Graph edgerules; + private Graph oxm; + + public ValidationProblems validate(String schemaVersion) { + ValidationProblems validationProblems = new ValidationProblems(); + BasicSchemaReader schema = new BasicSchemaReader(schemaVersion); + oxm = schema.getGraph("all", 0, 0, 0, "Parents"); + edgerules = schema.getGraph("all", 0, 0, 0, "Edgerules"); + + checkIfDanglingEdgerules(validationProblems); + checkIfObsoleteOxm(validationProblems); + schema.getSchemaErrors().forEach(validationProblems::addProblem); + return validationProblems; + } - /** - * computes nodes connected to relationship-list but not used in edgerules - * @param validationProblems - */ - private void checkIfObsoleteOxm(ValidationProblems validationProblems) { - Set<String> relationshipListConnected = getAllNodesConnectedToRelationshipList(); - Set<String> nodesInEdgerules = getEdgerulePairs().stream().flatMap(p -> Stream.of(p._1, p._2)) - .collect(Collectors.toSet()); - relationshipListConnected.removeAll(nodesInEdgerules); - relationshipListConnected.forEach(n -> - validationProblems.addProblem(String.format("%s is associated with relationship-list in OXM but not present in edgerules", n))); - } + /** + * computes nodes connected to relationship-list but not used in edgerules + * @param validationProblems + */ + private void checkIfObsoleteOxm(ValidationProblems validationProblems) { + Set<String> relationshipListConnected = getAllNodesConnectedToRelationshipList(); + Set<String> nodesInEdgerules = getEdgerulePairs().stream() + .flatMap(p -> Stream.of(p._1, p._2)) + .collect(Collectors.toSet()); + relationshipListConnected.removeAll(nodesInEdgerules); + relationshipListConnected.forEach(n -> validationProblems.addProblem( + String.format("%s is associated with relationship-list in OXM but not present in edgerules", + n))); + } - private Set<Tuple2<String, String>> getEdgerulePairs() { - return edgerules.getEdges().stream() - .map(e -> Tuple.of(e.getSource(), e.getTarget())).collect( - Collectors.toSet()); - } + private Set<Tuple2<String, String>> getEdgerulePairs() { + return edgerules.getEdges().stream() + .map(e -> Tuple.of(e.getSource(), e.getTarget())) + .collect(Collectors.toSet()); + } - /** - * computes edgerules which don't have the necessary connection to relationship-list in OXM - * @param validationProblems - */ - private void checkIfDanglingEdgerules( - ValidationProblems validationProblems) { - Set<Tuple2<String, String>> edgerulePairs = getEdgerulePairs(); - edgerulePairs.removeAll(getOxmPairs()); - edgerulePairs.forEach( erp -> - validationProblems.addProblem(String.format("%s and %s are associated in edgerules but not in OXM (via relationship-list)", erp._1, erp._2))); - } + /** + * computes edgerules which don't have the necessary connection to relationship-list in OXM + * @param validationProblems + */ + private void checkIfDanglingEdgerules(ValidationProblems validationProblems) { + Set<Tuple2<String, String>> edgerulePairs = getEdgerulePairs(); + edgerulePairs.removeAll(getOxmPairs()); + edgerulePairs.forEach(erp -> validationProblems.addProblem( + String.format("%s and %s are associated in edgerules but not in OXM (via relationship-list)", + erp._1, erp._2))); + } - private Set<Tuple2<String, String>> getOxmPairs() { - Set<Tuple2<String, String>> pairs = new HashSet<>(); - Set<String> inRelationshipList = getAllNodesConnectedToRelationshipList(); + private Set<Tuple2<String, String>> getOxmPairs() { + Set<Tuple2<String, String>> pairs = new HashSet<>(); + Set<String> inRelationshipList = getAllNodesConnectedToRelationshipList(); - inRelationshipList.forEach(edge1 -> - inRelationshipList.forEach(edge2 -> { - pairs.add(Tuple.of(edge1, edge2)); - })); - return pairs; - } + inRelationshipList.forEach(edge1 -> inRelationshipList + .forEach(edge2 -> pairs.add(Tuple.of(edge1, edge2)))); + return pairs; + } - private Set<String> getAllNodesConnectedToRelationshipList() { - List<Edge> edges = oxm.getEdges(); - Set<String> inRelationshipList = edges.stream().filter(e -> e.getSource().equals("relationship-list")).map( - Edge::getTarget).collect( - Collectors.toSet()); - inRelationshipList.addAll(edges.stream().filter(e -> e.getTarget().equals("relationship-list")) - .map(Edge::getSource).collect( - Collectors.toSet())); - return inRelationshipList; - } + private Set<String> getAllNodesConnectedToRelationshipList() { + List<Edge> edges = oxm.getEdges(); + Set<String> inRelationshipList = edges.stream() + .filter(e -> e.getSource().equals("relationship-list")) + .map(Edge::getTarget) + .collect(Collectors.toSet()); + inRelationshipList.addAll(edges.stream() + .filter(e -> e.getTarget().equals("relationship-list")) + .map(Edge::getSource) + .collect(Collectors.toSet())); + return inRelationshipList; + } } diff --git a/src/main/java/org/onap/aai/graphgraph/dto/Edge.java b/src/main/java/org/onap/aai/graphgraph/dto/Edge.java index 5154e5b..c3ba8df 100644 --- a/src/main/java/org/onap/aai/graphgraph/dto/Edge.java +++ b/src/main/java/org/onap/aai/graphgraph/dto/Edge.java @@ -1,26 +1,25 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.dto; import java.util.Arrays; -import java.util.Collections; import java.util.List; public class Edge { @@ -49,14 +48,14 @@ public class Edge { return source; } - public List<NodeName> getNodeNames() { - return Arrays.asList(new NodeName(source), new NodeName(target)); - } - public void setSource(String source) { this.source = source; } + public List<NodeName> getNodeNames() { + return Arrays.asList(new NodeName(source), new NodeName(target)); + } + public String getTarget() { return target; } diff --git a/src/main/java/org/onap/aai/graphgraph/dto/Graph.java b/src/main/java/org/onap/aai/graphgraph/dto/Graph.java index 83e5977..7d5f957 100644 --- a/src/main/java/org/onap/aai/graphgraph/dto/Graph.java +++ b/src/main/java/org/onap/aai/graphgraph/dto/Graph.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.dto; @@ -23,12 +23,15 @@ import java.util.Collections; import java.util.List; public class Graph { + private List<NodeName> nodeNames; private List<Edge> edges; private List<List<NodeName>> paths; private List<NodeProperty> startNodeProperties; - public Graph(List<NodeName> nodeNames, List<Edge> edges, List<List<NodeName>> pathsList, List<NodeProperty> startNodeProperties) { + public Graph( + List<NodeName> nodeNames, List<Edge> edges, + List<List<NodeName>> pathsList, List<NodeProperty> startNodeProperties) { this.nodeNames = nodeNames; this.edges = edges; this.paths = pathsList; @@ -36,8 +39,8 @@ public class Graph { } public static Graph emptyGraph() { - return new Graph(Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), - Collections.emptyList()); + return new Graph( + Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); } public List<NodeProperty> getStartNodeProperties() { diff --git a/src/main/java/org/onap/aai/graphgraph/dto/NodeName.java b/src/main/java/org/onap/aai/graphgraph/dto/NodeName.java index 992609c..013f1aa 100644 --- a/src/main/java/org/onap/aai/graphgraph/dto/NodeName.java +++ b/src/main/java/org/onap/aai/graphgraph/dto/NodeName.java @@ -1,25 +1,26 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.dto; public class NodeName { + private String id; public NodeName(String name) { @@ -56,8 +57,6 @@ public class NodeName { @Override public String toString() { - return "NodeName{" + - "id='" + id + '\'' + - '}'; + return "NodeName{" + "id='" + id + '\'' + '}'; } } diff --git a/src/main/java/org/onap/aai/graphgraph/dto/NodeProperty.java b/src/main/java/org/onap/aai/graphgraph/dto/NodeProperty.java index 93b04cf..0daa9f4 100644 --- a/src/main/java/org/onap/aai/graphgraph/dto/NodeProperty.java +++ b/src/main/java/org/onap/aai/graphgraph/dto/NodeProperty.java @@ -1,33 +1,35 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.dto; public class NodeProperty extends Property { + private String description; private String type; private boolean key; private boolean index; private boolean required; - public NodeProperty(String propertyName, - String description, String type, boolean key, boolean index, boolean required) { + public NodeProperty( + String propertyName, String description, String type, + boolean key, boolean index, boolean required) { super(propertyName, ""); this.description = description; this.type = type; @@ -92,7 +94,7 @@ public class NodeProperty extends Property { @Override public int hashCode() { - return getPropertyName().hashCode(); + return getPropertyName().hashCode(); } @Override diff --git a/src/main/java/org/onap/aai/graphgraph/dto/Property.java b/src/main/java/org/onap/aai/graphgraph/dto/Property.java index add7dc1..a08b0ec 100644 --- a/src/main/java/org/onap/aai/graphgraph/dto/Property.java +++ b/src/main/java/org/onap/aai/graphgraph/dto/Property.java @@ -1,25 +1,26 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.dto; -public class Property implements Comparable<Property>{ +public class Property implements Comparable<Property> { + private String propertyName; private String propertyValue; @@ -59,7 +60,6 @@ public class Property implements Comparable<Property>{ return false; } return propertyValue.equals(property.propertyValue); - } @Override @@ -71,8 +71,8 @@ public class Property implements Comparable<Property>{ @Override public int compareTo(Property o) { - if (o.getPropertyName().equals(getPropertyName()) && o.getPropertyValue() != null - && getPropertyValue() != null) { + if (o.getPropertyName().equals(getPropertyName()) + && o.getPropertyValue() != null && getPropertyValue() != null) { return getPropertyValue().compareTo(o.getPropertyValue()); } diff --git a/src/main/java/org/onap/aai/graphgraph/dto/ValidationProblems.java b/src/main/java/org/onap/aai/graphgraph/dto/ValidationProblems.java index 9b031d0..976058d 100644 --- a/src/main/java/org/onap/aai/graphgraph/dto/ValidationProblems.java +++ b/src/main/java/org/onap/aai/graphgraph/dto/ValidationProblems.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.dto; @@ -23,17 +23,18 @@ import java.util.LinkedList; import java.util.List; public class ValidationProblems { + private List<String> problems = new LinkedList<>(); - public List<String> getProblems() { - return problems; - } + public List<String> getProblems() { + return problems; + } - public void setProblems(List<String> problems) { - this.problems = problems; - } + public void setProblems(List<String> problems) { + this.problems = problems; + } - public void addProblem(String problem) { - problems.add(problem); - } + public void addProblem(String problem) { + problems.add(problem); + } } diff --git a/src/main/java/org/onap/aai/graphgraph/reader/BasicSchemaReader.java b/src/main/java/org/onap/aai/graphgraph/reader/BasicSchemaReader.java index b2d766b..7f903f7 100644 --- a/src/main/java/org/onap/aai/graphgraph/reader/BasicSchemaReader.java +++ b/src/main/java/org/onap/aai/graphgraph/reader/BasicSchemaReader.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.reader; @@ -50,357 +50,356 @@ import org.onap.aai.setup.SchemaVersion; public class BasicSchemaReader implements SchemaReader { - private Map<String, Introspector> allEntities; - private Graph<String, MetadataEdge> graph = new DefaultDirectedGraph<>(MetadataEdge.class); - private EdgeIngestor edgeIngestor; - private String version; - private List<String> schemaErrors = new LinkedList<>(); + private Map<String, Introspector> allEntities; + private Graph<String, MetadataEdge> graph = new DefaultDirectedGraph<>(MetadataEdge.class); + private EdgeIngestor edgeIngestor; + private String version; + private List<String> schemaErrors = new LinkedList<>(); - public List<String> getSchemaErrors() { - return schemaErrors; - } + public BasicSchemaReader(String version) { + this.version = version; + } - public BasicSchemaReader(String version) { - this.version = version; - } + public List<String> getSchemaErrors() { + return schemaErrors; + } - private void init() { - if (allEntities != null) { - return; + private void init() { + if (allEntities != null) { + return; + } + allEntities = App.moxyLoaders.get(getSchemaName()).getAllObjects(); + edgeIngestor = App.edgeIngestor; + graph = createGraph(true, true); } - allEntities = App.moxyLoaders.get(getSchemaName()).getAllObjects(); - edgeIngestor = App.edgeIngestor; - graph = createGraph(true, true); - } - - private Graph<String, MetadataEdge> createGraph(boolean withParentChild, boolean withEdgeRules) { - Graph<String, MetadataEdge> graph = new DefaultDirectedGraph<>(MetadataEdge.class); - for (Entry<String, Introspector> currentParent : allEntities.entrySet()) { - graph.addVertex(currentParent.getKey()); - currentParent.getValue().getProperties().stream() - .filter(v -> allEntities.containsKey(v)) - .filter(v -> !currentParent.getKey().equals(v)) - .forEach(v -> { - graph.addVertex(v); - if (withParentChild) { - addParentChildEdge(currentParent.getKey(), v, graph); - } - }); + + private Graph<String, MetadataEdge> createGraph(boolean withParentChild, boolean withEdgeRules) { + Graph<String, MetadataEdge> graph = new DefaultDirectedGraph<>(MetadataEdge.class); + for (Entry<String, Introspector> currentParent : allEntities.entrySet()) { + graph.addVertex(currentParent.getKey()); + currentParent.getValue().getProperties().stream() + .filter(v -> allEntities.containsKey(v)) + .filter(v -> !currentParent.getKey().equals(v)) + .forEach(v -> { + graph.addVertex(v); + if (withParentChild) { + addParentChildEdge(currentParent.getKey(), v, graph); + } + }); + } + + if (!withEdgeRules) { + return graph; + } + + Multimap<String, EdgeRule> allRules = null; + try { + allRules = edgeIngestor.getAllRules(new SchemaVersion(getSchemaName())); + } catch (EdgeRuleNotFoundException e) { + //TODO fix + } + + allRules.asMap().values().stream() + .flatMap(e -> e.stream()) + .forEach(e -> { + switch (e.getDirection()) { + case OUT: + addEdgerule(e.getFrom(), e.getTo(), e.getLabel(), graph); + break; + case IN: + addEdgerule(e.getTo(), e.getFrom(), e.getLabel(), graph); + break; + case BOTH: + addEdgerule(e.getFrom(), e.getTo(), e.getLabel(), graph); + addEdgerule(e.getTo(), e.getFrom(), e.getLabel(), graph); + break; + } + }); + + return graph; + } + + private void addEdgerule(String parent, String child, String label, Graph<String, MetadataEdge> graph) { + //shortening labels, long edge names are unreadable in the UI + if (label.contains(".")) { + String[] split = label.split("\\."); + label = split[split.length - 1]; + } + checkVertexExist(graph, parent); + checkVertexExist(graph, child); + + graph.addEdge(child, parent, + new MetadataEdge(EdgeType.EDGE_RULE.getTypeName(), child, parent, label)); + } + + private void checkVertexExist(Graph<String, MetadataEdge> graph, String vertex) { + if (!graph.vertexSet().contains(vertex)) { + graph.addVertex(vertex); + schemaErrors.add(String.format("Schema is inconsistent, missing node %s", vertex)); + } + } + + private void addParentChildEdge(String parent, String child, Graph<String, MetadataEdge> graph) { + graph.addEdge(parent, child, + new MetadataEdge(EdgeType.PARENT.getTypeName(), parent, child, EdgeType.PARENT.getTypeName())); + graph.addEdge(child, parent, + new MetadataEdge(EdgeType.CHILD.getTypeName(), child, parent, EdgeType.CHILD.getTypeName())); + } + + @Override + public String getSchemaName() { + return version; + } + + @Override + public List<NodeName> getAllVertexNames(String edgeFilter) { + init(); + + return createGraph( + isParentChildFilter(edgeFilter), + isEdgeRulesFilter(edgeFilter)).edgeSet().stream() + .flatMap(e -> Arrays.asList(e.getSource(), e.getTarget()).stream()) + .sorted().distinct() + .map(NodeName::new) + .collect(Collectors.toList()); } - if (!withEdgeRules) { - return graph; + @Override + public List<NodeProperty> getVertexProperties(String nodeName) { + init(); + + if (!allEntities.containsKey(nodeName)) { + return Collections.emptyList(); + } + Introspector introspector = allEntities.get(nodeName); + List<String> properties = introspector.getProperties().stream().sorted().collect(Collectors.toList()); + + return properties.stream() + .map(p -> new NodeProperty( + p, + introspector.getPropertyMetadata(p).getOrDefault( + PropertyMetadata.DESCRIPTION, "no description available"), + introspector.getType(p), + introspector.getAllKeys().contains(p), + introspector.getIndexedProperties().contains(p), + introspector.getRequiredProperties().contains(p))) + .collect(Collectors.toList()); + } + + @Override + public List<Property> getEdgeProperties(String fromNode, String toNode, String type) { + init(); + if (type.equals(EdgeType.EDGE_RULE.getTypeName())) { + try { + List<EdgeRule> rules = edgeIngestor.getAllRules(new SchemaVersion(getSchemaName())) + .asMap().values().stream() + .flatMap(Collection::stream) + .filter(identifyEdgeRule(fromNode, toNode)) + .collect(Collectors.toList()); + + Optional<List<Property>> properties = rules.stream().map(this::edgeRuleProperties).findFirst(); + return properties.orElse(Collections.emptyList()); + + } catch (EdgeRuleNotFoundException e) { + //TODO fix + } + } + return Collections.emptyList(); } - Multimap<String, EdgeRule> allRules = null; - try { - allRules = edgeIngestor.getAllRules(new SchemaVersion(getSchemaName())); - } catch (EdgeRuleNotFoundException e) { - //TODO fix + private Predicate<EdgeRule> identifyEdgeRule(String fromNode, String toNode) { + return e -> { + switch (e.getDirection()) { + case OUT: + return e.getFrom().equals(fromNode) && e.getTo().equals(toNode); + case IN: + return e.getFrom().equals(toNode) && e.getTo().equals(fromNode); + case BOTH: + return e.getFrom().equals(toNode) && e.getTo().equals(fromNode) + || e.getFrom().equals(fromNode) && e.getTo().equals(toNode); + default: + return false; + } + }; } - allRules.asMap().values().stream().flatMap(e -> e.stream()).forEach(e -> { - switch (e.getDirection()) { - case OUT: - addEdgerule(e.getFrom(), e.getTo(), e.getLabel(), graph); - break; - case IN: - addEdgerule(e.getTo(), e.getFrom(), e.getLabel(), graph); - break; - case BOTH: - addEdgerule(e.getFrom(), e.getTo(), e.getLabel(), graph); - addEdgerule(e.getTo(), e.getFrom(), e.getLabel(), graph); - break; - } - }); - - return graph; - } - - private void addEdgerule(String parent, String child, String label, - Graph<String, MetadataEdge> graph) { - //shortening labels, long edge names are unreadable in the UI - if (label.contains(".")) { - String[] split = label.split("\\."); - label = split[split.length - 1]; + private List<Property> edgeRuleProperties(EdgeRule r) { + List<Property> ps = new LinkedList<>(); + ps.add(new Property("Multiplicity", r.getMultiplicityRule().name())); + ps.add(new Property("Is default edge", String.valueOf(r.isDefault()))); + ps.add(new Property("Description", r.getDescription())); + ps.add(new Property("Is private edge", String.valueOf(r.isPrivateEdge()))); + ps.add(new Property("Contains", r.getContains())); + ps.add(new Property("Prevent delete", r.getPreventDelete())); + ps.add(new Property("Label", r.getLabel())); + ps.add(new Property("Delete other v", r.getDeleteOtherV())); + return ps; } - checkVertexExist(graph, parent); - checkVertexExist(graph, child); - graph.addEdge(child, parent, - new MetadataEdge(EdgeType.EDGE_RULE.getTypeName(), child, parent, label)); - } + @Override + public org.onap.aai.graphgraph.dto.Graph getGraph( + String initialNode, int parentHops, int cousinHops, int childHops, String edgeFilter) { + init(); + + Optional<String> anyVertex = graph.vertexSet().stream().findFirst(); + if (!anyVertex.isPresent()) { + return org.onap.aai.graphgraph.dto.Graph.emptyGraph(); + } + Set<Edge> edges = computeAllEdges( + anyVertex.get(), isParentChildFilter(edgeFilter), isEdgeRulesFilter(edgeFilter)); + + if (!"all".equals(initialNode)) { + Set<String> subGraphVertices = computeNodes(initialNode, parentHops, EdgeType.CHILD.getTypeName()); + subGraphVertices.addAll(computeNodes(initialNode, childHops, EdgeType.PARENT.getTypeName())); + subGraphVertices.addAll(computeNodes(initialNode, cousinHops, EdgeType.EDGE_RULE.getTypeName())); + edges = filterEdges(edges, subGraphVertices); + } - private void checkVertexExist(Graph<String, MetadataEdge> graph, String vertex) { - if (! graph.vertexSet().contains(vertex)) { - graph.addVertex(vertex); - schemaErrors.add(String.format("Schema is inconsistent, missing node %s", vertex)); + return new org.onap.aai.graphgraph.dto.Graph(new LinkedList<>(computeNodeNames(edges)), + new LinkedList<>(edges), Collections.emptyList(), getVertexProperties(initialNode)); } - } - - private void addParentChildEdge(String parent, String child, Graph<String, MetadataEdge> graph) { - graph.addEdge(parent, child, - new MetadataEdge(EdgeType.PARENT.getTypeName(), parent, child, EdgeType.PARENT.getTypeName())); - graph.addEdge(child, parent, - new MetadataEdge(EdgeType.CHILD.getTypeName(), child, parent, EdgeType.CHILD.getTypeName())); - } - - @Override - public String getSchemaName() { - return version; - } - - @Override - public List<NodeName> getAllVertexNames(String edgeFilter) { - init(); - - return createGraph(isParentChildFilter(edgeFilter), isEdgeRulesFilter(edgeFilter)) - .edgeSet().stream().flatMap(e -> Arrays.asList(e.getSource(), e.getTarget()).stream()) - .sorted() - .distinct() - .map(NodeName::new).collect( - Collectors.toList()); - } - - @Override - public List<NodeProperty> getVertexProperties(String nodeName) { - init(); - - if (!allEntities.containsKey(nodeName)) { - return Collections.emptyList(); + + private boolean isParentChildFilter(String edgeFilter) { + return "Parents".equals(edgeFilter); } - Introspector introspector = allEntities.get(nodeName); - List<String> properties = introspector.getProperties().stream() - .sorted() - .collect(Collectors.toList()); - - List<NodeProperty> result = properties.stream().map( p -> - new NodeProperty( - p, - introspector.getPropertyMetadata(p) - .getOrDefault(PropertyMetadata.DESCRIPTION,"no description available"), - introspector.getType(p), - introspector.getAllKeys().contains(p), - introspector.getIndexedProperties().contains(p), - introspector.getRequiredProperties().contains(p)) - ).collect(Collectors.toList()); - - return result; - } - - @Override - public List<Property> getEdgeProperties(String fromNode, String toNode, String type) { - init(); - if (type.equals(EdgeType.EDGE_RULE.getTypeName())) { - try { - List<EdgeRule> rules = edgeIngestor.getAllRules(new SchemaVersion(getSchemaName())) - .asMap() - .values() - .stream() - .flatMap(Collection::stream) - .filter(identifyEdgeRule(fromNode, toNode)) - .collect(Collectors.toList()); - - Optional<List<Property>> properties = rules.stream().map(this::edgeRuleProperties) - .findFirst(); - return properties.orElse(Collections.emptyList()); - - } catch (EdgeRuleNotFoundException e) { - //TODO fix - } + private boolean isEdgeRulesFilter(String edgeFilter) { + return "Edgerules".equals(edgeFilter); } - return Collections.emptyList(); - } - - private Predicate<EdgeRule> identifyEdgeRule(String fromNode, String toNode) { - return e -> { - switch (e.getDirection()) { - case OUT: - return e.getFrom().equals(fromNode) && e.getTo().equals(toNode); - case IN: - return e.getFrom().equals(toNode) && e.getTo().equals(fromNode); - case BOTH: - return e.getFrom().equals(toNode) && e.getTo().equals(fromNode) - || e.getFrom().equals(fromNode) && e.getTo().equals(toNode); - default: - return false; - } - }; - } - - private List<Property> edgeRuleProperties(EdgeRule r) { - List<Property> ps = new LinkedList<>(); - ps.add(new Property("Multiplicity", r.getMultiplicityRule().name())); - ps.add(new Property("Is default edge", String.valueOf(r.isDefault()))); - ps.add(new Property("Description", r.getDescription())); - ps.add(new Property("Is private edge", String.valueOf(r.isPrivateEdge()))); - ps.add(new Property("Contains", r.getContains())); - ps.add(new Property("Prevent delete", r.getPreventDelete())); - ps.add(new Property("Label", r.getLabel())); - ps.add(new Property("Delete other v", r.getDeleteOtherV())); - return ps; - } - - @Override - public org.onap.aai.graphgraph.dto.Graph getGraph(String initialNode, int parentHops, - int cousinHops, int childHops, String edgeFilter) { - init(); - - Optional<String> anyVertex = graph.vertexSet().stream().findFirst(); - if (!anyVertex.isPresent()) { - return org.onap.aai.graphgraph.dto.Graph.emptyGraph(); + + private Set<Edge> filterEdgesStrict(Set<Edge> edges, Set<String> subGraphVertices) { + return edges.stream() + .filter(e -> subGraphVertices.contains(e.getSource()) && subGraphVertices.contains(e.getTarget())) + .collect(Collectors.toSet()); } - Set<Edge> edges = computeAllEdges(anyVertex.get(), isParentChildFilter(edgeFilter), - isEdgeRulesFilter(edgeFilter)); - - if (!"all".equals(initialNode)) { - Set<String> subGraphVertices = computeNodes(initialNode, parentHops, EdgeType.CHILD.getTypeName()); - subGraphVertices.addAll(computeNodes(initialNode, childHops, EdgeType.PARENT.getTypeName())); - subGraphVertices.addAll(computeNodes(initialNode, cousinHops, EdgeType.EDGE_RULE.getTypeName())); - edges = filterEdges(edges, subGraphVertices); + + private Set<Edge> filterEdges(Set<Edge> edges, Set<String> subGraphVertices) { + return edges.stream() + .filter(e -> subGraphVertices.contains(e.getSource()) || subGraphVertices.contains(e.getTarget())) + .filter(e -> !e.getType().equals(EdgeType.EDGE_RULE.getTypeName())) + .collect(Collectors.toSet()); } - return new org.onap.aai.graphgraph.dto.Graph(new LinkedList<>(computeNodeNames(edges)), - new LinkedList<>(edges), Collections.emptyList(), getVertexProperties(initialNode)); - } - - private boolean isParentChildFilter(String edgeFilter) { - return "Parents".equals(edgeFilter); - } - - private boolean isEdgeRulesFilter(String edgeFilter) { - return "Edgerules".equals(edgeFilter); - } - - private Set<Edge> filterEdgesStrict(Set<Edge> edges, Set<String> subGraphVertices) { - return edges.stream().filter( - e -> subGraphVertices.contains(e.getSource()) && subGraphVertices.contains(e.getTarget())) - .collect( - Collectors.toSet()); - } - - private Set<Edge> filterEdges(Set<Edge> edges, Set<String> subGraphVertices) { - return edges.stream().filter(e -> - subGraphVertices.contains(e.getSource()) || subGraphVertices.contains(e.getTarget())) - .filter(e -> !e.getType().equals(EdgeType.EDGE_RULE.getTypeName())) - .collect(Collectors.toSet()); - } - - private Set<NodeName> computeNodeNames(Set<Edge> edges) { - return edges.stream().flatMap(e -> e.getNodeNames().stream()).collect( - Collectors.toSet()); - } - - private Set<Edge> computeAllEdges(String initial, boolean parentChild, boolean edgeRules) { - Set<Edge> result = new HashSet<>(); - List<String> toQuery = new LinkedList<>(); - toQuery.add(initial); - final List<String> toVisit = new LinkedList<>(); - Set<String> visited = new HashSet<>(); - - while (!toQuery.isEmpty()) { - for (String v : toQuery) { - visited.add(v); - - graph.edgesOf(v).forEach(edge -> { - String neighbour = edge.getTarget(); - toVisit.add(neighbour); - - if (EdgeType.CHILD.isType(edge.getType()) && parentChild) { - result.add(new Edge(neighbour, edge.getSource(), EdgeType.PARENT.getTypeName(), - createTooltip(neighbour, edge.getSource(), EdgeType.PARENT.getTypeName(), edge.getLabel()))); - } - - if (EdgeType.EDGE_RULE.isType(edge.getType()) && edgeRules) { - result.add(new Edge(edge.getSource(), neighbour, edge.getLabel(), - createTooltip(neighbour, edge.getSource(), EdgeType.EDGE_RULE.getTypeName(), - edge.getLabel()))); - } - }); - } - toQuery.clear(); - toQuery.addAll( - toVisit.stream().filter(s -> !visited.contains(s)).collect(Collectors.toList())); + private Set<NodeName> computeNodeNames(Set<Edge> edges) { + return edges.stream() + .flatMap(e -> e.getNodeNames().stream()) + .collect(Collectors.toSet()); } - return result; + private Set<Edge> computeAllEdges(String initial, boolean parentChild, boolean edgeRules) { + Set<Edge> result = new HashSet<>(); + List<String> toQuery = new LinkedList<>(); + toQuery.add(initial); + final List<String> toVisit = new LinkedList<>(); + Set<String> visited = new HashSet<>(); + + while (!toQuery.isEmpty()) { + for (String v : toQuery) { + visited.add(v); + + graph.edgesOf(v).forEach(edge -> { + String neighbour = edge.getTarget(); + toVisit.add(neighbour); + + if (EdgeType.CHILD.isType(edge.getType()) && parentChild) { + result.add(new Edge( + neighbour, edge.getSource(), EdgeType.PARENT.getTypeName(), createTooltip( + neighbour, edge.getSource(), EdgeType.PARENT.getTypeName(), edge.getLabel()))); + } + + if (EdgeType.EDGE_RULE.isType(edge.getType()) && edgeRules) { + result.add(new Edge( + edge.getSource(), neighbour, edge.getLabel(), createTooltip( + neighbour, edge.getSource(), EdgeType.EDGE_RULE.getTypeName(), edge.getLabel()))); + } + }); + } + toQuery.clear(); + toQuery.addAll(toVisit.stream().filter(s -> !visited.contains(s)).collect(Collectors.toList())); + } + + return result; + + } - } + private Set<String> computeNodes(String vertex, int hops, String relationshipName) { + List<String> toQuery = new LinkedList<>(); + toQuery.add(vertex); - private Set<String> computeNodes(String vertex, int hops, String relationshipName) { - List<String> toQuery = new LinkedList<>(); - toQuery.add(vertex); + Set<String> visited = new HashSet<>(); + int i = 0; - Set<String> visited = new HashSet<>(); - int i = 0; + final List<String> toVisit = new LinkedList<>(); + while (!toQuery.isEmpty() && hops > i) { + i++; + toVisit.clear(); - final List<String> toVisit = new LinkedList<>(); - while (!toQuery.isEmpty() && hops > i) { - i++; - toVisit.clear(); + for (String v : toQuery) { + visited.add(v); + graph.edgesOf(v).stream() + .filter(e -> e.getType().equals(relationshipName)) + .map(MetadataEdge::getTarget) + .forEach(toVisit::add); + } - for (String v : toQuery) { - visited.add(v); - graph.edgesOf(v).stream().filter(e -> e.getType().equals(relationshipName)).map( - MetadataEdge::getTarget).forEach(toVisit::add); - } + toQuery.clear(); + toQuery.addAll(toVisit.stream().filter(v -> !visited.contains(v)).collect(Collectors.toList())); + } - toQuery.clear(); - toQuery - .addAll(toVisit.stream().filter(v -> !visited.contains(v)).collect(Collectors.toList())); + return visited; } - return visited; - } - - - @Override - public org.onap.aai.graphgraph.dto.Graph getGraph(String fromNode, String toNode, - String edgeFilter) { - init(); - Graph<String, MetadataEdge> tempGraph = createGraph(isParentChildFilter(edgeFilter), isEdgeRulesFilter(edgeFilter)); - List<List<NodeName>> paths = new LinkedList<>(); - FloydWarshallShortestPaths<String, MetadataEdge> shortestPaths; - - while (true) { - shortestPaths = new FloydWarshallShortestPaths<>(tempGraph); - GraphPath<String, MetadataEdge> p = shortestPaths.getPath(fromNode, toNode); - if (p == null || p.getEdgeList() == null || p.getEdgeList().isEmpty()) { - break; - } - String previous = fromNode; - List<NodeName> path = new LinkedList<>(); - for (MetadataEdge e : p.getEdgeList()) { - if (e.getTarget().equals(previous)) { - previous = e.getSource(); - path.add(new NodeName(e.getTarget())); - } else { - previous = e.getTarget(); - path.add(new NodeName(e.getSource())); + @Override + public org.onap.aai.graphgraph.dto.Graph getGraph(String fromNode, String toNode, String edgeFilter) { + init(); + Graph<String, MetadataEdge> tempGraph = createGraph( + isParentChildFilter(edgeFilter), isEdgeRulesFilter(edgeFilter)); + List<List<NodeName>> paths = new LinkedList<>(); + FloydWarshallShortestPaths<String, MetadataEdge> shortestPaths; + + while (true) { + shortestPaths = new FloydWarshallShortestPaths<>(tempGraph); + GraphPath<String, MetadataEdge> p = shortestPaths.getPath(fromNode, toNode); + if (p == null || p.getEdgeList() == null || p.getEdgeList().isEmpty()) { + break; + } + String previous = fromNode; + List<NodeName> path = new LinkedList<>(); + for (MetadataEdge e : p.getEdgeList()) { + if (e.getTarget().equals(previous)) { + previous = e.getSource(); + path.add(new NodeName(e.getTarget())); + } else { + previous = e.getTarget(); + path.add(new NodeName(e.getSource())); + } + } + path.add(new NodeName(previous)); + paths.add(path); + tempGraph.removeEdge(p.getEdgeList().get(p.getLength() - 1)); //remove last edge from path } - } - path.add(new NodeName(previous)); - paths.add(path); - tempGraph.removeEdge(p.getEdgeList().get(p.getLength() - 1)); //remove last edge from path + + Set<Edge> edges = computeAllEdges(fromNode, isParentChildFilter(edgeFilter), isEdgeRulesFilter(edgeFilter)); + edges = filterEdgesStrict( + edges, + paths.stream() + .flatMap(Collection::stream) + .map(NodeName::getId) + .collect(Collectors.toSet())); + return new org.onap.aai.graphgraph.dto.Graph( + new LinkedList<>(computeNodeNames(edges)), new LinkedList<>(edges), + paths, getVertexProperties(fromNode)); } - Set<Edge> edges = computeAllEdges(fromNode, isParentChildFilter(edgeFilter), - isEdgeRulesFilter(edgeFilter)); - edges = filterEdgesStrict(edges, - paths.stream().flatMap(Collection::stream).map(NodeName::getId).collect( - Collectors.toSet())); - return new org.onap.aai.graphgraph.dto.Graph(new LinkedList<>(computeNodeNames(edges)), - new LinkedList<>(edges), paths, getVertexProperties(fromNode)); - } - - private List<Property> createTooltip(String target, String v, String type, String label) { - List<Property> properties = new LinkedList<>(); - properties.add(new Property("From", target)); - properties.add(new Property("To", v)); - properties.add(new Property("Type", type)); - properties.add(new Property("Relationship", label)); - properties.addAll(getEdgeProperties(v, target, EdgeType.EDGE_RULE.isType(type) ? EdgeType.EDGE_RULE.getTypeName() : EdgeType.PARENT.getTypeName())); - return properties; - } + private List<Property> createTooltip(String target, String v, String type, String label) { + List<Property> properties = new LinkedList<>(); + properties.add(new Property("From", target)); + properties.add(new Property("To", v)); + properties.add(new Property("Type", type)); + properties.add(new Property("Relationship", label)); + properties.addAll(getEdgeProperties( + v, target, + EdgeType.EDGE_RULE.isType(type) ? EdgeType.EDGE_RULE.getTypeName() : EdgeType.PARENT.getTypeName())); + return properties; + } } diff --git a/src/main/java/org/onap/aai/graphgraph/reader/EdgeType.java b/src/main/java/org/onap/aai/graphgraph/reader/EdgeType.java index 1250474..fd2614c 100644 --- a/src/main/java/org/onap/aai/graphgraph/reader/EdgeType.java +++ b/src/main/java/org/onap/aai/graphgraph/reader/EdgeType.java @@ -1,38 +1,40 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.reader; public enum EdgeType { - EDGE_RULE("edgerule"), PARENT("parent"), CHILD("child"); + EDGE_RULE("edgerule"), + PARENT("parent"), + CHILD("child"); - private final String name; + private final String name; - EdgeType(String s) { - name = s; - } + EdgeType(String s) { + name = s; + } - public String getTypeName() { - return this.name; - } + public String getTypeName() { + return this.name; + } - public boolean isType(String o){ - return name.equals(o); - } + public boolean isType(String o) { + return name.equals(o); + } } diff --git a/src/main/java/org/onap/aai/graphgraph/reader/MetadataEdge.java b/src/main/java/org/onap/aai/graphgraph/reader/MetadataEdge.java index fb8e53f..2dea403 100644 --- a/src/main/java/org/onap/aai/graphgraph/reader/MetadataEdge.java +++ b/src/main/java/org/onap/aai/graphgraph/reader/MetadataEdge.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.reader; @@ -23,49 +23,49 @@ import org.jgrapht.graph.DefaultEdge; public class MetadataEdge extends DefaultEdge { - private final String type; - private final String target; - private final String source; - private final String label; + private final String type; + private final String target; + private final String source; + private final String label; - MetadataEdge(String type, String source, String target) { - this.source = source; - this.target = target; - this.type = type; - this.label = ""; - } + MetadataEdge(String type, String source, String target) { + this.source = source; + this.target = target; + this.type = type; + this.label = ""; + } - MetadataEdge(String type, String target, String source, String label) { - this.type = type; - this.target = target; - this.source = source; - this.label = label; - } + MetadataEdge(String type, String target, String source, String label) { + this.type = type; + this.target = target; + this.source = source; + this.label = label; + } - public String getLabel() { - return label; - } + public String getLabel() { + return label; + } - @Override - public String getTarget() { - return target; - } + @Override + public String getTarget() { + return target; + } - @Override - public String getSource() { - return source; - } + @Override + public String getSource() { + return source; + } - public String getType() { - return type; - } + public String getType() { + return type; + } - @Override - public String toString() { - return "MetadataEdge{" + - "type='" + type + '\'' + - ", target='" + target + '\'' + - ", source='" + source + '\'' + - '}'; - } + @Override + public String toString() { + return "MetadataEdge{" + + "type='" + type + '\'' + + ", target='" + target + '\'' + + ", source='" + source + '\'' + + '}'; + } } diff --git a/src/main/java/org/onap/aai/graphgraph/reader/SchemaReader.java b/src/main/java/org/onap/aai/graphgraph/reader/SchemaReader.java index a924e63..38e1bf6 100644 --- a/src/main/java/org/onap/aai/graphgraph/reader/SchemaReader.java +++ b/src/main/java/org/onap/aai/graphgraph/reader/SchemaReader.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.reader; @@ -27,11 +27,16 @@ import org.onap.aai.graphgraph.dto.Property; import java.util.List; public interface SchemaReader { + String getSchemaName(); + List<NodeName> getAllVertexNames(String edgeFilter); + List<NodeProperty> getVertexProperties(String nodeName); + List<Property> getEdgeProperties(String fromNode, String toNode, String type); - Graph getGraph(String initialNode, int parentHops, int cousinHops, int childHops, - String edgeFilter); + + Graph getGraph(String initialNode, int parentHops, int cousinHops, int childHops, String edgeFilter); + Graph getGraph(String fromNode, String toNode, String edgeFilter); } diff --git a/src/main/java/org/onap/aai/graphgraph/reader/SchemaRepository.java b/src/main/java/org/onap/aai/graphgraph/reader/SchemaRepository.java index f49a669..5764c50 100644 --- a/src/main/java/org/onap/aai/graphgraph/reader/SchemaRepository.java +++ b/src/main/java/org/onap/aai/graphgraph/reader/SchemaRepository.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.reader; @@ -24,20 +24,22 @@ import java.util.Optional; import java.util.stream.Collectors; public class SchemaRepository { + private List<SchemaReader> readers; public SchemaRepository(List<SchemaReader> readers) { this.readers = readers; } - public List<String> getAllSchemaNames(){ + public List<String> getAllSchemaNames() { return readers.stream().map(SchemaReader::getSchemaName).collect(Collectors.toList()); } - public SchemaReader getSchemaReader(String schemaName){ + public SchemaReader getSchemaReader(String schemaName) { Optional<SchemaReader> reader = readers.stream().filter(r -> schemaName.equals(r.getSchemaName())).findFirst(); - if(!reader.isPresent()) + if (!reader.isPresent()) { throw new IllegalArgumentException("Schema " + schemaName + " not found"); + } return reader.get(); } diff --git a/src/main/java/org/onap/aai/graphgraph/velocity/VelocityAssociation.java b/src/main/java/org/onap/aai/graphgraph/velocity/VelocityAssociation.java index 697fad1..dda37b5 100644 --- a/src/main/java/org/onap/aai/graphgraph/velocity/VelocityAssociation.java +++ b/src/main/java/org/onap/aai/graphgraph/velocity/VelocityAssociation.java @@ -1,115 +1,113 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.velocity; public class VelocityAssociation extends VelocityId { - private final String name; - private final VelocityEntity fromEntity; - private final VelocityEntity toEntity; - private final String fromId = getRandomId(); - private final String toId = getRandomId(); - private final String multiplicity; - private final boolean isComposition; - - public VelocityAssociation(VelocityEntity fromEntity, - VelocityEntity toEntity, String name, String multiplicity, boolean isComposition) { - this.fromEntity = fromEntity; - this.toEntity = toEntity; - this.name = name; - this.multiplicity = multiplicity; - this.isComposition = isComposition; - } - - public String getFromEntityName(){ - return fromEntity.getName(); - } - - public String getToEntityName(){ - return toEntity.getName(); - } - - public String getFromEntityId(){ - return fromEntity.getId(); - } - - public String getToEntityId(){ - return toEntity.getId(); - } - - public String getFromId() { - return fromId; - } - - public String getToId() { - return toId; - } - - public String getName() { - return name; - } - - public String getMultiplicity() { - return multiplicity.toUpperCase(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; + private final String name; + private final VelocityEntity fromEntity; + private final VelocityEntity toEntity; + private final String fromId = getRandomId(); + private final String toId = getRandomId(); + private final String multiplicity; + private final boolean isComposition; + + public VelocityAssociation(VelocityEntity fromEntity, + VelocityEntity toEntity, String name, String multiplicity, boolean isComposition) { + this.fromEntity = fromEntity; + this.toEntity = toEntity; + this.name = name; + this.multiplicity = multiplicity; + this.isComposition = isComposition; } - if (o == null || getClass() != o.getClass()) { - return false; + + public String getFromEntityName() { + return fromEntity.getName(); + } + + public String getToEntityName() { + return toEntity.getName(); + } + + public String getFromEntityId() { + return fromEntity.getId(); } - VelocityAssociation that = (VelocityAssociation) o; + public String getToEntityId() { + return toEntity.getId(); + } + + public String getFromId() { + return fromId; + } + + public String getToId() { + return toId; + } - if (!name.equals(that.name)) { - return false; + public String getName() { + return name; } - if (!fromEntity.equals(that.fromEntity)) { - return false; + + public String getMultiplicity() { + return multiplicity.toUpperCase(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + VelocityAssociation that = (VelocityAssociation) o; + + if (!name.equals(that.name)) { + return false; + } + if (!fromEntity.equals(that.fromEntity)) { + return false; + } + return toEntity.equals(that.toEntity); + } + + public boolean getIsComposition() { + return isComposition; + } + + @Override + public String toString() { + return "VelocityAssociation{" + + "name='" + name + '\'' + + ", fromEntity=" + fromEntity + + ", toEntity=" + toEntity + + '}'; + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + fromEntity.hashCode(); + result = 31 * result + toEntity.hashCode(); + return result; } - return toEntity.equals(that.toEntity); - } - - public boolean getIsComposition() { - return isComposition; - } - - @Override - public String toString() { - return "VelocityAssociation{" + - "name='" + name + '\'' + - ", fromEntity=" + fromEntity + - ", toEntity=" + toEntity + - '}'; - } - - @Override - public int hashCode() { - int result = name.hashCode(); - result = 31 * result + fromEntity.hashCode(); - result = 31 * result + toEntity.hashCode(); - return result; - - - } } diff --git a/src/main/java/org/onap/aai/graphgraph/velocity/VelocityEntity.java b/src/main/java/org/onap/aai/graphgraph/velocity/VelocityEntity.java index 99f94f3..424c0e6 100644 --- a/src/main/java/org/onap/aai/graphgraph/velocity/VelocityEntity.java +++ b/src/main/java/org/onap/aai/graphgraph/velocity/VelocityEntity.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.velocity; @@ -24,75 +24,73 @@ import java.util.List; import java.util.Set; public class VelocityEntity extends VelocityId { - private String name; - private String description; - private List<VelocityAssociation> neighbours = new LinkedList<>(); - private Set<VelocityEntityProperty> properties; - public Set<VelocityEntityProperty> getProperties() { - return properties; - } + private String name; + private String description; + private List<VelocityAssociation> neighbours = new LinkedList<>(); + private Set<VelocityEntityProperty> properties; - public void setProperties(Set<VelocityEntityProperty> properties) { - this.properties = properties; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } + public VelocityEntity(String name) { + this.name = name; + } - public VelocityEntity(String name) { - this.name = name; - } + public Set<VelocityEntityProperty> getProperties() { + return properties; + } - public String getName() { - return name; - } + public void setProperties(Set<VelocityEntityProperty> properties) { + this.properties = properties; + } - public void setName(String name) { - this.name = name; - } + public String getDescription() { + return description; + } - public List<VelocityAssociation> getNeighbours() { - return neighbours; - } + public void setDescription(String description) { + this.description = description; + } - public void setNeighbours(List<VelocityAssociation> neighbours) { - this.neighbours = neighbours; - } + public String getName() { + return name; + } - public void addNeighbours(VelocityAssociation neighbour) { - neighbours.add(neighbour); - } + public void setName(String name) { + this.name = name; + } + public List<VelocityAssociation> getNeighbours() { + return neighbours; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; + public void setNeighbours(List<VelocityAssociation> neighbours) { + this.neighbours = neighbours; } - if (o == null || getClass() != o.getClass()) { - return false; + + public void addNeighbours(VelocityAssociation neighbour) { + neighbours.add(neighbour); } - VelocityEntity that = (VelocityEntity) o; + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } - return name.equals(that.name); - } + VelocityEntity that = (VelocityEntity) o; - @Override - public int hashCode() { - return name.hashCode(); - } + return name.equals(that.name); + } - @Override - public String toString() { - return "VelocityEntity{" + - "name='" + name + '\'' + - '}'; - } + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public String toString() { + return "VelocityEntity{" + "name='" + name + '\'' + '}'; + } } diff --git a/src/main/java/org/onap/aai/graphgraph/velocity/VelocityEntityProperty.java b/src/main/java/org/onap/aai/graphgraph/velocity/VelocityEntityProperty.java index e8dec9f..2c65ae3 100644 --- a/src/main/java/org/onap/aai/graphgraph/velocity/VelocityEntityProperty.java +++ b/src/main/java/org/onap/aai/graphgraph/velocity/VelocityEntityProperty.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.velocity; @@ -24,35 +24,33 @@ import org.onap.aai.graphgraph.dto.Property; public class VelocityEntityProperty extends Property { - private final VelocityEntity entity; - private final String propertyId; - public VelocityEntityProperty(String propertyName, String propertyValue, VelocityEntity entity) { - super(propertyName, propertyValue); - this.entity = entity; - propertyId = entity != null ? entity.getRandomId() : UUID.randomUUID().toString(); - } - - public String getEntityId() { - return entity.getId(); - } - - public String getEntityName() { - return entity.getName(); - } - - public boolean hasEntity(){ - return entity != null; - } - - public String getPropertyId() { - return propertyId; - } - - @Override - public String toString() { - return "VelocityEntityProperty{" + - " name=" + getPropertyName() + - " type=" + getPropertyValue() + - '}'; - } + private final VelocityEntity entity; + private final String propertyId; + + public VelocityEntityProperty(String propertyName, String propertyValue, VelocityEntity entity) { + super(propertyName, propertyValue); + this.entity = entity; + propertyId = entity != null ? entity.getRandomId() : UUID.randomUUID().toString(); + } + + public String getEntityId() { + return entity.getId(); + } + + public String getEntityName() { + return entity.getName(); + } + + public boolean hasEntity() { + return entity != null; + } + + public String getPropertyId() { + return propertyId; + } + + @Override + public String toString() { + return "VelocityEntityProperty{" + " name=" + getPropertyName() + " type=" + getPropertyValue() + '}'; + } } diff --git a/src/main/java/org/onap/aai/graphgraph/velocity/VelocityId.java b/src/main/java/org/onap/aai/graphgraph/velocity/VelocityId.java index fc44a5e..9b59c82 100644 --- a/src/main/java/org/onap/aai/graphgraph/velocity/VelocityId.java +++ b/src/main/java/org/onap/aai/graphgraph/velocity/VelocityId.java @@ -1,34 +1,35 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2019 Orange Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph.velocity; import java.util.UUID; public abstract class VelocityId { - private String id = getRandomId(); - public String getId() { - return id; - } + private String id = getRandomId(); + + public String getId() { + return id; + } - public String getRandomId() { - return UUID.randomUUID().toString(); - } + public String getRandomId() { + return UUID.randomUUID().toString(); + } } diff --git a/src/main/resources/docker-assembly.xml b/src/main/resources/docker-assembly.xml index afd1970..54a87af 100644 --- a/src/main/resources/docker-assembly.xml +++ b/src/main/resources/docker-assembly.xml @@ -1,6 +1,6 @@ <assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"> <id>distribution</id> <formats> <format>jar</format> @@ -13,11 +13,11 @@ </fileSet> </fileSets> -<files> - <file> + <files> + <file> <source>target/${project.artifactId}-${project.version}.${project.packaging}</source> <outputDirectory>/</outputDirectory> <filtered>false</filtered> </file> -</files> -</assembly>
\ No newline at end of file + </files> +</assembly> diff --git a/src/main/resources/etc/auth/realm.properties b/src/main/resources/etc/auth/realm.properties index f0e0172..3ba96bc 100644 --- a/src/main/resources/etc/auth/realm.properties +++ b/src/main/resources/etc/auth/realm.properties @@ -1,13 +1,13 @@ # format : username: password[,rolename ...] # default username/password: AAI/AAI, MSO/MSO, ModelLoader/ModelLoader... -AAI:OBF:1gfr1ev31gg7,admin -MSO:OBF:1jzx1lz31k01,admin -SDNC:OBF:1itr1i0l1i151isv,admin -DCAE:OBF:1g8u1f9d1f991g8w,admin -POLICY:OBF:1mk61i171ima1im41i0j1mko,admin -ASDC:OBF:1f991j0u1j001f9d,admin -VID:OBF:1jm91i0v1jl9,admin -APPC:OBF:1f991ksf1ksf1f9d,admin -ModelLoader:OBF:1qvu1v2h1sov1sar1wfw1j7j1wg21saj1sov1v1x1qxw,admin -AaiUI:OBF:1gfr1p571unz1p4j1gg7,admin -OOF:OBF:1img1ke71ily,admin +AAI=OBF:1gfr1ev31gg7,admin +MSO=OBF:1jzx1lz31k01,admin +SDNC=OBF:1itr1i0l1i151isv,admin +DCAE=OBF:1g8u1f9d1f991g8w,admin +POLICY=OBF:1mk61i171ima1im41i0j1mko,admin +ASDC=OBF:1f991j0u1j001f9d,admin +VID=OBF:1jm91i0v1jl9,admin +APPC=OBF:1f991ksf1ksf1f9d,admin +ModelLoader=OBF:1qvu1v2h1sov1sar1wfw1j7j1wg21saj1sov1v1x1qxw,admin +AaiUI=OBF:1gfr1p571unz1p4j1gg7,admin +OOF=OBF:1img1ke71ily,admin diff --git a/src/main/resources/model_export.vm b/src/main/resources/model_export.vm index c461d59..ae92fab 100644 --- a/src/main/resources/model_export.vm +++ b/src/main/resources/model_export.vm @@ -5,148 +5,148 @@ <importedPackage xmi:type="uml:Model" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#_0"/> </packageImport> - <packagedElement xmi:type="uml:Package" xmi:id="_dl4P8LIpEemXwfLFUQ7Icw" name="Associations"> + <packagedElement xmi:type="uml:Package" xmi:id="_dl4P8LIpEemXwfLFUQ7Icw" name="Associations"> #foreach($association in $associationList) - <packagedElement xmi:type="uml:Association" xmi:id="$association.id" - name="$association.name" memberEnd="$association.fromId $association.toId"> - <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="$association.randomId" - source="org.eclipse.papyrus"> - <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="$association.randomId" - key="nature" value="UML_Nature"/> - </eAnnotations> - <ownedEnd xmi:type="uml:Property" xmi:id="$association.fromId" - name="$association.toEntityName" type="$association.toEntityId" - association="$association.id"> - #if($association.multiplicity == "ONE2MANY" || $association.multiplicity == - "MANY2MANY") - <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId"/> - <upperValue xmi:type="uml:LiteralUnlimitedNatural" xmi:id="$association.randomId" - value="*"/> - #else - <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" value="1"/> - <upperValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" value="1"/> - #end - </ownedEnd> - #if( ! $association.isComposition) - <ownedEnd xmi:type="uml:Property" xmi:id="$association.toId" - name="$association.fromEntityName" type="$association.fromEntityId" - association="$association.id"> - #if($association.multiplicity == "MANY2ONE" || $association.multiplicity == - "MANY2MANY") - <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId"/> - <upperValue xmi:type="uml:LiteralUnlimitedNatural" - xmi:id="$association.randomId" value="*"/> - #else - <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" - value="1"/> - <upperValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" - value="1"/> - #end - </ownedEnd> - #end - </packagedElement> + <packagedElement xmi:type="uml:Association" xmi:id="$association.id" + name="$association.name" memberEnd="$association.fromId $association.toId"> + <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="$association.randomId" + source="org.eclipse.papyrus"> + <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="$association.randomId" + key="nature" value="UML_Nature"/> + </eAnnotations> + <ownedEnd xmi:type="uml:Property" xmi:id="$association.fromId" + name="$association.toEntityName" type="$association.toEntityId" + association="$association.id"> + #if($association.multiplicity == "ONE2MANY" || $association.multiplicity == + "MANY2MANY") + <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId"/> + <upperValue xmi:type="uml:LiteralUnlimitedNatural" xmi:id="$association.randomId" + value="*"/> + #else + <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" value="1"/> + <upperValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" value="1"/> + #end + </ownedEnd> + #if( ! $association.isComposition) + <ownedEnd xmi:type="uml:Property" xmi:id="$association.toId" + name="$association.fromEntityName" type="$association.fromEntityId" + association="$association.id"> + #if($association.multiplicity == "MANY2ONE" || $association.multiplicity == + "MANY2MANY") + <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId"/> + <upperValue xmi:type="uml:LiteralUnlimitedNatural" + xmi:id="$association.randomId" value="*"/> + #else + <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" + value="1"/> + <upperValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" + value="1"/> + #end + </ownedEnd> + #end + </packagedElement> #end - </packagedElement> - <packagedElement xmi:type="uml:Package" xmi:id="_rBN-QLIqEemXwfLFUQ7Icw" name="ObjectClasses"> + </packagedElement> + <packagedElement xmi:type="uml:Package" xmi:id="_rBN-QLIqEemXwfLFUQ7Icw" name="ObjectClasses"> #foreach($entity in $entityList) - <packagedElement xmi:type="uml:Class" xmi:id="$entity.id" name="$entity.name"> - <ownedComment xmi:type="uml:Comment" xmi:id="$entity.randomId" annotatedElement="$entity.id"> - <body>$entity.description</body> - </ownedComment> - #foreach($association in $entity.neighbours) - #if( $association.isComposition) - <ownedAttribute xmi:type="uml:Property" xmi:id="$association.toId" - name="$association.fromEntityName" - type="$association.fromEntityId" aggregation="composite" - association="$association.id"> - #if($association.multiplicity == "MANY2ONE" || $association.multiplicity == - "MANY2MANY") - <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId"/> - <upperValue xmi:type="uml:LiteralUnlimitedNatural" - xmi:id="$association.randomId" value="*"/> - #else - <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" - value="1"/> - <upperValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" - value="1"/> - #end - </ownedAttribute> - #end - #end - #foreach($prop in $entity.properties) - #if($prop.propertyValue.contains("java.lang")) - <ownedAttribute xmi:type="uml:Property" xmi:id="$prop.propertyId" - name="$prop.propertyName"> - #if($prop.propertyValue == "java.lang.String") - <type xmi:type="uml:PrimitiveType" - href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/> - #end# - #if($prop.propertyValue == "java.lang.Long") - <type xmi:type="uml:PrimitiveType" - href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Integer"/> - #end - #if($prop.propertyValue == "java.lang.Boolean") - <type xmi:type="uml:PrimitiveType" - href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Boolean"/> - #end - #if($prop.propertyValue == "java.lang.Integer") - <type xmi:type="uml:PrimitiveType" - href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Integer"/> - #end - </ownedAttribute> - #elseif(! $prop.hasEntity) - #* <ownedAttribute xmi:type="uml:Property" xmi:id="$entity.randomId" name="$prop.propertyName"/>*# - #else - <ownedAttribute xmi:type="uml:Property" xmi:id="$prop.propertyId" - name="$prop.getEntityName()" type="$prop.getEntityId()"/> - #end - #end - </packagedElement> - #end - </packagedElement> - <profileApplication xmi:type="uml:ProfileApplication" xmi:id="_o4e8sN4HEemqKsY3En9wuw"> - <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_o5H14N4HEemqKsY3En9wuw" source="PapyrusVersion"> - <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_o5Ic8N4HEemqKsY3En9wuw" key="Version" value="0.0.4"/> - <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_o5Ic8d4HEemqKsY3En9wuw" key="Comment" value="Metaclasses Property and Stereotype added via <Element Import>."/> - <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_o5Ic8t4HEemqKsY3En9wuw" key="Copyright" value=""/> - <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_o5Ic894HEemqKsY3En9wuw" key="Date" value="2017-08-08"/> - <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_o5Ic9N4HEemqKsY3En9wuw" key="Author" value=""/> - </eAnnotations> - <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_o4lDUN4HEemqKsY3En9wuw" source="http://www.eclipse.org/uml2/2.0.0/UML"> - <references xmi:type="ecore:EPackage" href="../ProfileLifecycleProfile/ProfileLifecycle_Profile.profile.uml#_AL3HsHweEee8oZaf2rRQlg"/> - </eAnnotations> - <appliedProfile xmi:type="uml:Profile" href="../ProfileLifecycleProfile/ProfileLifecycle_Profile.profile.uml#_CBpGoEdZEearpawF38eisA"/> - </profileApplication> - <profileApplication xmi:type="uml:ProfileApplication" xmi:id="_o5JEAN4HEemqKsY3En9wuw"> - <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_pCSY8d4HEemqKsY3En9wuw" source="PapyrusVersion"> - <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_pCSY8t4HEemqKsY3En9wuw" key="Version" value="0.2.17"/> - <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_pCTAAN4HEemqKsY3En9wuw" key="Comment" value="Editorial corrections in the description of OpenModelAttribute::partOfObjectKey and Reference."/> - <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_pCTAAd4HEemqKsY3En9wuw" key="Copyright" value=""/> - <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_pCTAAt4HEemqKsY3En9wuw" key="Date" value="2018-12-12"/> - <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_pCTAA94HEemqKsY3En9wuw" key="Author" value=""/> - </eAnnotations> - <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_o5JrEN4HEemqKsY3En9wuw" source="http://www.eclipse.org/uml2/2.0.0/UML"> - <references xmi:type="ecore:EPackage" href="../OpenModelProfile/OpenModel_Profile.profile.uml#_FV_e4P4LEeiJYfiYi3RAYQ"/> - </eAnnotations> - <appliedProfile xmi:type="uml:Profile" href="../OpenModelProfile/OpenModel_Profile.profile.uml#_m1xqsHBgEd6FKu9XX1078A"/> - </profileApplication> -</uml:Model> -<OpenModel_Profile:OpenModelStatement xmi:id="_o-KG4N4HEemqKsY3En9wuw" base_Model="_Z9InoLIpEemXwfLFUQ7Icw"/> -#foreach($association in $associationList) -<OpenModel_Profile:OpenModelAttribute xmi:id="$association.randomId" base_StructuralFeature="$association.fromId"/> -#if( ! $association.isComposition) -<OpenModel_Profile:OpenModelAttribute xmi:id="$association.randomId" base_StructuralFeature="$association.toId"/> -#end -#end -#foreach($entity in $entityList) - <OpenModel_Profile:OpenModelClass xmi:id="$entity.randomId" base_Class="$entity.id"/> - #foreach($association in $entity.neighbours) + <packagedElement xmi:type="uml:Class" xmi:id="$entity.id" name="$entity.name"> + <ownedComment xmi:type="uml:Comment" xmi:id="$entity.randomId" annotatedElement="$entity.id"> + <body>$entity.description</body> + </ownedComment> + #foreach($association in $entity.neighbours) #if( $association.isComposition) - <OpenModel_Profile:OpenModelAttribute xmi:id="$association.randomId" base_StructuralFeature="$association.toId"/> + <ownedAttribute xmi:type="uml:Property" xmi:id="$association.toId" + name="$association.fromEntityName" + type="$association.fromEntityId" aggregation="composite" + association="$association.id"> + #if($association.multiplicity == "MANY2ONE" || $association.multiplicity == + "MANY2MANY") + <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId"/> + <upperValue xmi:type="uml:LiteralUnlimitedNatural" + xmi:id="$association.randomId" value="*"/> + #else + <lowerValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" + value="1"/> + <upperValue xmi:type="uml:LiteralInteger" xmi:id="$association.randomId" + value="1"/> + #end + </ownedAttribute> #end - #end - #foreach($prop in $entity.properties) - <OpenModel_Profile:OpenModelAttribute xmi:id="$entity.randomId" base_StructuralFeature="$prop.propertyId"/> - #end -#end -</xmi:XMI>
\ No newline at end of file + #end + #foreach($prop in $entity.properties) + #if($prop.propertyValue.contains("java.lang")) + <ownedAttribute xmi:type="uml:Property" xmi:id="$prop.propertyId" + name="$prop.propertyName"> + #if($prop.propertyValue == "java.lang.String") + <type xmi:type="uml:PrimitiveType" + href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/> + #end# + #if($prop.propertyValue == "java.lang.Long") + <type xmi:type="uml:PrimitiveType" + href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Integer"/> + #end + #if($prop.propertyValue == "java.lang.Boolean") + <type xmi:type="uml:PrimitiveType" + href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Boolean"/> + #end + #if($prop.propertyValue == "java.lang.Integer") + <type xmi:type="uml:PrimitiveType" + href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Integer"/> + #end + </ownedAttribute> + #elseif(! $prop.hasEntity) + #* <ownedAttribute xmi:type="uml:Property" xmi:id="$entity.randomId" name="$prop.propertyName"/>*# + #else + <ownedAttribute xmi:type="uml:Property" xmi:id="$prop.propertyId" + name="$prop.getEntityName()" type="$prop.getEntityId()"/> + #end + #end + </packagedElement> + #end + </packagedElement> + <profileApplication xmi:type="uml:ProfileApplication" xmi:id="_o4e8sN4HEemqKsY3En9wuw"> + <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_o5H14N4HEemqKsY3En9wuw" source="PapyrusVersion"> + <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_o5Ic8N4HEemqKsY3En9wuw" key="Version" value="0.0.4"/> + <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_o5Ic8d4HEemqKsY3En9wuw" key="Comment" value="Metaclasses Property and Stereotype added via <Element Import>."/> + <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_o5Ic8t4HEemqKsY3En9wuw" key="Copyright" value=""/> + <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_o5Ic894HEemqKsY3En9wuw" key="Date" value="2017-08-08"/> + <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_o5Ic9N4HEemqKsY3En9wuw" key="Author" value=""/> + </eAnnotations> + <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_o4lDUN4HEemqKsY3En9wuw" source="http://www.eclipse.org/uml2/2.0.0/UML"> + <references xmi:type="ecore:EPackage" href="../ProfileLifecycleProfile/ProfileLifecycle_Profile.profile.uml#_AL3HsHweEee8oZaf2rRQlg"/> + </eAnnotations> + <appliedProfile xmi:type="uml:Profile" href="../ProfileLifecycleProfile/ProfileLifecycle_Profile.profile.uml#_CBpGoEdZEearpawF38eisA"/> + </profileApplication> + <profileApplication xmi:type="uml:ProfileApplication" xmi:id="_o5JEAN4HEemqKsY3En9wuw"> + <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_pCSY8d4HEemqKsY3En9wuw" source="PapyrusVersion"> + <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_pCSY8t4HEemqKsY3En9wuw" key="Version" value="0.2.17"/> + <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_pCTAAN4HEemqKsY3En9wuw" key="Comment" value="Editorial corrections in the description of OpenModelAttribute::partOfObjectKey and Reference."/> + <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_pCTAAd4HEemqKsY3En9wuw" key="Copyright" value=""/> + <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_pCTAAt4HEemqKsY3En9wuw" key="Date" value="2018-12-12"/> + <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_pCTAA94HEemqKsY3En9wuw" key="Author" value=""/> + </eAnnotations> + <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_o5JrEN4HEemqKsY3En9wuw" source="http://www.eclipse.org/uml2/2.0.0/UML"> + <references xmi:type="ecore:EPackage" href="../OpenModelProfile/OpenModel_Profile.profile.uml#_FV_e4P4LEeiJYfiYi3RAYQ"/> + </eAnnotations> + <appliedProfile xmi:type="uml:Profile" href="../OpenModelProfile/OpenModel_Profile.profile.uml#_m1xqsHBgEd6FKu9XX1078A"/> + </profileApplication> + </uml:Model> + <OpenModel_Profile:OpenModelStatement xmi:id="_o-KG4N4HEemqKsY3En9wuw" base_Model="_Z9InoLIpEemXwfLFUQ7Icw"/> + #foreach($association in $associationList) + <OpenModel_Profile:OpenModelAttribute xmi:id="$association.randomId" base_StructuralFeature="$association.fromId"/> + #if( ! $association.isComposition) + <OpenModel_Profile:OpenModelAttribute xmi:id="$association.randomId" base_StructuralFeature="$association.toId"/> + #end + #end + #foreach($entity in $entityList) + <OpenModel_Profile:OpenModelClass xmi:id="$entity.randomId" base_Class="$entity.id"/> + #foreach($association in $entity.neighbours) + #if( $association.isComposition) + <OpenModel_Profile:OpenModelAttribute xmi:id="$association.randomId" base_StructuralFeature="$association.toId"/> + #end + #end + #foreach($prop in $entity.properties) + <OpenModel_Profile:OpenModelAttribute xmi:id="$entity.randomId" base_StructuralFeature="$prop.propertyId"/> + #end + #end +</xmi:XMI> diff --git a/src/test/java/org/onap/aai/graphgraph/AppTest.java b/src/test/java/org/onap/aai/graphgraph/AppTest.java index 7879fc3..5393d26 100644 --- a/src/test/java/org/onap/aai/graphgraph/AppTest.java +++ b/src/test/java/org/onap/aai/graphgraph/AppTest.java @@ -1,21 +1,21 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= +/* + ============LICENSE_START======================================================= + org.onap.aai + ================================================================================ + Copyright © 2019-2020 Orange Intellectual Property. All rights reserved. + ================================================================================ + 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. + ============LICENSE_END========================================================= */ package org.onap.aai.graphgraph; @@ -26,7 +26,7 @@ import org.junit.Test; /** * Unit test for simple App. */ -public class AppTest +public class AppTest { /** * Rigorous Test :-) |