Spring integration graph view using mock data

This commit is contained in:
Kris De Volder
2019-08-28 16:12:12 -07:00
parent 9f2553f09a
commit 42951dc1d3
32 changed files with 5984 additions and 11 deletions

View File

@@ -1,4 +1,4 @@
package org.springframework.ide.vscode.commons.sprotty.scan;
package org.springframework.ide.vscode.commons.sprotty.api;
import org.eclipse.sprotty.RequestModelAction;
import org.eclipse.sprotty.SModelRoot;

View File

@@ -1,4 +1,4 @@
package org.springframework.ide.vscode.commons.sprotty.scan;
package org.springframework.ide.vscode.commons.sprotty.api;
import java.util.function.Consumer;

View File

@@ -13,6 +13,8 @@ import org.eclipse.sprotty.SGraph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ide.vscode.commons.sprotty.api.DiagramGenerator;
import org.springframework.ide.vscode.commons.sprotty.api.DiagramServerManager;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

View File

@@ -1,6 +1,7 @@
package org.springframework.ide.vscode.commons.sprotty.scan;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import org.eclipse.sprotty.ActionMessage;
@@ -12,6 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient;
import org.springframework.ide.vscode.commons.sprotty.api.DiagramServerManager;
import org.springframework.stereotype.Controller;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
@@ -38,7 +40,7 @@ public class DiagramWebsocketServer implements WebSocketConfigurer, Initializing
private DiagramServerManager diagramServers;
@Autowired
private SimpleLanguageServer server;
private Optional<SimpleLanguageServer> optServer;
private void initializeGson() {
if (gson == null) {
@@ -51,10 +53,10 @@ public class DiagramWebsocketServer implements WebSocketConfigurer, Initializing
@Override
public void afterPropertiesSet() throws Exception {
initializeGson();
server.onSprottyMessage((jsonMessage) -> {
optServer.ifPresent(server -> server.onSprottyMessage((jsonMessage) -> {
ActionMessage actionMessage = gson.fromJson(jsonMessage, ActionMessage.class);
diagramServers.sendMessageToServer(actionMessage);
});
}));
diagramServers.setRemoteEndpoint(message -> {
sendMessage((JsonObject)gson.toJsonTree(message));
});
@@ -126,10 +128,12 @@ public class DiagramWebsocketServer implements WebSocketConfigurer, Initializing
}
private void sendMessage(JsonObject msg) {
STS4LanguageClient client = server.getClient();
if (client!=null) {
client.sprottyMessage(msg);
}
optServer.ifPresent(server -> {
STS4LanguageClient client = server.getClient();
if (client!=null) {
client.sprottyMessage(msg);
}
});
synchronized (ws_sessions) {
for (WebSocketSession ws : ws_sessions) {
try {

View File

@@ -14,6 +14,7 @@
<module>manifest-yaml-language-server</module>
<module>concourse-language-server</module>
<module>spring-boot-language-server</module>
<module>sprotty-si-view</module>
<module>bosh-language-server</module>
<module>jdt-ls-extension</module>
</modules>

View File

@@ -5,7 +5,6 @@ import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import org.eclipse.sprotty.DefaultDiagramServer;
import org.eclipse.sprotty.Dimension;
import org.eclipse.sprotty.Point;
import org.eclipse.sprotty.RequestModelAction;
@@ -22,7 +21,7 @@ import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.commons.sprotty.scan.DiagramGenerator;
import org.springframework.ide.vscode.commons.sprotty.api.DiagramGenerator;
import org.springframework.stereotype.Component;
@Component

View File

@@ -0,0 +1,31 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**
!**/src/test/**
/src/main/resources/static
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
### VS Code ###
.vscode/

View File

@@ -0,0 +1,31 @@
#!/bin/bash
set -e
workdir=$(pwd)
cd client
npm install && npm run build
client_dir=$(pwd)
cd $workdir
static_dir=${workdir}/src/main/resources/static
rm -fr $static_dir
mkdir -p $static_dir
echo "cp ${client_dir}/bundle* $static_dir"
cp ${client_dir}/bundle* $static_dir
cp ${client_dir}/index.html $static_dir
cp -R ${client_dir}/css $static_dir/css
cp -R ${client_dir}/lib $static_dir/lib
cp -R ${client_dir}/src $static_dir/src
../mvnw \
-f ../pom.xml \
-pl sprotty-si-view \
-am \
clean install \
-DskipTests

View File

@@ -0,0 +1,4 @@
node_modules
bundle.js
bundle.js.map
/lib

View File

@@ -0,0 +1,46 @@
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
.sprotty-node {
fill: #aae;
stroke: #66b;
stroke-width: 3;
}
.sprotty-text {
font-size: 16pt;
text-anchor: middle;
}
.sprotty-edge {
fill: none;
stroke: #488;
stroke-width: 2;
}
.sprotty-node.selected {
stroke: #dd8;
stroke-width: 6;
}
.sprotty-missing {
stroke-width: 1;
stroke: #f00;
fill: #f00;
font-family: SansSerif;
font-size: 14pt;
text-anchor: middle;
}

View File

@@ -0,0 +1,38 @@
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
.copyright {
margin-top: 10px;
text-align: right;
font-size: 10px;
color: #888;
}
.help {
margin-top: 24px;
text-align: right;
font-size: 16px;
color: #888;
}
svg {
margin-top: 15px;
width: 100%;
height: 500px;
border-style: solid;
border-width: 1px;
border-color: #bbb;
}

View File

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>sprotty Circles Example</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
<link rel="stylesheet" href="css/page.css">
<!-- support Microsoft browsers -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dom4/3.0.0/dom4.js">
</head>
<body>
<div class="container">
<div class="row" id="sprotty-app" client-id="spring-boot">
<div class="col-md-10">
<h1>sprotty Circles Example</h1>
<p>
<button id="refresh">Refresh</button>
<button id="scrambleNodes">Scramble nodes</button>
</p>
</div>
<div class="help col-md-2">
<a href='https://github.com/theia-ide/sprotty/wiki/Using-sprotty'>Help</a>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="spring-boot" class="sprotty"/>
</div>
<div class="copyright">
&copy; 2017 <a href="http://typefox.io">TypeFox GmbH</a>.
</div>
</div>
</div>
</div>
<script src="bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
{
"name": "sprotty-si-client",
"version": "1.0.0",
"description": "",
"main": "bundle.js",
"scripts": {
"defunct-build": "tsc -p ./tsconfig.json",
"build": "webpack --progress --config ./webpack.config.js",
"run": "npx http-server -c-1 .",
"watch": "tsc -w -p ./tsconfig.json",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Kris De Volder <kdevolder@pivotal.io>",
"license": "ISC",
"dependencies": {
"reflect-metadata": "^0.1.10",
"sprotty": "^0.6.0",
"sockjs-client": "^1.3.0"
},
"devDependencies": {
"@types/sockjs-client": "^1.1.1",
"@types/node": "^12.7.2",
"circular-dependency-plugin": "^2.0.0",
"core-js": "^2.5.1",
"css-loader": "^2.1.0",
"http-server": "^0.11.1",
"style-loader": "^0.23.1",
"ts-loader": "^4.4.2",
"typescript": "^3.5.3",
"webpack": "^4.15.1",
"webpack-cli": "^3.0.8"
}
}

View File

@@ -0,0 +1,22 @@
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import "reflect-metadata";
console.log("Loaded reflect-metadata");
import runStandalone from "./standalone";
runStandalone();

View File

@@ -0,0 +1,92 @@
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import {BeanNode, ExampleWebsocketDiagramServer, VSCodeWebViewDiagramServer} from "./model";
console.log("Loading di.config");
import "reflect-metadata"
import { Container, ContainerModule } from "inversify";
import {
defaultModule,
TYPES,
configureViewerOptions,
ConsoleLogger,
LogLevel,
boundsModule,
hoverModule,
moveModule,
selectModule,
undoRedoModule,
viewportModule,
LocalModelSource,
exportModule,
CircularNode,
configureModelElement,
SGraph,
updateModule,
graphModule,
routingModule,
modelSourceModule,
SLabel,
SLabelView,
SCompartment,
SCompartmentView, SEdge
} from "sprotty";
import {BeanNodeView, CircleNodeView, EdgeView, ExampleGraphView, ChannelNodeView} from "./views";
export enum TransportMedium {
None,
Websocket,
LSP
}
export default (transport: TransportMedium, clientId: string) => {
require("sprotty/css/sprotty.css");
require("../css/diagram.css");
const circlegraphModule = new ContainerModule((bind, unbind, isBound, rebind) => {
switch (transport) {
case TransportMedium.Websocket:
bind(TYPES.ModelSource).to(ExampleWebsocketDiagramServer).inSingletonScope();
break;
case TransportMedium.LSP:
bind(TYPES.ModelSource).to(VSCodeWebViewDiagramServer).inSingletonScope();
break;
default:
bind(TYPES.ModelSource).to(LocalModelSource).inSingletonScope();
}
rebind(TYPES.ILogger).to(ConsoleLogger).inSingletonScope();
rebind(TYPES.LogLevel).toConstantValue(LogLevel.log);
const context = { bind, unbind, isBound, rebind };
configureModelElement(context, 'graph', SGraph, ExampleGraphView);
configureModelElement(context, 'node:circle', CircularNode, CircleNodeView);
configureModelElement(context, 'node:bean', BeanNode, BeanNodeView);
configureModelElement(context, 'node:channel', BeanNode, ChannelNodeView);
configureModelElement(context, 'node:label', SLabel, SLabelView);
configureModelElement(context, 'compartment', SCompartment, SCompartmentView);
configureModelElement(context, 'edge:straight', /*OrthogonalEgde*/ SEdge, EdgeView);
configureViewerOptions(context, {
needsClientLayout: true,
needsServerLayout: true,
baseDiv: clientId,
});
console.log('di.config.ts : ' + clientId);
});
const container = new Container();
container.load(defaultModule, selectModule, moveModule, boundsModule, undoRedoModule, viewportModule,
exportModule, updateModule, graphModule, routingModule, modelSourceModule, circlegraphModule, hoverModule);
return container;
};

View File

@@ -0,0 +1,71 @@
import {ManhattanEdgeRouter, RectangularNode, SEdge, WebSocketDiagramServer, ActionMessage, DiagramServer} from "sprotty";
import {injectable} from 'inversify';
export class BeanNode extends RectangularNode {
}
export class OrthogonalEgde extends SEdge {
routerKind = ManhattanEdgeRouter.KIND;
}
const SOCKET_MESSAGE_BUFFER = 4000;
const END_MESSAGE = '@end';
@injectable()
export class ExampleWebsocketDiagramServer extends WebSocketDiagramServer {
protected sendMessage(message: ActionMessage): void {
if (this.webSocket) {
const messageStr = JSON.stringify(message);
let offset = 0;
while (offset < messageStr.length) {
this.webSocket.send(messageStr.substring(offset, offset + SOCKET_MESSAGE_BUFFER));
offset += SOCKET_MESSAGE_BUFFER;
}
this.webSocket.send(END_MESSAGE);
} else {
throw new Error('WebSocket is not connected');
}
}
}
@injectable()
export class VSCodeWebViewDiagramServer extends DiagramServer {
protected vscode: any;
listen(vscode: any): void {
console.log('Adding listener!');
window.addEventListener('message', (event: any) => {
const message = event.data; // The json data that the extension sent
if (event.data == 'test') {
console.log('Received TEST message!');
} else {
console.log('Message Received');
this.messageReceived(message);
}
});
this.vscode = vscode;
}
// disconnect() {
// if (this.webSocket) {
// this.webSocket.close();
// this.webSocket = undefined;
// }
// }
//
protected sendMessage(message: ActionMessage): void {
console.log('Send Message');
if (this.vscode) {
this.vscode.postMessage(message);
} else {
throw new Error('No VSCode api object');
}
}
}

View File

@@ -0,0 +1,74 @@
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
declare const acquireVsCodeApi: any;
import {
TYPES, IActionDispatcher,
ModelSource, WebSocketDiagramServer, RequestModelAction
} from "sprotty";
import createContainer, {TransportMedium} from "./di.config";
import * as SockJS from "sockjs-client";
import {VSCodeWebViewDiagramServer} from "./model";
export default function runStandalone() {
const clientId = getOptionFromDom('client-id') || 'sprotty-client';
const transport = getOptionFromDom('transport') || 'websocket';
const container = createContainer(transport=='websocket' ? TransportMedium.Websocket : TransportMedium.LSP, clientId);
const dispatcher = container.get<IActionDispatcher>(TYPES.IActionDispatcher);
// Run
const modelSource = container.get<ModelSource>(TYPES.ModelSource);
if (modelSource instanceof WebSocketDiagramServer) {
const ws = new SockJS('/websocket');
modelSource.listen(ws);
ws.addEventListener('open', () => {
dispatcher.dispatch(requestModelAction());
});
ws.addEventListener('error', (event) => {
console.error(`WebSocket Error: ${event}`)
})
}
if (modelSource instanceof VSCodeWebViewDiagramServer) {
modelSource.listen(acquireVsCodeApi());
dispatcher.dispatch(requestModelAction());
}
// Button features
document.getElementById('refresh')!.addEventListener('click', () => {
dispatcher.dispatch(requestModelAction());
});
function getOptionFromDom(att: string) : string | null {
const appDiv = document.getElementById('sprotty-app');
if (appDiv) {
return appDiv.getAttribute(att);
}
return null;
}
function getTarget() : string {
return getOptionFromDom('target') || 'target-missing';
}
function requestModelAction() : RequestModelAction {
return new RequestModelAction({ 'target' : getTarget()});
}
}

View File

@@ -0,0 +1,130 @@
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
/** @jsx svg */
import { svg } from 'snabbdom-jsx';
import { VNode } from "snabbdom/vnode";
import {
RenderingContext,
SNode,
IView,
SGraphView,
SGraph,
PolylineEdgeView,
SEdge,
Point,
RectangularNodeView,
Selectable,
SPort,
Hoverable,
SShapeElement
} from "sprotty";
import { injectable } from 'inversify';
/**
* A very simple example node consisting of a plain circle.
*/
@injectable()
export class CircleNodeView implements IView {
render(node: SNode, context: RenderingContext): VNode {
const radius = this.getRadius(node);
return <g>
<circle class-sprotty-node={true} class-selected={node.selected} r={radius} cx={radius} cy={radius}></circle>
<text x={radius} y={radius + 7} class-sprotty-text={true}>{node.id.substr(4)}</text>
</g>;
}
protected getRadius(node: SNode) {
return 40;
}
}
@injectable()
export class ExampleGraphView extends SGraphView {
render(model: Readonly<SGraph>, context: RenderingContext): VNode {
const transform = `scale(${model.zoom}) translate(${-model.scroll.x},${-model.scroll.y})`;
return <svg class-sprotty-graph={true}>
<defs>
<marker id="arrow" orient="auto" overflow="visible" markerUnits="userSpaceOnUse">
<path transform="rotate(180)" d="M 10 -5 0 0 10 5 z" />
</marker>
<marker id="dot" viewBox="0 0 10 10" refX="5" refY="5"
markerWidth="5" markerHeight="5">
<circle cx="5" cy="5" r="5" fill="red" />
</marker>
</defs>
<g transform={transform}>
{context.renderChildren(model)}
</g>
</svg>;
}
}
export class EdgeView extends PolylineEdgeView {
protected renderLine(edge: SEdge, segments: Point[], context: RenderingContext): VNode {
const firstPoint = segments[0];
let path = `M ${firstPoint.x},${firstPoint.y}`;
for (let i = 1; i < segments.length; i++) {
const p = segments[i];
path += ` L ${p.x},${p.y}`;
}
const dom: any = <path d={path}/>;
dom.data.attrs['marker-end'] = 'url(#arrow)';
return dom;
}
}
@injectable()
export class BeanNodeView extends RectangularNodeView {
render(node: Readonly<SShapeElement & Hoverable & Selectable>, context: RenderingContext): VNode {
return <g>
<rect class-sprotty-node={node instanceof SNode} class-sprotty-port={node instanceof SPort}
class-mouseover={node.hoverFeedback} class-selected={node.selected}
x="0" y="0" width={Math.max(node.size.width, 0)} height={Math.max(node.size.height, 0)} rx="10" ry="10"></rect>
{context.renderChildren(node)}
</g>;
}
}
@injectable()
export class ChannelNodeView extends RectangularNodeView {
render(node: Readonly<SShapeElement & Hoverable & Selectable>, context: RenderingContext): VNode {
const w = Math.max(node.size.width, 0);
const h = Math.max(node.size.height, 0);
const path = `M 0 ${h} H ${w-8} A 8 ${h/2} 0 0 0 ${w-8} 0 H 0`;
return <g>
<g class-sprotty-node={node instanceof SNode}>
<path d={path}/>
<ellipse cx="0" cy={h/2} rx="8" ry={h/2}/>
</g>
{context.renderChildren(node)}
</g>;
}
}
/*
<g class="shape">'+
'<rect class="border"/>' +
'<path class="the_shape" d="M 0 10 H 100 A 8 10 0 0 1 100 30 H 0"/>'+
'<ellipse class="the_shape" cx="0" cy="20" rx="8" ry="10"/>'+
'<text class="label"/>'+
'<text class="label2"/>'+
'</g>*/

View File

@@ -0,0 +1,33 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": [
"es6", "dom"
],
"types": [
"node", "reflect-metadata",
],
"typeRoots": [
"node_modules/@types", "typings"
],
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"strictNullChecks": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"outDir": "lib",
"jsx": "react"
},
"include": [
"src", "typings"
],
"exclude": [
"**/*.spec.ts", "**/*.spec.tsx"
]
}

