You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ea...@apache.org on 2018/06/25 21:26:54 UTC
[8/8] qpid-dispatch git commit: DISPATCH-1049 Add console tests
DISPATCH-1049 Add console tests
Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/b5deb035
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/b5deb035
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/b5deb035
Branch: refs/heads/master
Commit: b5deb03579a7dedd81a56f32baa3e5f4b5b57136
Parents: af99754
Author: Ernest Allen <ea...@redhat.com>
Authored: Mon Jun 25 17:25:50 2018 -0400
Committer: Ernest Allen <ea...@redhat.com>
Committed: Mon Jun 25 17:25:50 2018 -0400
----------------------------------------------------------------------
.gitignore | 2 +-
console/CMakeLists.txt | 207 +-
console/stand-alone/.babelrc | 21 +
console/stand-alone/gulpfile.js | 101 +-
console/stand-alone/index.html | 11 +-
console/stand-alone/main.js | 254 ++
console/stand-alone/modules/connection.js | 347 +++
console/stand-alone/modules/correlator.js | 50 +
console/stand-alone/modules/management.js | 63 +
console/stand-alone/modules/topology.js | 403 +++
console/stand-alone/modules/utilities.js | 115 +
console/stand-alone/package-lock.json | 2519 ++++++++++++++----
console/stand-alone/package.json | 24 +-
console/stand-alone/plugin/css/dispatch.css | 4 +-
console/stand-alone/plugin/html/qdrList.html | 53 +-
.../plugin/html/tmplChartConfig.html | 12 +-
console/stand-alone/plugin/js/chord/data.js | 234 +-
console/stand-alone/plugin/js/chord/filters.js | 52 +-
.../plugin/js/chord/layout/layout.js | 6 +-
console/stand-alone/plugin/js/chord/matrix.js | 3 +-
console/stand-alone/plugin/js/chord/qdrChord.js | 24 +-
.../plugin/js/chord/ribbon/ribbon.js | 4 +-
console/stand-alone/plugin/js/dispatchPlugin.js | 264 --
.../stand-alone/plugin/js/dlgChartController.js | 31 +-
console/stand-alone/plugin/js/navbar.js | 140 +-
.../stand-alone/plugin/js/posintDirective.js | 75 +
.../stand-alone/plugin/js/qdrChartService.js | 1607 ++++++-----
console/stand-alone/plugin/js/qdrCharts.js | 33 +-
console/stand-alone/plugin/js/qdrGlobals.js | 58 +-
console/stand-alone/plugin/js/qdrList.js | 1732 ++++++------
console/stand-alone/plugin/js/qdrListChart.js | 146 -
console/stand-alone/plugin/js/qdrOverview.js | 85 +-
.../plugin/js/qdrOverviewChartsController.js | 18 +-
.../plugin/js/qdrOverviewLogsController.js | 25 +-
console/stand-alone/plugin/js/qdrSchema.js | 22 +-
console/stand-alone/plugin/js/qdrService.js | 317 +--
console/stand-alone/plugin/js/qdrSettings.js | 89 +-
.../plugin/js/qdrTopAddressesController.js | 16 +-
console/stand-alone/plugin/js/topology/links.js | 216 ++
console/stand-alone/plugin/js/topology/nodes.js | 162 ++
.../plugin/js/topology/qdrTopology.js | 2512 +++++++----------
.../stand-alone/plugin/js/topology/topoUtils.js | 225 ++
.../stand-alone/plugin/js/topology/traffic.js | 445 ++++
.../stand-alone/plugin/js/topology/traffic.ts | 443 ---
console/stand-alone/test/filter.js | 73 +
console/stand-alone/test/links.js | 82 +
console/stand-alone/test/matrix.js | 51 +
console/stand-alone/test/nodes.json | 1 +
console/stand-alone/test/utilities.js | 192 ++
console/stand-alone/tsconfig.json | 23 +-
console/stand-alone/vendor-js.txt | 12 +-
51 files changed, 8155 insertions(+), 5449 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 49a9c44..d7c32e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,7 +12,7 @@ tests/policy-1/policy-*.json
.metadata
.settings
console/test/topolgies/config-*
-.history
+.history/
.tox
.vscode
console/stand-alone/node_modules/
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/console/CMakeLists.txt b/console/CMakeLists.txt
index bfd9cd8..a55b572 100644
--- a/console/CMakeLists.txt
+++ b/console/CMakeLists.txt
@@ -20,101 +20,122 @@
##
## Add cmake option to choose whether to install stand-alone console
##
-option(CONSOLE_INSTALL "Build and install console (requires npm)" ON)
+option(CONSOLE_INSTALL "Build and install console (requires npm 5.2+)" ON)
if(CONSOLE_INSTALL)
- find_program(NPX_EXE npx DOC "Location of the npx task runner")
- if (NPX_EXE)
-
- set(CONSOLE_SOURCE_DIR "${CMAKE_SOURCE_DIR}/console/stand-alone")
- set(CONSOLE_BUILD_DIR "${CMAKE_BINARY_DIR}/console")
-
- ## Files needed to create the ${CONSOLE_ARTIFACTS}
- file (GLOB_RECURSE CONSOLE_JS_SOURCES ${CONSOLE_SOURCE_DIR}/plugin/js/*.js)
- file (GLOB_RECURSE CONSOLE_TS_SOURCES ${CONSOLE_SOURCE_DIR}/plugin/js/*.ts)
- set(CONSOLE_CSS_SOURCE ${CONSOLE_SOURCE_DIR}/plugin/css/dispatch.css)
- set(ALL_CONSOLE_SOURCES ${CONSOLE_JS_SOURCES} ${CONSOLE_TS_SOURCES} ${CONSOLE_CSS_SOURCE})
-
- ## Files created during the console build
- set(CONSOLE_ARTIFACTS
- ${CONSOLE_BUILD_DIR}/dist/js/dispatch.min.js
- ${CONSOLE_BUILD_DIR}/dist/js/vendor.min.js
- ${CONSOLE_BUILD_DIR}/dist/css/dispatch.min.css
- ${CONSOLE_BUILD_DIR}/dist/css/vendor.min.css
- )
-
- ## copy the build config files
- configure_file( ${CONSOLE_SOURCE_DIR}/package.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
- configure_file( ${CONSOLE_SOURCE_DIR}/package-lock.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
- configure_file( ${CONSOLE_SOURCE_DIR}/tslint.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
- configure_file( ${CONSOLE_SOURCE_DIR}/gulpfile.js ${CONSOLE_BUILD_DIR}/ COPYONLY)
- configure_file( ${CONSOLE_SOURCE_DIR}/vendor-js.txt ${CONSOLE_BUILD_DIR}/ COPYONLY)
- configure_file( ${CONSOLE_SOURCE_DIR}/vendor-css.txt ${CONSOLE_BUILD_DIR}/ COPYONLY)
-
- ## Tell cmake how and when to build ${CONSOLE_ARTIFACTS}
- add_custom_command (
- OUTPUT ${CONSOLE_ARTIFACTS}
- COMMENT "Running console build"
- COMMAND npm install --loglevel=error
- COMMAND ${NPX_EXE} gulp --src ${CONSOLE_SOURCE_DIR}
- DEPENDS ${ALL_CONSOLE_SOURCES}
- WORKING_DIRECTORY ${CONSOLE_BUILD_DIR}/
- )
-
- ## Ensure ${CONSOLE_ARTIFACTS} is built on a make when needed
- add_custom_target(console ALL
- DEPENDS ${CONSOLE_ARTIFACTS}
- )
-
- ##
- ## Install the static and built console files
- ##
-
- ## Files copied to the root of the console's install dir
- set(BASE_FILES
- ${CONSOLE_SOURCE_DIR}/index.html
- ${CONSOLE_SOURCE_DIR}/favicon-32x32.png
- )
- ## Files copied to the css/ dir
- set(CSS_FONTS
- ${CONSOLE_SOURCE_DIR}/plugin/css/brokers.ttf
- ${CONSOLE_BUILD_DIR}/node_modules/angular-ui-grid/ui-grid.woff
- ${CONSOLE_BUILD_DIR}/node_modules/angular-ui-grid/ui-grid.ttf
- )
- ## Files copied to the fonts/ dir
- set(VENDOR_FONTS
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Regular-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Light-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Semibold-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-BoldItalic-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Italic-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/fontawesome-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/fontawesome-webfont.eot
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.ttf
- ${CONSOLE_BUILD_DIR}/node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2
- )
-
- install(DIRECTORY ${CONSOLE_BUILD_DIR}/dist/
- DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}
- PATTERN "*.map" EXCLUDE
- )
- install(DIRECTORY ${CONSOLE_SOURCE_DIR}/plugin/html/
- DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/html
- FILES_MATCHING PATTERN "*.html"
- )
- install(FILES ${BASE_FILES}
- DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}
- )
- install(FILES ${CSS_FONTS}
- DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/css/
- )
- install(FILES ${VENDOR_FONTS}
- DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/fonts/
- )
- else(NPX_EXE)
- message(STATUS "Cannot build console, npm not found")
- endif(NPX_EXE)
+ find_program (NPM_EXECUTABLE npm DOC "Location of npm package manager")
+
+ if (NPM_EXECUTABLE)
+ execute_process(COMMAND ${NPM_EXECUTABLE} --version
+ OUTPUT_VARIABLE NPM_VERSION)
+ if(${NPM_VERSION} VERSION_EQUAL "5.2.0" OR ${NPM_VERSION} VERSION_GREATER "5.2.0")
+
+ find_program(NPX_EXE npx DOC "Location of the npx task runner")
+ if (NPX_EXE)
+
+ set(CONSOLE_SOURCE_DIR "${CMAKE_SOURCE_DIR}/console/stand-alone")
+ set(CONSOLE_BUILD_DIR "${CMAKE_BINARY_DIR}/console")
+
+ ## Files needed to create the ${CONSOLE_ARTIFACTS}
+ file (GLOB_RECURSE CONSOLE_JS_SOURCES ${CONSOLE_SOURCE_DIR}/plugin/js/*.js)
+ file (GLOB_RECURSE CONSOLE_TS_SOURCES ${CONSOLE_SOURCE_DIR}/plugin/js/*.ts)
+ file (GLOB_RECURSE CONSOLE_MODULE_SOURCES ${CONSOLE_SOURCE_DIR}/modules/*.js)
+ set(CONSOLE_CSS_SOURCE ${CONSOLE_SOURCE_DIR}/plugin/css/dispatch.css)
+ set(CONSOLE_MAIN ${CONSOLE_SOURCE_DIR}/main.js)
+ set(ALL_CONSOLE_SOURCES ${CONSOLE_MAIN} ${CONSOLE_MODULE_SOURCES} ${CONSOLE_JS_SOURCES} ${CONSOLE_TS_SOURCES} ${CONSOLE_CSS_SOURCE})
+
+ ## Files created during the console build
+ set(CONSOLE_ARTIFACTS
+ ${CONSOLE_BUILD_DIR}/dist/js/main.min.js
+ ${CONSOLE_BUILD_DIR}/dist/js/vendor.min.js
+ ${CONSOLE_BUILD_DIR}/dist/css/dispatch.min.css
+ ${CONSOLE_BUILD_DIR}/dist/css/vendor.min.css
+ )
+
+ ## copy the build config files
+ configure_file( ${CONSOLE_SOURCE_DIR}/package.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
+ configure_file( ${CONSOLE_SOURCE_DIR}/package-lock.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
+ configure_file( ${CONSOLE_SOURCE_DIR}/tslint.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
+ configure_file( ${CONSOLE_SOURCE_DIR}/gulpfile.js ${CONSOLE_BUILD_DIR}/ COPYONLY)
+ configure_file( ${CONSOLE_SOURCE_DIR}/vendor-js.txt ${CONSOLE_BUILD_DIR}/ COPYONLY)
+ configure_file( ${CONSOLE_SOURCE_DIR}/vendor-css.txt ${CONSOLE_BUILD_DIR}/ COPYONLY)
+
+ ## Tell cmake how and when to build ${CONSOLE_ARTIFACTS}
+ add_custom_command (
+ OUTPUT ${CONSOLE_ARTIFACTS}
+ COMMENT "Running console build"
+ COMMAND npm install --loglevel=error
+ COMMAND ${NPX_EXE} gulp --src ${CONSOLE_SOURCE_DIR} --build "production"
+ DEPENDS ${ALL_CONSOLE_SOURCES}
+ WORKING_DIRECTORY ${CONSOLE_BUILD_DIR}/
+ )
+
+ ## Ensure ${CONSOLE_ARTIFACTS} is built on a make when needed
+ add_custom_target(console ALL
+ DEPENDS ${CONSOLE_ARTIFACTS}
+ )
+
+ ##
+ ## Install the static and built console files
+ ##
+
+ ## Files copied to the root of the console's install dir
+ set(BASE_FILES
+ ${CONSOLE_SOURCE_DIR}/index.html
+ ${CONSOLE_SOURCE_DIR}/favicon-32x32.png
+ )
+ ## Files copied to the css/ dir
+ set(CSS_FONTS
+ ${CONSOLE_SOURCE_DIR}/plugin/css/brokers.ttf
+ )
+ ## Files copied to the css/fonts/ dir
+ set(CSSFONTS_FONTS
+ ${CONSOLE_BUILD_DIR}/node_modules/angular-ui-grid/fonts/ui-grid.woff
+ ${CONSOLE_BUILD_DIR}/node_modules/angular-ui-grid/fonts/ui-grid.ttf
+ )
+ ## Files copied to the fonts/ dir
+ set(VENDOR_FONTS
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Regular-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Light-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Semibold-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-SemiboldItalic-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-BoldItalic-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Italic-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/fontawesome-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/fontawesome-webfont.eot
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.ttf
+ ${CONSOLE_BUILD_DIR}/node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2
+ )
+
+ install(DIRECTORY ${CONSOLE_BUILD_DIR}/dist/
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}
+ PATTERN "*.map" EXCLUDE
+ )
+ install(DIRECTORY ${CONSOLE_SOURCE_DIR}/plugin/html/
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/html
+ FILES_MATCHING PATTERN "*.html"
+ )
+ install(FILES ${BASE_FILES}
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}
+ )
+ install(FILES ${CSS_FONTS}
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/css/
+ )
+ install(FILES ${CSSFONTS_FONTS}
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/css/fonts/
+ )
+ install(FILES ${VENDOR_FONTS}
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/fonts/
+ )
+ else(NPX_EXE)
+ message(STATUS "Cannot build console, npx not found.")
+ endif(NPX_EXE)
+ else(${NPM_VERSION} VERSION_EQUAL "5.2.0" OR ${NPM_VERSION} VERSION_GREATER "5.2.0")
+ message(STATUS "Cannot build console. npm version 5.2 or greater is required.")
+ endif(${NPM_VERSION} VERSION_EQUAL "5.2.0" OR ${NPM_VERSION} VERSION_GREATER "5.2.0")
+ endif(NPM_EXECUTABLE)
+
endif(CONSOLE_INSTALL)
##
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/.babelrc
----------------------------------------------------------------------
diff --git a/console/stand-alone/.babelrc b/console/stand-alone/.babelrc
new file mode 100644
index 0000000..d67a300
--- /dev/null
+++ b/console/stand-alone/.babelrc
@@ -0,0 +1,21 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+*/
+{
+ "presets": ["es2015"]
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/gulpfile.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/gulpfile.js b/console/stand-alone/gulpfile.js
index 46531c3..0ddaf10 100644
--- a/console/stand-alone/gulpfile.js
+++ b/console/stand-alone/gulpfile.js
@@ -19,20 +19,26 @@ under the License.
`;
const gulp = require('gulp'),
- babel = require('gulp-babel'),
+ mocha = require('gulp-mocha'),
+ gulpif = require('gulp-if'),
+ rollup = require('rollup-stream'),
+ source = require('vinyl-source-stream'),
+ buffer = require('vinyl-buffer'),
concat = require('gulp-concat'),
uglify = require('gulp-uglify'),
+ terser = require('gulp-terser'),
+ babel = require('gulp-babel'),
ngAnnotate = require('gulp-ng-annotate'),
- rename = require('gulp-rename'),
cleanCSS = require('gulp-clean-css'),
del = require('del'),
eslint = require('gulp-eslint'),
maps = require('gulp-sourcemaps'),
insert = require('gulp-insert'),
+ rename = require('gulp-rename'),
fs = require('fs'),
tsc = require('gulp-typescript'),
- tslint = require('gulp-tslint');
- //tsProject = tsc.createProject('tsconfig.json');
+ tslint = require('gulp-tslint'),
+ through = require('through2');
// temp directory for converted typescript files
const built_ts = 'built_ts';
@@ -59,6 +65,7 @@ const arg = (argList => {
})(process.argv);
var src = arg.src ? arg.src + '/' : '';
+var production = (arg.build === 'production');
const paths = {
typescript: {
@@ -70,10 +77,14 @@ const paths = {
dest: 'dist/css/'
},
scripts: {
- src: [src + 'plugin/js/**/*.js', built_ts + '/**/*.js'],
+ src: [src + 'plugin/js/**/*.js'],
dest: 'dist/js/'
}
};
+var touch = through.obj(function(file, enc, done) {
+ var now = new Date;
+ fs.utimes(file.path, now, now, done);
+});
function clean() {
return del(['dist',built_ts ]);
@@ -110,20 +121,6 @@ function vendor_styles() {
.pipe(gulp.dest(paths.styles.dest));
}
-function scripts() {
- return gulp.src(paths.scripts.src, { sourcemaps: true })
- .pipe(babel({
- presets: [require.resolve('babel-preset-env')]
- }))
- .pipe(ngAnnotate())
- .pipe(maps.init())
- .pipe(uglify())
- .pipe(concat('dispatch.min.js'))
- .pipe(insert.prepend(license))
- .pipe(maps.write('./'))
- .pipe(gulp.dest(paths.scripts.dest));
-}
-
function vendor_scripts() {
var vendor_lines = fs.readFileSync('vendor-js.txt').toString().split('\n');
var vendor_files = vendor_lines.filter( function (line) {
@@ -134,7 +131,8 @@ function vendor_scripts() {
.pipe(uglify())
.pipe(concat('vendor.min.js'))
.pipe(maps.write('./'))
- .pipe(gulp.dest(paths.scripts.dest));
+ .pipe(gulp.dest(paths.scripts.dest))
+ .pipe(touch);
}
function watch() {
gulp.watch(paths.scripts.src, scripts);
@@ -167,10 +165,68 @@ function ts_lint() {
.pipe(tslint.report());
}
+function scripts() {
+ return rollup({
+ input: src + './main.js',
+ sourcemap: true,
+ format: 'es'
+ }).on('error', e => {
+ console.error(`${e.stack}`);
+ })
+
+ // point to the entry file and gives the name of the output file.
+ .pipe(source('main.min.js', src))
+
+ // buffer the output. most gulp plugins, including gulp-sourcemaps, don't support streams.
+ .pipe(buffer())
+
+ // tell gulp-sourcemaps to load the inline sourcemap produced by rollup-stream.
+ .pipe(maps.init({loadMaps: true}))
+ // transform the code further here.
+ /*
+ .pipe(babel(
+ {presets: [
+ ['env', {
+ targets: {
+ 'browsers': [
+ 'Chrome >= 52',
+ 'FireFox >= 44',
+ 'Safari >= 7',
+ 'Explorer 11',
+ 'last 4 Edge versions'
+ ]
+ },
+ useBuiltIns: true,
+ //debug: true
+ }],
+ 'es2015'
+ ],
+ 'ignore': [
+ 'node_modules'
+ ]
+ }
+ ))
+ */
+ .pipe(ngAnnotate())
+ //.pipe(gulpif(production, uglify()))
+ .pipe(gulpif(production, terser()))
+ .pipe(gulpif(production, insert.prepend(license)))
+ // write the sourcemap alongside the output file.
+ .pipe(maps.write('.'))
+
+ // and output to ./dist/main.js as normal.
+ .pipe(gulp.dest(paths.scripts.dest));
+}
+
+function test () {
+ return gulp.src(['test/**/*.js'], {read: false})
+ .pipe(mocha({require: ['babel-core/register'], exit: true}))
+ .on('error', console.error);
+}
+
var build = gulp.series(
clean, // removes the dist/ dir
- gulp.parallel(lint, ts_lint), // lints the .js, .ts files
- typescript, // converts .ts to .js
+ lint, // lints the .js
gulp.parallel(vendor_styles, vendor_scripts, styles, scripts), // uglify and concat
cleanup // remove .js that were converted from .ts
);
@@ -186,5 +242,6 @@ exports.tsc = typescript;
exports.scripts = scripts;
exports.styles = styles;
exports.vendor = vendor;
+exports.test = test;
gulp.task('default', build);
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/index.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/index.html b/console/stand-alone/index.html
index 68070a3..1f08594 100644
--- a/console/stand-alone/index.html
+++ b/console/stand-alone/index.html
@@ -20,7 +20,6 @@ under the License.
<html xmlns:ng="https://angularjs.org">
<head>
-
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
@@ -76,9 +75,15 @@ under the License.
</div>
</div>
+<!-- <script type="module" src="js/main.min.js"></script> -->
<script type="text/javascript" src="js/vendor.min.js"></script>
-<script type="text/javascript" src="js/dispatch.min.js"></script>
-
+<script type="text/javascript" src="js/main.min.js"></script>
+<script defer nomodule>
+ var installError = document.getElementById('installError');
+ if (installError) {
+ installError.innerHTML = 'This browser is not supported because it does not support es-2015 modules. <a href="https://www.ecma-international.org/ecma-262/6.0/">https://www.ecma-international.org/ecma-262/6.0/</a><br/>Please use a different browser.';
+ }
+</script>
<script>
// If angular hasn't loaded a page after 1 second, display the error message
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/main.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/main.js b/console/stand-alone/main.js
new file mode 100644
index 0000000..ca69709
--- /dev/null
+++ b/console/stand-alone/main.js
@@ -0,0 +1,254 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+*/
+/* global angular d3 */
+
+/**
+ * @module QDR
+ * @main QDR
+ *
+ * The main entry point for the QDR module
+ *
+ */
+
+//import angular from 'angular';
+import { QDRLogger, QDRTemplatePath, QDR_LAST_LOCATION } from './plugin/js/qdrGlobals.js';
+import { QDRService } from './plugin/js/qdrService.js';
+import { QDRChartService } from './plugin/js/qdrChartService.js';
+import { NavBarController } from './plugin/js/navbar.js';
+import { OverviewController } from './plugin/js/qdrOverview.js';
+import { OverviewChartsController } from './plugin/js/qdrOverviewChartsController.js';
+import { OverviewLogsController } from './plugin/js/qdrOverviewLogsController.js';
+import { TopologyController } from './plugin/js/topology/qdrTopology.js';
+import { ChordController } from './plugin/js/chord/qdrChord.js';
+import { ListController } from './plugin/js/qdrList.js';
+import { TopAddressesController } from './plugin/js/qdrTopAddressesController.js';
+import { ChartDialogController } from './plugin/js/dlgChartController.js';
+import { SettingsController } from './plugin/js/qdrSettings.js';
+import { SchemaController } from './plugin/js/qdrSchema.js';
+import { ChartsController } from './plugin/js/qdrCharts.js';
+import { posint } from './plugin/js/posintDirective.js';
+
+(function(QDR) {
+
+ /**
+ * This plugin's angularjs module instance
+ */
+ QDR.module = angular.module('QDR', ['ngRoute', 'ngSanitize', 'ngResource', 'ui.bootstrap',
+ 'ui.grid', 'ui.grid.selection', 'ui.grid.autoResize', 'ui.grid.resizeColumns', 'ui.grid.saveState',
+ 'ui.slider', 'ui.checkbox']);
+
+ // set up the routing for this plugin
+ QDR.module.config(function($routeProvider) {
+ $routeProvider
+ .when('/', {
+ templateUrl: QDRTemplatePath + 'qdrOverview.html'
+ })
+ .when('/overview', {
+ templateUrl: QDRTemplatePath + 'qdrOverview.html'
+ })
+ .when('/topology', {
+ templateUrl: QDRTemplatePath + 'qdrTopology.html'
+ })
+ .when('/list', {
+ templateUrl: QDRTemplatePath + 'qdrList.html'
+ })
+ .when('/schema', {
+ templateUrl: QDRTemplatePath + 'qdrSchema.html'
+ })
+ .when('/charts', {
+ templateUrl: QDRTemplatePath + 'qdrCharts.html'
+ })
+ .when('/chord', {
+ templateUrl: QDRTemplatePath + 'qdrChord.html'
+ })
+ .when('/connect', {
+ templateUrl: QDRTemplatePath + 'qdrConnect.html'
+ });
+ });
+
+ QDR.module.config(function ($compileProvider) {
+ $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension|file|blob):/);
+ $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension):/);
+ });
+
+ QDR.module.filter('to_trusted', ['$sce', function($sce){
+ return function(text) {
+ return $sce.trustAsHtml(text);
+ };
+ }]);
+
+ QDR.module.filter('humanify', ['QDRService', function (QDRService) {
+ return function (input) {
+ return QDRService.utilities.humanify(input);
+ };
+ }]);
+
+ QDR.module.filter('Pascalcase', function () {
+ return function (str) {
+ if (!str)
+ return '';
+ return str.replace(/(\w)(\w*)/g,
+ function(g0,g1,g2){return g1.toUpperCase() + g2.toLowerCase();});
+ };
+ });
+
+ QDR.module.filter('safePlural', function () {
+ return function (str) {
+ var es = ['x', 'ch', 'ss', 'sh'];
+ for (var i=0; i<es.length; ++i) {
+ if (str.endsWith(es[i]))
+ return str + 'es';
+ }
+ if (str.endsWith('y'))
+ return str.substr(0, str.length-2) + 'ies';
+ if (str.endsWith('s'))
+ return str;
+ return str + 's';
+ };
+ });
+
+ QDR.module.filter('pretty', function () {
+ return function (str) {
+ var formatComma = d3.format(',');
+ if (!isNaN(parseFloat(str)) && isFinite(str))
+ return formatComma(str);
+ return str;
+ };
+ });
+
+ // one-time initialization happens in the run function
+ // of our module
+ QDR.module.run( ['$rootScope', '$route', '$timeout', '$location', '$log', 'QDRService', 'QDRChartService', function ($rootScope, $route, $timeout, $location, $log, QDRService, QDRChartService) {
+ let QDRLog = new QDRLogger($log, 'main');
+ QDRLog.info('************* creating Dispatch Console ************');
+
+ var curPath = $location.path();
+ var org = curPath.substr(1);
+ if (org && org.length > 0 && org !== 'connect') {
+ $location.search('org', org);
+ } else {
+ $location.search('org', null);
+ }
+ QDR.queue = d3.queue;
+
+ if (!QDRService.management.connection.is_connected()) {
+ // attempt to connect to the host:port that served this page
+ var host = $location.host();
+ var port = $location.port();
+ var search = $location.search();
+ if (search.org) {
+ if (search.org === 'connect')
+ $location.search('org', 'overview');
+ }
+ var connectOptions = {address: host, port: port};
+ QDRLog.info('Attempting AMQP over websockets connection using address:port of browser ('+host+':'+port+')');
+ QDRService.management.connection.testConnect(connectOptions)
+ .then( function () {
+ // We didn't connect with reconnect: true flag.
+ // The reason being that if we used reconnect:true and the connection failed, rhea would keep trying. There
+ // doesn't appear to be a way to tell it to stop trying to reconnect.
+ QDRService.disconnect();
+ QDRLog.info('Connect succeeded. Using address:port of browser');
+ connectOptions.reconnect = true;
+ // complete the connection (create the sender/receiver)
+ QDRService.connect(connectOptions)
+ .then( function () {
+ // register a callback for when the node list is available (needed for loading saved charts)
+ QDRService.management.topology.addUpdatedAction('initChartService', function() {
+ QDRService.management.topology.delUpdatedAction('initChartService');
+ QDRChartService.init(); // initialize charting service after we are connected
+ });
+ // get the list of nodes
+ QDRService.management.topology.startUpdating(false);
+ });
+ }, function () {
+ QDRLog.info('failed to auto-connect to ' + host + ':' + port);
+ QDRLog.info('redirecting to connect page');
+ $timeout(function () {
+ $location.path('/connect');
+ $location.search('org', org);
+ $location.replace();
+ });
+ });
+ }
+
+ $rootScope.$on('$routeChangeSuccess', function() {
+ var path = $location.path();
+ if (path !== '/connect') {
+ localStorage[QDR_LAST_LOCATION] = path;
+ }
+ });
+ }]);
+
+ QDR.module.controller ('QDR.MainController', ['$scope', '$log', '$location', function ($scope, $log, $location) {
+ let QDRLog = new QDRLogger($log, 'MainController');
+ QDRLog.debug('started QDR.MainController with location.url: ' + $location.url());
+ QDRLog.debug('started QDR.MainController with window.location.pathname : ' + window.location.pathname);
+ $scope.topLevelTabs = [];
+ $scope.topLevelTabs.push({
+ id: 'qdr',
+ content: 'Qpid Dispatch Router Console',
+ title: 'Dispatch Router Console',
+ isValid: function() { return true; },
+ href: function() { return '#connect'; },
+ isActive: function() { return true; }
+ });
+ }]);
+
+ QDR.module.controller ('QDR.Core', function ($scope, $rootScope) {
+ $scope.alerts = [];
+ $scope.breadcrumb = {};
+ $scope.closeAlert = function(index) {
+ $scope.alerts.splice(index, 1);
+ };
+ $scope.$on('setCrumb', function(event, data) {
+ $scope.breadcrumb = data;
+ });
+ $scope.$on('newAlert', function(event, data) {
+ $scope.alerts.push(data);
+ $scope.$apply();
+ });
+ $scope.$on('clearAlerts', function () {
+ $scope.alerts = [];
+ $scope.$apply();
+ });
+ $scope.pageMenuClicked = function () {
+ $rootScope.$broadcast('pageMenuClicked');
+ };
+ });
+
+ QDR.module.controller('QDR.NavBarController', NavBarController);
+ QDR.module.controller('QDR.OverviewController', OverviewController);
+ QDR.module.controller('QDR.OverviewChartsController', OverviewChartsController);
+ QDR.module.controller('QDR.OverviewLogsController', OverviewLogsController);
+ QDR.module.controller('QDR.TopAddressesController', TopAddressesController);
+ QDR.module.controller('QDR.ChartDialogController', ChartDialogController);
+ QDR.module.controller('QDR.SettingsController', SettingsController);
+ QDR.module.controller('QDR.TopologyController', TopologyController);
+ QDR.module.controller('QDR.ChordController', ChordController);
+ QDR.module.controller('QDR.ListController', ListController);
+ QDR.module.controller('QDR.SchemaController', SchemaController);
+ QDR.module.controller('QDR.ChartsController', ChartsController);
+
+ QDR.module.service('QDRService', QDRService);
+ QDR.module.service('QDRChartService', QDRChartService);
+ QDR.module.directive('posint', posint);
+ // .directive('exampleDirective', () => new ExampleDirective);
+}({}));
+
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/modules/connection.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/modules/connection.js b/console/stand-alone/modules/connection.js
new file mode 100644
index 0000000..db21e01
--- /dev/null
+++ b/console/stand-alone/modules/connection.js
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2017 Red Hat Inc.
+ *
+ * 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.
+ */
+/* global Promise */
+
+const rhea = require('rhea');
+//import { on, websocket_connect, removeListener, once, connect } from 'rhea';
+import Correlator from './correlator.js';
+
+class ConnectionManager {
+ constructor(protocol) {
+ this.sender = undefined;
+ this.receiver = undefined;
+ this.connection = undefined;
+ this.version = undefined;
+ this.errorText = undefined;
+ this.protocol = protocol;
+ this.schema = undefined;
+ this.connectActions = [];
+ this.disconnectActions = [];
+ this.correlator = new Correlator();
+ this.on_message = (function (context) {
+ this.correlator.resolve(context);
+ }).bind(this);
+ this.on_disconnected = (function () {
+ this.errorText = 'Disconnected';
+ this.executeDisconnectActions(this.errorText);
+ }).bind(this);
+ this.on_connection_open = (function () {
+ this.executeConnectActions();
+ }).bind(this);
+ }
+ versionCheck(minVer) {
+ var verparts = this.version.split('.');
+ var minparts = minVer.split('.');
+ try {
+ for (var i = 0; i < minparts.length; ++i) {
+ if (parseInt(minVer[i] > parseInt(verparts[i])))
+ return false;
+ }
+ }
+ catch (e) {
+ return false;
+ }
+ return true;
+ }
+ addConnectAction(action) {
+ if (typeof action === 'function') {
+ this.delConnectAction(action);
+ this.connectActions.push(action);
+ }
+ }
+ addDisconnectAction(action) {
+ if (typeof action === 'function') {
+ this.delDisconnectAction(action);
+ this.disconnectActions.push(action);
+ }
+ }
+ delConnectAction(action) {
+ if (typeof action === 'function') {
+ var index = this.connectActions.indexOf(action);
+ if (index >= 0)
+ this.connectActions.splice(index, 1);
+ }
+ }
+ delDisconnectAction(action) {
+ if (typeof action === 'function') {
+ var index = this.disconnectActions.indexOf(action);
+ if (index >= 0)
+ this.disconnectActions.splice(index, 1);
+ }
+ }
+ executeConnectActions() {
+ this.connectActions.forEach(function (action) {
+ try {
+ action();
+ }
+ catch (e) {
+ // in case the page that registered the handler has been unloaded
+ }
+ });
+ this.connectActions = [];
+ }
+ executeDisconnectActions(message) {
+ this.disconnectActions.forEach(function (action) {
+ try {
+ action(message);
+ }
+ catch (e) {
+ // in case the page that registered the handler has been unloaded
+ }
+ });
+ this.disconnectActions = [];
+ }
+ on(eventType, fn) {
+ if (eventType === 'connected') {
+ this.addConnectAction(fn);
+ }
+ else if (eventType === 'disconnected') {
+ this.addDisconnectAction(fn);
+ }
+ else {
+ console.log('unknown event type ' + eventType);
+ }
+ }
+ setSchema(schema) {
+ this.schema = schema;
+ }
+ is_connected() {
+ return this.connection &&
+ this.sender &&
+ this.receiver &&
+ this.receiver.remote &&
+ this.receiver.remote.attach &&
+ this.receiver.remote.attach.source &&
+ this.receiver.remote.attach.source.address &&
+ this.connection.is_open();
+ }
+ disconnect() {
+ if (this.sender)
+ this.sender.close();
+ if (this.receiver)
+ this.receiver.close();
+ if (this.connection)
+ this.connection.close();
+ }
+ createSenderReceiver(options) {
+ return new Promise((function (resolve, reject) {
+ var timeout = options.timeout || 10000;
+ // set a timer in case the setup takes too long
+ var giveUp = (function () {
+ this.connection.removeListener('receiver_open', receiver_open);
+ this.connection.removeListener('sendable', sendable);
+ this.errorText = 'timed out creating senders and receivers';
+ reject(Error(this.errorText));
+ }).bind(this);
+ var timer = setTimeout(giveUp, timeout);
+ // register an event hander for when the setup is complete
+ var sendable = (function (context) {
+ clearTimeout(timer);
+ this.version = this.connection.properties ? this.connection.properties.version : '0.1.0';
+ // in case this connection dies
+ rhea.on('disconnected', this.on_disconnected);
+ // in case this connection dies and is then reconnected automatically
+ rhea.on('connection_open', this.on_connection_open);
+ // receive messages here
+ this.connection.on('message', this.on_message);
+ resolve(context);
+ }).bind(this);
+ this.connection.once('sendable', sendable);
+ // Now actually createt the sender and receiver.
+ // register an event handler for when the receiver opens
+ var receiver_open = (function () {
+ // once the receiver is open, create the sender
+ if (options.sender_address)
+ this.sender = this.connection.open_sender(options.sender_address);
+ else
+ this.sender = this.connection.open_sender();
+ }).bind(this);
+ this.connection.once('receiver_open', receiver_open);
+ // create a dynamic receiver
+ this.receiver = this.connection.open_receiver({ source: { dynamic: true } });
+ }).bind(this));
+ }
+ connect(options) {
+ return new Promise((function (resolve, reject) {
+ var finishConnecting = function () {
+ this.createSenderReceiver(options)
+ .then(function (results) {
+ resolve(results);
+ }, function (error) {
+ reject(error);
+ });
+ };
+ if (!this.connection) {
+ options.test = false; // if you didn't want a connection, you should have called testConnect() and not connect()
+ this.testConnect(options)
+ .then((function () {
+ finishConnecting.call(this);
+ }).bind(this), (function () {
+ // connect failed or timed out
+ this.errorText = 'Unable to connect';
+ this.executeDisconnectActions(this.errorText);
+ reject(Error(this.errorText));
+ }).bind(this));
+ }
+ else {
+ finishConnecting.call(this);
+ }
+ }).bind(this));
+ }
+ getReceiverAddress() {
+ return this.receiver.remote.attach.source.address;
+ }
+ // Try to connect using the options.
+ // if options.test === true -> close the connection if it succeeded and resolve the promise
+ // if the connection attempt fails or times out, reject the promise regardless of options.test
+ testConnect(options, callback) {
+ return new Promise((function (resolve, reject) {
+ var timeout = options.timeout || 10000;
+ var reconnect = options.reconnect || false; // in case options.reconnect is undefined
+ var baseAddress = options.address + ':' + options.port;
+ if (options.linkRouteAddress) {
+ baseAddress += ('/' + options.linkRouteAddress);
+ }
+ var wsprotocol = location.protocol === 'https:' ? 'wss' : 'ws';
+ if (this.connection) {
+ delete this.connection;
+ this.connection = null;
+ }
+ var ws = rhea.websocket_connect(WebSocket);
+ var c = {
+ connection_details: new ws(wsprotocol + '://' + baseAddress, ['binary']),
+ reconnect: reconnect,
+ properties: options.properties || { console_identifier: 'Dispatch console' }
+ };
+ if (options.hostname)
+ c.hostname = options.hostname;
+ if (options.username && options.username !== '') {
+ c.username = options.username;
+ }
+ if (options.password && options.password !== '') {
+ c.password = options.password;
+ }
+ // set a timeout
+ var disconnected = (function () {
+ clearTimeout(timer);
+ rhea.removeListener('disconnected', disconnected);
+ rhea.removeListener('connection_open', connection_open);
+ this.connection = null;
+ var rej = 'failed to connect';
+ if (callback)
+ callback({ error: rej });
+ reject(Error(rej));
+ }).bind(this);
+ var timer = setTimeout(disconnected, timeout);
+ // the event handler for when the connection opens
+ var connection_open = (function (context) {
+ clearTimeout(timer);
+ // prevent future disconnects from calling reject
+ rhea.removeListener('disconnected', disconnected);
+ // we were just checking. we don't really want a connection
+ if (options.test) {
+ context.connection.close();
+ this.connection = null;
+ }
+ else
+ this.on_connection_open();
+ var res = { context: context };
+ if (callback)
+ callback(res);
+ resolve(res);
+ }).bind(this);
+ // register an event handler for when the connection opens
+ rhea.once('connection_open', connection_open);
+ // register an event handler for if the connection fails to open
+ rhea.once('disconnected', disconnected);
+ // attempt the connection
+ this.connection = rhea.connect(c);
+ }).bind(this));
+ }
+ sendMgmtQuery(operation) {
+ return this.send([], '/$management', operation);
+ }
+ sendQuery(toAddr, entity, attrs, operation) {
+ operation = operation || 'QUERY';
+ var fullAddr = this._fullAddr(toAddr);
+ var body = { attributeNames: attrs || [] };
+ return this.send(body, fullAddr, operation, this.schema.entityTypes[entity].fullyQualifiedType);
+ }
+ send(body, to, operation, entityType) {
+ var application_properties = {
+ operation: operation,
+ type: 'org.amqp.management',
+ name: 'self'
+ };
+ if (entityType)
+ application_properties.entityType = entityType;
+ return this._send(body, to, application_properties);
+ }
+ sendMethod(toAddr, entity, attrs, operation, props) {
+ var fullAddr = this._fullAddr(toAddr);
+ var application_properties = {
+ operation: operation,
+ };
+ if (entity) {
+ application_properties.type = this.schema.entityTypes[entity].fullyQualifiedType;
+ }
+ if (attrs.name)
+ application_properties.name = attrs.name;
+ else if (attrs.identity)
+ application_properties.identity = attrs.identity;
+ if (props) {
+ for (var attrname in props) {
+ application_properties[attrname] = props[attrname];
+ }
+ }
+ return this._send(attrs, fullAddr, application_properties);
+ }
+ _send(body, to, application_properties) {
+ var _correlationId = this.correlator.corr();
+ var self = this;
+ return new Promise(function (resolve, reject) {
+ self.correlator.register(_correlationId, resolve, reject);
+ self.sender.send({
+ body: body,
+ to: to,
+ reply_to: self.receiver.remote.attach.source.address,
+ correlation_id: _correlationId,
+ application_properties: application_properties
+ });
+ });
+ }
+ _fullAddr(toAddr) {
+ var toAddrParts = toAddr.split('/');
+ toAddrParts.shift();
+ var fullAddr = toAddrParts.join('/');
+ return fullAddr;
+ }
+ availableQeueuDepth() {
+ return this.correlator.depth();
+ }
+}
+
+class ConnectionException {
+ constructor(message) {
+ this.message = message;
+ this.name = 'ConnectionException';
+ }
+}
+
+const _ConnectionManager = ConnectionManager;
+export { _ConnectionManager as ConnectionManager };
+const _ConnectionException = ConnectionException;
+export { _ConnectionException as ConnectionException };
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/modules/correlator.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/modules/correlator.js b/console/stand-alone/modules/correlator.js
new file mode 100644
index 0000000..bf34f93
--- /dev/null
+++ b/console/stand-alone/modules/correlator.js
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 Red Hat Inc.
+ *
+ * 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.
+ */
+
+import { utils } from './utilities.js';
+
+class Correlator {
+ constructor() {
+ this._objects = {};
+ this._correlationID = 0;
+ this.maxCorrelatorDepth = 10;
+ }
+ corr() {
+ return ++(this._correlationID) + '';
+ }
+ // Associate this correlation id with the promise's resolve and reject methods
+ register(id, resolve, reject) {
+ this._objects[id] = { resolver: resolve, rejector: reject };
+ }
+ // Call the promise's resolve method.
+ // This is called by rhea's receiver.on('message') function
+ resolve(context) {
+ var correlationID = context.message.correlation_id;
+ // call the promise's resolve function with a copy of the rhea response (so we don't keep any references to internal rhea data)
+ this._objects[correlationID].resolver({ response: utils.copy(context.message.body), context: context });
+ delete this._objects[correlationID];
+ }
+ reject(id, error) {
+ this._objects[id].rejector(error);
+ delete this._objects[id];
+ }
+ // Return the number of requests that can be sent before we start queuing requests
+ depth() {
+ return Math.max(1, this.maxCorrelatorDepth - Object.keys(this._objects).length);
+ }
+}
+
+export default Correlator;
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/modules/management.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/modules/management.js b/console/stand-alone/modules/management.js
new file mode 100644
index 0000000..4b3bb32
--- /dev/null
+++ b/console/stand-alone/modules/management.js
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 Red Hat Inc.
+ *
+ * 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.
+ */
+
+/* global Promise */
+
+import { ConnectionManager } from './connection.js';
+import Topology from './topology.js';
+
+export class Management {
+ constructor(protocol) {
+ this.connection = new ConnectionManager(protocol);
+ this.topology = new Topology(this.connection);
+ }
+ getSchema(callback) {
+ var self = this;
+ return new Promise(function (resolve, reject) {
+ self.connection.sendMgmtQuery('GET-SCHEMA')
+ .then(function (responseAndContext) {
+ var response = responseAndContext.response;
+ for (var entityName in response.entityTypes) {
+ var entity = response.entityTypes[entityName];
+ if (entity.deprecated) {
+ // deprecated entity
+ delete response.entityTypes[entityName];
+ }
+ else {
+ for (var attributeName in entity.attributes) {
+ var attribute = entity.attributes[attributeName];
+ if (attribute.deprecated) {
+ // deprecated attribute
+ delete response.entityTypes[entityName].attributes[attributeName];
+ }
+ }
+ }
+ }
+ self.connection.setSchema(response);
+ if (callback)
+ callback(response);
+ resolve(response);
+ }, function (error) {
+ if (callback)
+ callback(error);
+ reject(error);
+ });
+ });
+ }
+ schema() {
+ return this.connection.schema;
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/modules/topology.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/modules/topology.js b/console/stand-alone/modules/topology.js
new file mode 100644
index 0000000..e208a6f
--- /dev/null
+++ b/console/stand-alone/modules/topology.js
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2015 Red Hat Inc.
+ *
+ * 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.
+ */
+
+/* global Promise d3 */
+
+import { utils } from './utilities.js';
+
+class Topology {
+ constructor(connectionManager) {
+ this.connection = connectionManager;
+ this.updatedActions = {};
+ this.entities = []; // which entities to request each topology update
+ this.entityAttribs = {};
+ this._nodeInfo = {}; // info about all known nodes and entities
+ this.filtering = false; // filter out nodes that don't have connection info
+ this.timeout = 5000;
+ this.updateInterval = 5000;
+ this._getTimer = null;
+ this.updating = false;
+ }
+ addUpdatedAction(key, action) {
+ if (typeof action === 'function') {
+ this.updatedActions[key] = action;
+ }
+ }
+ delUpdatedAction(key) {
+ if (key in this.updatedActions)
+ delete this.updatedActions[key];
+ }
+ executeUpdatedActions(error) {
+ for (var action in this.updatedActions) {
+ this.updatedActions[action].apply(this, [error]);
+ }
+ }
+ setUpdateEntities(entities) {
+ this.entities = entities;
+ for (var i = 0; i < entities.length; i++) {
+ this.entityAttribs[entities[i]] = [];
+ }
+ }
+ addUpdateEntities(entityAttribs) {
+ if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') {
+ entityAttribs = [entityAttribs];
+ }
+ for (var i = 0; i < entityAttribs.length; i++) {
+ var entity = entityAttribs[i].entity;
+ this.entityAttribs[entity] = entityAttribs[i].attrs || [];
+ }
+ }
+ on(eventName, fn, key) {
+ if (eventName === 'updated')
+ this.addUpdatedAction(key, fn);
+ }
+ unregister(eventName, key) {
+ if (eventName === 'updated')
+ this.delUpdatedAction(key);
+ }
+ nodeInfo() {
+ return this._nodeInfo;
+ }
+ get() {
+ return new Promise((function (resolve, reject) {
+ this.connection.sendMgmtQuery('GET-MGMT-NODES')
+ .then((function (response) {
+ response = response.response;
+ if (Object.prototype.toString.call(response) === '[object Array]') {
+ var workInfo = {};
+ // if there is only one node, it will not be returned
+ if (response.length === 0) {
+ var parts = this.connection.getReceiverAddress().split('/');
+ parts[parts.length - 1] = '$management';
+ response.push(parts.join('/'));
+ }
+ for (var i = 0; i < response.length; ++i) {
+ workInfo[response[i]] = {};
+ }
+ var gotResponse = function (nodeName, entity, response) {
+ workInfo[nodeName][entity] = response;
+ };
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ for (var id in workInfo) {
+ for (var entity in this.entityAttribs) {
+ q.defer((this.q_fetchNodeInfo).bind(this), id, entity, this.entityAttribs[entity], q, gotResponse);
+ }
+ }
+ q.await((function () {
+ // filter out nodes that have no connection info
+ if (this.filtering) {
+ for (var id in workInfo) {
+ if (!(workInfo[id].connection)) {
+ this.flux = true;
+ delete workInfo[id];
+ }
+ }
+ }
+ this._nodeInfo = utils.copy(workInfo);
+ this.onDone(this._nodeInfo);
+ resolve(this._nodeInfo);
+ }).bind(this));
+ }
+ }).bind(this), function (error) {
+ reject(error);
+ });
+ }).bind(this));
+ }
+ onDone(result) {
+ clearTimeout(this._getTimer);
+ if (this.updating)
+ this._getTimer = setTimeout((this.get).bind(this), this.updateInterval);
+ this.executeUpdatedActions(result);
+ }
+ startUpdating(filter) {
+ this.stopUpdating();
+ this.updating = true;
+ this.filtering = filter;
+ this.get();
+ }
+ stopUpdating() {
+ this.updating = false;
+ if (this._getTimer) {
+ clearTimeout(this._getTimer);
+ this._getTimer = null;
+ }
+ }
+ fetchEntity(node, entity, attrs, callback) {
+ var results = {};
+ var gotResponse = function (nodeName, dotentity, response) {
+ results = response;
+ };
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ q.defer((this.q_fetchNodeInfo).bind(this), node, entity, attrs, q, gotResponse);
+ q.await(function () {
+ callback(node, entity, results);
+ });
+ }
+ // called from d3.queue.defer so the last argument (callback) is supplied by d3
+ q_fetchNodeInfo(nodeId, entity, attrs, q, heartbeat, callback) {
+ this.getNodeInfo(nodeId, entity, attrs, q, function (nodeName, dotentity, response) {
+ heartbeat(nodeName, dotentity, response);
+ callback(null);
+ });
+ }
+ // get all the requested entities/attributes for a single router
+ fetchEntities(node, entityAttribs, doneCallback, resultCallback) {
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ var results = {};
+ if (!resultCallback) {
+ resultCallback = function (nodeName, dotentity, response) {
+ if (!results[nodeName])
+ results[nodeName] = {};
+ results[nodeName][dotentity] = response;
+ };
+ }
+ var gotAResponse = function (nodeName, dotentity, response) {
+ resultCallback(nodeName, dotentity, response);
+ };
+ if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') {
+ entityAttribs = [entityAttribs];
+ }
+ for (var i = 0; i < entityAttribs.length; ++i) {
+ var ea = entityAttribs[i];
+ q.defer((this.q_fetchNodeInfo).bind(this), node, ea.entity, ea.attrs || [], q, gotAResponse);
+ }
+ q.await(function () {
+ doneCallback(results);
+ });
+ }
+ // get all the requested entities for all known routers
+ fetchAllEntities(entityAttribs, doneCallback, resultCallback) {
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ var results = {};
+ if (!resultCallback) {
+ resultCallback = function (nodeName, dotentity, response) {
+ if (!results[nodeName])
+ results[nodeName] = {};
+ results[nodeName][dotentity] = response;
+ };
+ }
+ var gotAResponse = function (nodeName, dotentity, response) {
+ resultCallback(nodeName, dotentity, response);
+ };
+ if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') {
+ entityAttribs = [entityAttribs];
+ }
+ var nodes = Object.keys(this._nodeInfo);
+ for (var n = 0; n < nodes.length; ++n) {
+ for (var i = 0; i < entityAttribs.length; ++i) {
+ var ea = entityAttribs[i];
+ q.defer((this.q_fetchNodeInfo).bind(this), nodes[n], ea.entity, ea.attrs || [], q, gotAResponse);
+ }
+ }
+ q.await(function () {
+ doneCallback(results);
+ });
+ }
+ // enusre all the topology nones have all these entities
+ ensureAllEntities(entityAttribs, callback, extra) {
+ this.ensureEntities(Object.keys(this._nodeInfo), entityAttribs, callback, extra);
+ }
+ // ensure these nodes have all these entities. don't fetch unless forced to
+ ensureEntities(nodes, entityAttribs, callback, extra) {
+ if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') {
+ entityAttribs = [entityAttribs];
+ }
+ if (Object.prototype.toString.call(nodes) !== '[object Array]') {
+ nodes = [nodes];
+ }
+ this.addUpdateEntities(entityAttribs);
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ for (var n = 0; n < nodes.length; ++n) {
+ for (var i = 0; i < entityAttribs.length; ++i) {
+ var ea = entityAttribs[i];
+ // if we don'e already have the entity or we want to force a refresh
+ if (!this._nodeInfo[nodes[n]][ea.entity] || ea.force)
+ q.defer((this.q_ensureNodeInfo).bind(this), nodes[n], ea.entity, ea.attrs || [], q);
+ }
+ }
+ q.await(function () {
+ callback(extra);
+ });
+ }
+ addNodeInfo(id, entity, values) {
+ // save the results in the nodeInfo object
+ if (id) {
+ if (!(id in this._nodeInfo)) {
+ this._nodeInfo[id] = {};
+ }
+ // copy the values to allow garbage collection
+ this._nodeInfo[id][entity] = values;
+ }
+ }
+ isLargeNetwork() {
+ return Object.keys(this._nodeInfo).length >= 12;
+ }
+ getConnForLink(link) {
+ // find the connection for this link
+ var conns = this._nodeInfo[link.nodeId].connection;
+ var connIndex = conns.attributeNames.indexOf('identity');
+ var linkCons = conns.results.filter(function (conn) {
+ return conn[connIndex] === link.connectionId;
+ });
+ return utils.flatten(conns.attributeNames, linkCons[0]);
+ }
+ nodeNameList() {
+ var nl = [];
+ for (var id in this._nodeInfo) {
+ nl.push(utils.nameFromId(id));
+ }
+ return nl.sort();
+ }
+ nodeIdList() {
+ var nl = [];
+ for (var id in this._nodeInfo) {
+ //if (this._nodeInfo['connection'])
+ nl.push(id);
+ }
+ return nl.sort();
+ }
+ nodeList() {
+ var nl = [];
+ for (var id in this._nodeInfo) {
+ nl.push({
+ name: utils.nameFromId(id),
+ id: id
+ });
+ }
+ return nl;
+ }
+ // d3.queue'd function to make a management query for entities/attributes
+ q_ensureNodeInfo(nodeId, entity, attrs, q, callback) {
+ this.getNodeInfo(nodeId, entity, attrs, q, (function (nodeName, dotentity, response) {
+ this.addNodeInfo(nodeName, dotentity, response);
+ callback(null);
+ }).bind(this));
+ return {
+ abort: function () {
+ delete this._nodeInfo[nodeId];
+ }
+ };
+ }
+ getNodeInfo(nodeName, entity, attrs, q, callback) {
+ var timedOut = function (q) {
+ q.abort();
+ };
+ var atimer = setTimeout(timedOut, this.timeout, q);
+ this.connection.sendQuery(nodeName, entity, attrs)
+ .then(function (response) {
+ clearTimeout(atimer);
+ callback(nodeName, entity, response.response);
+ }, function () {
+ q.abort();
+ });
+ }
+ getMultipleNodeInfo(nodeNames, entity, attrs, callback, selectedNodeId, aggregate) {
+ var self = this;
+ if (typeof aggregate === 'undefined')
+ aggregate = true;
+ var responses = {};
+ var gotNodesResult = function (nodeName, dotentity, response) {
+ responses[nodeName] = response;
+ };
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ nodeNames.forEach(function (id) {
+ q.defer((self.q_fetchNodeInfo).bind(self), id, entity, attrs, q, gotNodesResult);
+ });
+ q.await(function () {
+ if (aggregate)
+ self.aggregateNodeInfo(nodeNames, entity, selectedNodeId, responses, callback);
+ else {
+ callback(nodeNames, entity, responses);
+ }
+ });
+ }
+ quiesceLink(nodeId, name) {
+ var attributes = {
+ adminStatus: 'disabled',
+ name: name
+ };
+ return this.connection.sendMethod(nodeId, 'router.link', attributes, 'UPDATE');
+ }
+ aggregateNodeInfo(nodeNames, entity, selectedNodeId, responses, callback) {
+ // aggregate the responses
+ var self = this;
+ var newResponse = {};
+ var thisNode = responses[selectedNodeId];
+ newResponse.attributeNames = thisNode.attributeNames;
+ newResponse.results = thisNode.results;
+ newResponse.aggregates = [];
+ // initialize the aggregates
+ for (var i = 0; i < thisNode.results.length; ++i) {
+ // there is a result for each unique entity found (ie addresses, links, etc.)
+ var result = thisNode.results[i];
+ var vals = [];
+ // there is a val for each attribute in this entity
+ result.forEach(function (val) {
+ vals.push({
+ sum: val,
+ detail: []
+ });
+ });
+ newResponse.aggregates.push(vals);
+ }
+ var nameIndex = thisNode.attributeNames.indexOf('name');
+ var ent = self.connection.schema.entityTypes[entity];
+ var ids = Object.keys(responses);
+ ids.sort();
+ ids.forEach(function (id) {
+ var response = responses[id];
+ var results = response.results;
+ results.forEach(function (result) {
+ // find the matching result in the aggregates
+ var found = newResponse.aggregates.some(function (aggregate) {
+ if (aggregate[nameIndex].sum === result[nameIndex]) {
+ // result and aggregate are now the same record, add the graphable values
+ newResponse.attributeNames.forEach(function (key, i) {
+ if (ent.attributes[key] && ent.attributes[key].graph) {
+ if (id != selectedNodeId)
+ aggregate[i].sum += result[i];
+ }
+ aggregate[i].detail.push({
+ node: utils.nameFromId(id) + ':',
+ val: result[i]
+ });
+ });
+ return true; // stop looping
+ }
+ return false; // continute looking for the aggregate record
+ });
+ if (!found) {
+ // this attribute was not found in the aggregates yet
+ // because it was not in the selectedNodeId's results
+ var vals = [];
+ result.forEach(function (val) {
+ vals.push({
+ sum: val,
+ detail: [{
+ node: utils.nameFromId(id),
+ val: val
+ }]
+ });
+ });
+ newResponse.aggregates.push(vals);
+ }
+ });
+ });
+ callback(nodeNames, entity, newResponse);
+ }
+}
+
+export default Topology;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/modules/utilities.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/modules/utilities.js b/console/stand-alone/modules/utilities.js
new file mode 100644
index 0000000..328da38
--- /dev/null
+++ b/console/stand-alone/modules/utilities.js
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2015 Red Hat Inc.
+ *
+ * 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.
+ */
+
+/* global d3 */
+var ddd = typeof window === 'undefined' ? require ('d3') : d3;
+
+var utils = {
+ isAConsole: function (properties, connectionId, nodeType, key) {
+ return this.isConsole({
+ properties: properties,
+ connectionId: connectionId,
+ nodeType: nodeType,
+ key: key
+ });
+ },
+ isConsole: function (d) {
+ return (d && d.properties && d.properties.console_identifier === 'Dispatch console');
+ },
+ isArtemis: function (d) {
+ return (d.nodeType === 'route-container' || d.nodeType === 'on-demand') && (d.properties && d.properties.product === 'apache-activemq-artemis');
+ },
+
+ isQpid: function (d) {
+ return (d.nodeType === 'route-container' || d.nodeType === 'on-demand') && (d.properties && d.properties.product === 'qpid-cpp');
+ },
+ flatten: function (attributes, result) {
+ if (!attributes || !result)
+ return {};
+ var flat = {};
+ attributes.forEach(function(attr, i) {
+ if (result && result.length > i)
+ flat[attr] = result[i];
+ });
+ return flat;
+ },
+ copy: function (obj) {
+ if (obj)
+ return JSON.parse(JSON.stringify(obj));
+ },
+ identity_clean: function (identity) {
+ if (!identity)
+ return '-';
+ var pos = identity.indexOf('/');
+ if (pos >= 0)
+ return identity.substring(pos + 1);
+ return identity;
+ },
+ addr_text: function (addr) {
+ if (!addr)
+ return '-';
+ if (addr[0] == 'M')
+ return addr.substring(2);
+ else
+ return addr.substring(1);
+ },
+ addr_class: function (addr) {
+ if (!addr) return '-';
+ if (addr[0] == 'M') return 'mobile';
+ if (addr[0] == 'R') return 'router';
+ if (addr[0] == 'A') return 'area';
+ if (addr[0] == 'L') return 'local';
+ if (addr[0] == 'C') return 'link-incoming';
+ if (addr[0] == 'E') return 'link-incoming';
+ if (addr[0] == 'D') return 'link-outgoing';
+ if (addr[0] == 'F') return 'link-outgoing';
+ if (addr[0] == 'T') return 'topo';
+ return 'unknown: ' + addr[0];
+ },
+ humanify: function (s) {
+ if (!s || s.length === 0)
+ return s;
+ var t = s.charAt(0).toUpperCase() + s.substr(1).replace(/[A-Z]/g, ' $&');
+ return t.replace('.', ' ');
+ },
+ pretty: function (v) {
+ var formatComma = ddd.format(',');
+ if (!isNaN(parseFloat(v)) && isFinite(v))
+ return formatComma(v);
+ return v;
+ },
+ isMSIE: function () {
+ return (document.documentMode || /Edge/.test(navigator.userAgent));
+ },
+ valFor: function (aAr, vAr, key) {
+ var idx = aAr.indexOf(key);
+ if ((idx > -1) && (idx < vAr.length)) {
+ return vAr[idx];
+ }
+ return null;
+ },
+ // extract the name of the router from the router id
+ nameFromId: function (id) {
+ // the router id looks like 'amqp:/topo/0/routerName/$managemrnt'
+ var parts = id.split('/');
+ // handle cases where the router name contains a /
+ parts.splice(0, 3); // remove amqp, topo, 0
+ parts.pop(); // remove $management
+ return parts.join('/');
+ }
+
+};
+export { utils };
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org