View File

@@ -0,0 +1,53 @@
var webpack = require('webpack');
var path = require('path');
var CircularDependencyPlugin = require('circular-dependency-plugin');
module.exports = {
entry: [
'core-js/es6/map',
'core-js/es6/promise',
'core-js/es6/string',
'core-js/es6/symbol',
'./src/app.ts'
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '.')
},
mode: 'development',
devtool: 'source-map',
resolve: {
// Add `.ts` and `.tsx` as a resolvable extension.
extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js']
},
module: {
rules: [
// all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
{
test: /\.tsx?$/,
use: [{
loader: 'ts-loader',
options: {
configFile: path.resolve(__dirname, 'tsconfig.json')
}
}]
},
{
test: /\.css$/,
exclude: /\.useable\.css$/,
loader: 'style-loader!css-loader'
}
]
},
node : { fs: 'empty', net: 'empty' },
plugins: [
new CircularDependencyPlugin({
exclude: /(node_modules|examples)\/./,
failOnError: false
}),
new webpack.WatchIgnorePlugin([
/\.js$/,
/\.d\.ts$/
])
]
};

View File

@@ -0,0 +1,93 @@
<project xmlns="https://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>sprotty-si-view</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-parent</artifactId>
<version>1.11.0-SNAPSHOT</version>
<relativePath>../commons/pom.xml</relativePath>
</parent>
<properties>
<dependencies.version>${project.version}</dependencies.version>
</properties>
<repositories>
<repository>
<id>${project.name}-repo</id>
<url>file://${project.basedir}/repo</url>
</repository>
<repository>
<id>oss-jfrog-snapshots</id>
<url>https://oss.jfrog.org/artifactory/libs-snapshot</url>
<!-- <releases> <enabled>false</enabled> </releases> -->
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.typefox.sprotty/diagram-api
<dependency>
<groupId>io.typefox.sprotty</groupId>
<artifactId>diagram-api</artifactId>
<version>0.4.0</version>
</dependency> -->
<!-- https://mvnrepository.com/artifact/io.typefox.sprotty/diagram-server
<dependency>
<groupId>io.typefox.sprotty</groupId>
<artifactId>diagram-server</artifactId>
<version>0.4.0</version>
</dependency> -->
<!-- Test harness -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-sprotty</artifactId>
<version>${dependencies.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Configure fat jar -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>ZIP</layout>
<classifier>exec</classifier>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,7 @@
package org.springframework.ide.si.view;
import org.springframework.ide.si.view.json.SpringIntegrationGraph;
public interface GraphDataProvider {
SpringIntegrationGraph getGraph();
}

View File

@@ -0,0 +1,329 @@
package org.springframework.ide.si.view;
import org.springframework.ide.si.view.json.SpringIntegrationGraph;
import org.springframework.stereotype.Component;
import com.google.gson.Gson;
@Component
public class MockGraphData implements GraphDataProvider {
public static String data = "{\n" +
"contentDescriptor: {\n" +
"providerVersion: \"5.1.6.RELEASE\",\n" +
"providerFormatVersion: 1,\n" +
"provider: \"spring-integration\"\n" +
"},\n" +
"nodes: [\n" +
"{\n" +
"nodeId: 1,\n" +
"name: \"news\",\n" +
"stats: {\n" +
"countsEnabled: true,\n" +
"statsEnabled: true,\n" +
"loggingEnabled: true,\n" +
"sendCount: 20,\n" +
"sendErrorCount: 0,\n" +
"timeSinceLastSend: 3017590.1150589883,\n" +
"meanSendRate: 0.006416917317469066,\n" +
"meanErrorRate: 0,\n" +
"meanErrorRatio: 0,\n" +
"meanSendDuration: 0.6229622172698512,\n" +
"minSendDuration: 0.36042,\n" +
"maxSendDuration: 5.398724,\n" +
"standardDeviationSendDuration: 0.6248008418287394,\n" +
"sendDuration: {\n" +
"count: 20,\n" +
"min: 0.36042,\n" +
"max: 5.398724,\n" +
"mean: 0.6229622172698512,\n" +
"standardDeviation: 0.6248008418287394,\n" +
"countLong: 20\n" +
"},\n" +
"sendRate: {\n" +
"count: 20,\n" +
"min: 4.519030865997076,\n" +
"max: 5.000686829984188,\n" +
"mean: 0.006416915226255189,\n" +
"standardDeviation: 0.003059182339655441,\n" +
"countLong: 20\n" +
"},\n" +
"errorRate: {\n" +
"count: 0,\n" +
"min: 0,\n" +
"max: 0,\n" +
"mean: 0,\n" +
"standardDeviation: 0,\n" +
"countLong: 0\n" +
"}\n" +
"},\n" +
"componentType: \"channel\",\n" +
"properties: { }\n" +
"},\n" +
"{\n" +
"nodeId: 2,\n" +
"name: \"file\",\n" +
"stats: {\n" +
"countsEnabled: true,\n" +
"statsEnabled: true,\n" +
"loggingEnabled: true,\n" +
"sendCount: 20,\n" +
"sendErrorCount: 0,\n" +
"timeSinceLastSend: 3017591.1391609907,\n" +
"meanSendRate: 0.006416922929073101,\n" +
"meanErrorRate: 0,\n" +
"meanErrorRatio: 0,\n" +
"meanSendDuration: 0.35330047484316307,\n" +
"minSendDuration: 0.208864,\n" +
"maxSendDuration: 2.849803,\n" +
"standardDeviationSendDuration: 0.33755534347541105,\n" +
"sendDuration: {\n" +
"count: 20,\n" +
"min: 0.208864,\n" +
"max: 2.849803,\n" +
"mean: 0.35330047484316307,\n" +
"standardDeviation: 0.33755534347541105,\n" +
"countLong: 20\n" +
"},\n" +
"sendRate: {\n" +
"count: 20,\n" +
"min: 4.516921804994345,\n" +
"max: 5.000899210005999,\n" +
"mean: 0.00641692253069279,\n" +
"standardDeviation: 0.003073929309381288,\n" +
"countLong: 20\n" +
"},\n" +
"errorRate: {\n" +
"count: 0,\n" +
"min: 0,\n" +
"max: 0,\n" +
"mean: 0,\n" +
"standardDeviation: 0,\n" +
"countLong: 0\n" +
"}\n" +
"},\n" +
"componentType: \"channel\",\n" +
"properties: { }\n" +
"},\n" +
"{\n" +
"nodeId: 3,\n" +
"name: \"nullChannel\",\n" +
"stats: {\n" +
"countsEnabled: true,\n" +
"statsEnabled: true,\n" +
"loggingEnabled: true,\n" +
"sendCount: 0,\n" +
"sendErrorCount: 0,\n" +
"timeSinceLastSend: 0,\n" +
"meanSendRate: 0,\n" +
"meanErrorRate: 0,\n" +
"meanErrorRatio: 0,\n" +
"meanSendDuration: 0,\n" +
"minSendDuration: 0,\n" +
"maxSendDuration: 0,\n" +
"standardDeviationSendDuration: 0,\n" +
"sendDuration: {\n" +
"count: 0,\n" +
"min: 0,\n" +
"max: 0,\n" +
"mean: 0,\n" +
"standardDeviation: 0,\n" +
"countLong: 0\n" +
"},\n" +
"sendRate: {\n" +
"count: 0,\n" +
"min: 0,\n" +
"max: 0,\n" +
"mean: 0,\n" +
"standardDeviation: 0,\n" +
"countLong: 0\n" +
"},\n" +
"errorRate: {\n" +
"count: 0,\n" +
"min: 0,\n" +
"max: 0,\n" +
"mean: 0,\n" +
"standardDeviation: 0,\n" +
"countLong: 0\n" +
"}\n" +
"},\n" +
"componentType: \"null-channel\",\n" +
"properties: { }\n" +
"},\n" +
"{\n" +
"nodeId: 4,\n" +
"name: \"errorChannel\",\n" +
"stats: {\n" +
"countsEnabled: true,\n" +
"statsEnabled: true,\n" +
"loggingEnabled: true,\n" +
"sendCount: 0,\n" +
"sendErrorCount: 0,\n" +
"timeSinceLastSend: 0,\n" +
"meanSendRate: 0,\n" +
"meanErrorRate: 0,\n" +
"meanErrorRatio: 0,\n" +
"meanSendDuration: 0,\n" +
"minSendDuration: 0,\n" +
"maxSendDuration: 0,\n" +
"standardDeviationSendDuration: 0,\n" +
"sendDuration: {\n" +
"count: 0,\n" +
"min: 0,\n" +
"max: 0,\n" +
"mean: 0,\n" +
"standardDeviation: 0,\n" +
"countLong: 0\n" +
"},\n" +
"sendRate: {\n" +
"count: 0,\n" +
"min: 0,\n" +
"max: 0,\n" +
"mean: 0,\n" +
"standardDeviation: 0,\n" +
"countLong: 0\n" +
"},\n" +
"errorRate: {\n" +
"count: 0,\n" +
"min: 0,\n" +
"max: 0,\n" +
"mean: 0,\n" +
"standardDeviation: 0,\n" +
"countLong: 0\n" +
"}\n" +
"},\n" +
"componentType: \"publish-subscribe-channel\",\n" +
"properties: { }\n" +
"},\n" +
"{\n" +
"nodeId: 5,\n" +
"name: \"news.adapter\",\n" +
"stats: {\n" +
"messageCount: 20\n" +
"},\n" +
"componentType: \"feed:inbound-channel-adapter\",\n" +
"properties: { },\n" +
"output: \"news\",\n" +
"errors: null\n" +
"},\n" +
"{\n" +
"nodeId: 6,\n" +
"name: \"org.springframework.integration.config.ConsumerEndpointFactoryBean#0\",\n" +
"stats: {\n" +
"activeCount: 0,\n" +
"duration: {\n" +
"count: 20,\n" +
"min: 0.346117,\n" +
"max: 4.516881,\n" +
"mean: 0.585700683679285,\n" +
"standardDeviation: 0.5242315172537475,\n" +
"countLong: 20\n" +
"},\n" +
"countsEnabled: true,\n" +
"statsEnabled: true,\n" +
"loggingEnabled: true,\n" +
"maxDuration: 4.516881,\n" +
"minDuration: 0.346117,\n" +
"meanDuration: 0.585700683679285,\n" +
"errorCount: 0,\n" +
"handleCount: 20,\n" +
"standardDeviationDuration: 0.5242315172537475\n" +
"},\n" +
"componentType: \"transformer\",\n" +
"properties: {\n" +
"expression: \"payload.title + ' @ ' + payload.link + ' '\"\n" +
"},\n" +
"output: \"file\",\n" +
"input: \"news\"\n" +
"},\n" +
"{\n" +
"nodeId: 7,\n" +
"name: \"file.adapter\",\n" +
"stats: {\n" +
"activeCount: 0,\n" +
"duration: {\n" +
"count: 20,\n" +
"min: 0.192626,\n" +
"max: 2.602112,\n" +
"mean: 0.3295736390049931,\n" +
"standardDeviation: 0.31151136752438835,\n" +
"countLong: 20\n" +
"},\n" +
"countsEnabled: true,\n" +
"statsEnabled: true,\n" +
"loggingEnabled: true,\n" +
"maxDuration: 2.602112,\n" +
"minDuration: 0.192626,\n" +
"meanDuration: 0.3295736390049931,\n" +
"errorCount: 0,\n" +
"handleCount: 20,\n" +
"standardDeviationDuration: 0.31151136752438835\n" +
"},\n" +
"componentType: \"message-handler\",\n" +
"properties: { },\n" +
"output: null,\n" +
"input: \"file\"\n" +
"},\n" +
"{\n" +
"nodeId: 8,\n" +
"name: \"_org.springframework.integration.errorLogger\",\n" +
"stats: {\n" +
"activeCount: 0,\n" +
"duration: {\n" +
"count: 0,\n" +
"min: 0,\n" +
"max: 0,\n" +
"mean: 0,\n" +
"standardDeviation: 0,\n" +
"countLong: 0\n" +
"},\n" +
"countsEnabled: true,\n" +
"statsEnabled: true,\n" +
"loggingEnabled: true,\n" +
"maxDuration: 0,\n" +
"minDuration: 0,\n" +
"meanDuration: 0,\n" +
"errorCount: 0,\n" +
"handleCount: 0,\n" +
"standardDeviationDuration: 0\n" +
"},\n" +
"componentType: \"logging-channel-adapter\",\n" +
"properties: { },\n" +
"output: null,\n" +
"input: \"errorChannel\"\n" +
"}\n" +
"],\n" +
"links: [\n" +
"{\n" +
"from: 5,\n" +
"to: 1,\n" +
"type: \"output\"\n" +
"},\n" +
"{\n" +
"from: 1,\n" +
"to: 6,\n" +
"type: \"input\"\n" +
"},\n" +
"{\n" +
"from: 6,\n" +
"to: 2,\n" +
"type: \"output\"\n" +
"},\n" +
"{\n" +
"from: 2,\n" +
"to: 7,\n" +
"type: \"input\"\n" +
"},\n" +
"{\n" +
"from: 4,\n" +
"to: 8,\n" +
"type: \"input\"\n" +
"}\n" +
"]\n" +
"}";
@Override
public SpringIntegrationGraph getGraph() {
return new Gson().fromJson(data, SpringIntegrationGraph.class);
}
}

View File

@@ -0,0 +1,151 @@
package org.springframework.ide.si.view;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.sprotty.Dimension;
import org.eclipse.sprotty.Point;
import org.eclipse.sprotty.RequestModelAction;
import org.eclipse.sprotty.SCompartment;
import org.eclipse.sprotty.SEdge;
import org.eclipse.sprotty.SGraph;
import org.eclipse.sprotty.SLabel;
import org.eclipse.sprotty.SModelElement;
import org.eclipse.sprotty.SModelRoot;
import org.eclipse.sprotty.SNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ide.si.view.json.SpringIntegrationEdge;
import org.springframework.ide.si.view.json.SpringIntegrationGraph;
import org.springframework.ide.si.view.json.SpringIntegrationNode;
import org.springframework.ide.vscode.commons.sprotty.api.DiagramGenerator;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.stereotype.Component;
import com.google.common.collect.ImmutableSet;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@Component
public class SIDiagramGenerator implements DiagramGenerator {
private static final Logger log = LoggerFactory.getLogger(SIDiagramGenerator.class);
private static final ImmutableSet<String> channelTypes = ImmutableSet.of(
"channel",
"publish-subscribe-channel"
);
@Autowired
GraphDataProvider graphDataProvider;
Gson gson = new GsonBuilder().setPrettyPrinting().create();
@Override
public SModelRoot generateModel(String clientId, RequestModelAction modelRequest) {
try {
SpringIntegrationGraph jsonData = graphDataProvider.getGraph();
return toSprottyGraph(jsonData);
} catch (Exception e) {
log.error("", e);
return SModelRoot.EMPTY_ROOT;
}
}
private SGraph toSprottyGraph(SpringIntegrationGraph json) throws Exception {
SGraph graph = new SGraph();
graph.setId("root");
graph.setType("graph");
List<SModelElement> children = new ArrayList<>();
graph.setChildren(children);
Set<String> nodeIds = new HashSet<>();
for (SpringIntegrationNode node : json.getNodes()) {
String id = node.getNodeId()+"";
String name = node.getName();
Assert.isLegal(nodeIds.add(id));
SNode n = createNode(id, name, node.getComponentType());
children.add(n);
}
int linkId = 0;
for (SpringIntegrationEdge link : json.getLinks()) {
String id = "l"+(linkId++);
String sourceId = link.getFrom()+"";
ensureNode(nodeIds, children, sourceId);
String targetId = link.getTo()+"";
ensureNode(nodeIds, children, targetId);
SEdge e = createEdge(id, sourceId, targetId);
children.add(e);
}
//
// LiveBeansModel beansModel = app.getBeans();
// for (String targetBeanId : beansModel.getBeanNames()) {
// for (LiveBean bean : beansModel.getBeansOfName(targetBeanId)) {
// graphChildren.add(createBean(bean.getId(), bean.getShortName(), new Point(), new Dimension()));
// }
// for (LiveBean sourceBean : beansModel.getBeansDependingOn(targetBeanId)) {
// graphChildren.add(createEdge(sourceBean.getId() + " " + targetBeanId, sourceBean.getId(), targetBeanId));
// }
// }
return graph;
}
private void ensureNode(Set<String> nodeIds, List<SModelElement> children, String sourceId) {
if (!nodeIds.contains(sourceId)) {
nodeIds.add(sourceId);
children.add(createNode(sourceId, "?"+sourceId, "unknown"));
}
}
private static SNode createNode(String id, String labelText, String type) {
SNode node = new SNode();
node.setId(id);
node.setType("node:"+visualType(type));
node.setLayout("vbox");
node.setPosition(new Point(Math.random() * 1024, Math.random() * 768));
node.setSize(new Dimension(80, 80));
node.setChildren(new ArrayList<>());
SCompartment compartment = new SCompartment();
compartment.setId(id + "-comp");
compartment.setType("compartment");
compartment.setLayout("hbox");
compartment.setChildren(new ArrayList<>());
SLabel label = new SLabel();
label.setId(id + "-lanbel");
label.setType("node:label");
label.setText(labelText);
compartment.getChildren().add(label);
node.getChildren().add(compartment);
return node;
}
private static String visualType(String type) {
if (channelTypes.contains(type)) {
return "channel";
} else {
return "bean";
}
}
private static SEdge createEdge(String id, String sourceId, String targetId) {
SEdge edge = new SEdge();
edge.setId(id);
edge.setType("edge:straight");
edge.setSourceId(sourceId);
edge.setTargetId(targetId);
return edge;
}
}

View File

@@ -0,0 +1,15 @@
package org.springframework.ide.si.view;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SprottySiViewApplication {
public static void main(String[] args) {
SpringApplication.run(SprottySiViewApplication.class, args);
}
}

View File

@@ -0,0 +1,13 @@
package org.springframework.ide.si.view.json;
import com.google.gson.GsonBuilder;
public class PrettyJson {
@Override
public String toString() {
return new GsonBuilder().setPrettyPrinting().create().toJson(this);
}
}

View File

@@ -0,0 +1,27 @@
package org.springframework.ide.si.view.json;
public class SpringIntegrationEdge extends PrettyJson {
private int from;
private int to;
private String type;
public int getFrom() {
return from;
}
public void setFrom(int from) {
this.from = from;
}
public int getTo() {
return to;
}
public void setTo(int to) {
this.to = to;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@@ -0,0 +1,30 @@
package org.springframework.ide.si.view.json;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
public class SpringIntegrationGraph extends PrettyJson {
private JsonObject contentDescriptor;
private SpringIntegrationNode[] nodes;
private SpringIntegrationEdge[] links;
public JsonObject getContentDescriptor() {
return contentDescriptor;
}
public void setContentDescriptor(JsonObject contentDescriptor) {
this.contentDescriptor = contentDescriptor;
}
public SpringIntegrationNode[] getNodes() {
return nodes;
}
public void setNodes(SpringIntegrationNode[] nodes) {
this.nodes = nodes;
}
public SpringIntegrationEdge[] getLinks() {
return links;
}
public void setLinks(SpringIntegrationEdge[] links) {
this.links = links;
}
}

View File

@@ -0,0 +1,43 @@
package org.springframework.ide.si.view.json;
import com.google.gson.JsonObject;
public class SpringIntegrationNode extends PrettyJson {
private int nodeId;
private String name;
private JsonObject stats;
private String componentType;
private JsonObject properties;
public int getNodeId() {
return nodeId;
}
public void setNodeId(int nodeId) {
this.nodeId = nodeId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public JsonObject getStats() {
return stats;
}
public void setStats(JsonObject stats) {
this.stats = stats;
}
public String getComponentType() {
return componentType;
}
public void setComponentType(String componentType) {
this.componentType = componentType;
}
public JsonObject getProperties() {
return properties;
}
public void setProperties(JsonObject properties) {
this.properties = properties;
}
}

View File

@@ -0,0 +1,2 @@
server.port=8877
spring.main.banner-mode=off

View File

@@ -0,0 +1,16 @@
package com.example.demo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SprottySiViewApplicationTests {
@Test
public void contextLoads() {
}
}