str.substr(0, prefix.length + forVersion.length) === `${prefix}${forVersion}`);
33 | if (bodyStartIndex > -1) {
34 | let bodyEndIndex = lines.findIndex((str, idx) => idx > bodyStartIndex && str.substr(0, prefix.length) === prefix);
35 | let header = forVersion;
36 | let body = lines.slice(bodyStartIndex + 1 + skipLines, bodyEndIndex).join('\n');
37 | resolve({header, body});
38 | } else {
39 | resolve({header : forVersion, body : ''});
40 | }
41 | }
42 | });
43 | });
44 | };
45 |
46 | // Build archive type .zip
47 | // FIXME: fix handling of sub directory
48 | const buildZipArchive = ({lookupDir, archiveFilePath}) => {
49 | return new Promise((resolve, reject) => {
50 | fs.readdir(lookupDir, (err, files) => {
51 | if (err) {
52 | reject();
53 | } else {
54 | let archive = new AdmZip();
55 | files.forEach(file => {
56 | let localFilename = path.join(lookupDir, file);
57 | archive.addLocalFile(localFilename);
58 | });
59 | archive.writeZip(archiveFilePath);
60 | resolve(archiveFilePath);
61 | }
62 | });
63 | });
64 | };
65 |
66 | // Build archive type .tar.gz
67 | const buildTarGzArchive = ({lookupDir, archiveFilePath}) => {
68 | let gzipOptions = {
69 | level : 9,
70 | memLevel : 9,
71 | };
72 | let tarOptions = {
73 | fromBase : true
74 | };
75 | return new TarGz(gzipOptions, tarOptions)
76 | .compress(lookupDir, archiveFilePath)
77 | .then(() => archiveFilePath);
78 | };
79 |
80 | // Upload and apply release
81 | const publish = ({ghApiToken, projectVersion, projectVersionName, projectOwner, projectRepo, changelog, assets}) => {
82 | return new Promise((resolve, reject) => {
83 | publishRelease({
84 | token : ghApiToken,
85 | owner : projectOwner,
86 | repo : projectRepo,
87 | tag : projectVersion,
88 | name : projectVersionName,
89 | notes : changelog.body,
90 | draft : false,
91 | prerelease : false,
92 | reuseRelease : true,
93 | reuseDraftOnly : true,
94 | assets : assets,
95 | }, function (err, release) {
96 | if (err) {
97 | reject(err);
98 | } else {
99 | resolve(release);
100 | }
101 | })
102 | });
103 | };
104 |
105 | // Main
106 | // Load repo details
107 | const pkg = require('./../package.json');
108 |
109 | if (!process.env.GH_TOKEN) {
110 | console.log('Missing env key GH_TOKEN');
111 | process.exit(1);
112 | }
113 | //if (!process.env.PROJECT_OWNER) {
114 | // console.log('Missing env key PROJECT_OWNER');
115 | // process.exit(1);
116 | //}
117 |
118 | console.log(`Get changelog for version ${pkg.version}...`);
119 | const changelogFilePath = path.normalize(path.join(__dirname, '../CHANGELOG.md'));
120 | const distDir = path.normalize(path.join(__dirname, '..', 'dist'));
121 | const tempDir = path.normalize(path.join(__dirname, '..', 'temp'));
122 | const tempAssetsDir = path.normalize(path.join(__dirname, '..', 'temp', 'assets'));
123 |
124 | // ensure temp structure
125 | const resetTempResources = () => {
126 | return new Promise((resolve) => {
127 | if (fs.existsSync(tempAssetsDir)) {
128 | // delete only
129 | rimraf(tempAssetsDir, () => {
130 | mkdirSync(tempAssetsDir);
131 | resolve();
132 | });
133 | } else {
134 | mkdirSync(tempDir);
135 | mkdirSync(tempAssetsDir);
136 | resolve();
137 | }
138 | });
139 | };
140 |
141 | // get changelog and get fresh archives
142 | const bundle = () => {
143 | return Promise.all([
144 | getChangelog(changelogFilePath, pkg.version),
145 | Promise.all([
146 | buildZipArchive({lookupDir : distDir, archiveFilePath : `${tempAssetsDir}/${pkg.name}-${pkg.version}.zip`}),
147 | buildTarGzArchive({lookupDir : distDir, archiveFilePath : `${tempAssetsDir}/${pkg.name}-${pkg.version}.tar.gz`}),
148 | ])
149 | ]);
150 | };
151 |
152 | resetTempResources()
153 | .then(bundle)
154 | .then(([changelog, assets]) => {
155 | return publish({
156 | ghApiToken : process.env.GH_TOKEN,
157 | projectOwner : 'knalli',
158 | projectRepo : process.env.PROJECT_REPO || pkg.name,
159 | projectVersion : pkg.version,
160 | projectVersionName : `${pkg.name} ${changelog.header || pkg.version}`,
161 | changelog : changelog,
162 | assets : assets,
163 | });
164 | })
165 | .then(() => {
166 | console.log('Release published successfully');
167 | process.exit(0)
168 | })
169 | .catch((err) => {
170 | console.log('Failed: ' + err);
171 | process.exit(1);
172 | });
173 |
--------------------------------------------------------------------------------
/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-vertxbus",
3 | "description": "AngularJS facade and service acting as a Vert.x SockJS client",
4 | "version": "master",
5 | "main": "dist/angular-vertxbus.js",
6 | "keywords": ["angular", "vertx", "facade", "websocket"],
7 | "repository": "knalli/angular-vertxbus",
8 | "scripts": [
9 | "dist/angular-vertxbus.js"
10 | ],
11 | "dependencies": {
12 | "components/angular.js": ">=1.2 <=1.6"
13 | },
14 | "license": "MIT"
15 | }
16 |
--------------------------------------------------------------------------------
/dependencyci.yml:
--------------------------------------------------------------------------------
1 | # don't run unlicensed test on any development dependencies
2 | tests:
3 | unlicensed: skip # broken due vertx-ventbus-client (https://dependencyci.com/github/knalli/angular-vertxbus/builds/6)
4 |
--------------------------------------------------------------------------------
/docs/github-badge.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | "use strict";
3 | window.setTimeout(function () {
4 | var img = document.createElement('img');
5 | img.style.position = 'absolute';
6 | img.style.top = '40px';
7 | img.style.right = 0;
8 | img.style.border = 0;
9 | img.src = 'https://camo.githubusercontent.com/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67';
10 | img.alt = 'Fork me on GitHub';
11 | img.dataCanonicalSrc = 'https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png';
12 | var a = document.createElement('a');
13 | a.href = 'https://github.com/knalli/angular-vertxbus';
14 | a.appendChild(img);
15 | document.body.appendChild(a);
16 | }, 150);
17 |
18 | })();
19 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | /*eslint-env node, commonjs */
2 | /*eslint comma-dangle:0,no-console:0 */
3 |
4 | const fs = require('fs');
5 | const path = require('path');
6 | const webpack = require('webpack');
7 | const browserslist = require('browserslist');
8 |
9 | var AVAILABLE_SCOPES = [], isValidScope, injectByScope, getAffectiveScope, isDefaultScope;
10 |
11 | (function () {
12 | AVAILABLE_SCOPES = fs.readdirSync('./test_scopes').filter(function (filename) {
13 | return filename[0] !== '.';
14 | });
15 | isValidScope = function (scope) {
16 | return AVAILABLE_SCOPES.indexOf(scope) > -1;
17 | };
18 | getAffectiveScope = function (scope) {
19 | if (isValidScope(scope)) {
20 | return scope;
21 | } else {
22 | return '(default)';
23 | }
24 | };
25 | injectByScope = function (scope, path) {
26 | var prefix = '';
27 | // unless a scope is given, use the default resources
28 | if (scope && isValidScope(scope)) {
29 | prefix = 'test_scopes/' + scope + '/';
30 | }
31 | return prefix + 'bower_components/' + path;
32 | },
33 | isDefaultScope = function (scope) {
34 | return !isValidScope(scope);
35 | };
36 | })();
37 |
38 | /**
39 | * Configuration set for SauceLabs browser tests
40 | * @type {{enabled, launchers, buildOptions}}
41 | */
42 | const sourcelabsConfig = ((browsers) => {
43 |
44 | const enabled = !!process.env.SAUCE_ENABLED;
45 |
46 | // parse list of browsers (by browserslist) and build a useful list
47 | const launchers = browsers
48 | .map((string) => {
49 | let [name, version] = string.split(' ');
50 | return {
51 | name,
52 | version,
53 | };
54 | })
55 | .filter(browser => {
56 | switch (browser.name) {
57 | case 'chrome':
58 | case 'edge':
59 | case 'firefox':
60 | case 'ie':
61 | case 'opera':
62 | case 'safari':
63 | return true;
64 | default:
65 | return false;
66 | }
67 | })
68 | .map(browser => {
69 | switch (browser.name) {
70 | case 'chrome':
71 | return {
72 | id : `sl_${browser.name}_${browser.version}`,
73 | base : 'SauceLabs',
74 | browserName : browser.name,
75 | platform : 'Windows 7',
76 | version : browser.version
77 | };
78 | case 'firefox':
79 | return {
80 | id : `sl_${browser.name}_${browser.version}`,
81 | base : 'SauceLabs',
82 | browserName : browser.name,
83 | platform : 'Windows 7',
84 | version : browser.version
85 | };
86 | case 'ie':
87 | return {
88 | id : `sl_${browser.name}_${browser.version}`,
89 | base : 'SauceLabs',
90 | browserName : 'internet explorer',
91 | platform : 'Windows 7',
92 | version : browser.version
93 | };
94 | case 'edge':
95 | return {
96 | id : `sl_${browser.name}_${browser.version}`,
97 | base : 'SauceLabs',
98 | browserName : 'microsoftedge',
99 | platform : 'Windows 10',
100 | version : browser.version
101 | };
102 | case 'safari':
103 | // skip 9
104 | if (browser.version.substring(0, 1) === '9') {
105 | break;
106 | }
107 | return {
108 | id : `sl_${browser.name}_${browser.version}`,
109 | base : 'SauceLabs',
110 | browserName : browser.name,
111 | platform : 'macOS 10.12',
112 | version : browser.version
113 | };
114 | }
115 | })
116 | .filter(browser => browser);
117 |
118 | const buildOptions = (scope) => {
119 | return {
120 | testName : `angular-vertxbus Unit Tests, scope: ${scope}`,
121 | verbose : true,
122 | doctor : true,
123 | logger : console.log
124 | };
125 | };
126 |
127 | return {
128 | enabled,
129 | launchers,
130 | buildOptions,
131 | };
132 | })(browserslist('last 2 versions, Firefox ESR'));
133 |
134 | module.exports = function (config) {
135 |
136 | const scope = process.env.TEST_SCOPE;
137 | const actualScope = getAffectiveScope(scope);
138 | console.log('Available test scopes: ', AVAILABLE_SCOPES);
139 | console.log('Currently selected scope: ', actualScope);
140 |
141 | var vertxEventBusFile = injectByScope(scope, 'vertx3-eventbus-client/vertx-eventbus.js');
142 |
143 | config.set({
144 |
145 | // list of files / patterns to load in the browser
146 | files : [
147 | injectByScope(scope, 'angular/angular.js'),
148 | injectByScope(scope, 'angular-mocks/angular-mocks.js'),
149 | 'test/unit/util/unhandledRejectionTracing.js',
150 | 'node_modules/babel-polyfill/browser.js',
151 | 'test/unit/test_index.js',
152 | ],
153 |
154 | frameworks : ['expect', 'mocha', 'jasmine'],
155 |
156 | reporters : (() => {
157 | let reporters = ['progress'];
158 | if (isDefaultScope(scope)) {
159 | reporters.push('coverage-istanbul');
160 | }
161 | if (sourcelabsConfig.enabled) {
162 | reporters.push('saucelabs');
163 | }
164 | return reporters;
165 | })(),
166 |
167 | preprocessors : {
168 | 'test/unit/test_index.js' : ['webpack'],
169 | },
170 |
171 | // SourceLabs
172 | // https://oligofren.wordpress.com/2014/05/27/running-karma-tests-on-browserstack/
173 | // http://stackoverflow.com/questions/24093155/karma-sauce-launcher-disconnects-every-test-run-resulting-in-failed-runs-with-ie
174 | sauceLabs : ((scope) => {
175 | if (sourcelabsConfig.enabled) {
176 | return sourcelabsConfig.buildOptions(scope);
177 | }
178 | })(actualScope),
179 | customLaunchers : (() => {
180 | if (sourcelabsConfig.enabled) {
181 | return sourcelabsConfig.launchers;
182 | }
183 | })(),
184 | browserDisconnectTimeout : (() => {
185 | if (sourcelabsConfig.enabled) {
186 | return 10000; // default 2000
187 | }
188 | })(),
189 | browserDisconnectTolerance : (() => {
190 | if (sourcelabsConfig.enabled) {
191 | return 1; // default 0
192 | }
193 | })(),
194 | browserNoActivityTimeout : (() => {
195 | if (sourcelabsConfig.enabled) {
196 | return 5 * 60 * 1000; // default 10000
197 | }
198 | })(),
199 |
200 | webpack : {
201 | devtool : 'source-map',
202 | //entry: [],
203 | resolve : {
204 | modules : [
205 | path.join(__dirname, ''),
206 | path.join(__dirname, 'src'),
207 | path.join(__dirname, 'test'),
208 | 'node_modules'
209 | ],
210 | alias : {
211 | 'sockjs-client' : 'test/unit/mock/sockjs.js',
212 | 'vertx-eventbus' : vertxEventBusFile
213 | }
214 | },
215 | module : {
216 | rules : [
217 | {
218 | enforce : 'pre',
219 | test : /\.js$/,
220 | exclude : [
221 | /(node_modules|bower_components)/,
222 | ],
223 | loader : 'babel-loader',
224 | options : {
225 | presets : ['env'],
226 | plugins : ['transform-runtime'],
227 | },
228 | },
229 | {
230 | enforce : 'post',
231 | test : /\.js$/,
232 | use: {
233 | loader : 'istanbul-instrumenter-loader',
234 | options : {
235 | esModules : true,
236 | },
237 | },
238 | include : [
239 | path.resolve('src/')
240 | ],
241 | exclude : [
242 | /(node_modules|bower_components)/,
243 | ],
244 | },
245 | {
246 | test : /vertx-eventbus\.js$/,
247 | loader : 'imports-loader',
248 | options : {
249 | 'SockJS' : 'sockjs-client',
250 | },
251 | },
252 | ]
253 | },
254 | plugins : [
255 | //new JasmineWebpackPlugin(),
256 | new webpack.ProvidePlugin({
257 | 'EventBus' : 'vertx-eventbus',
258 | 'SockJS' : 'sockjs-client',
259 | 'window.EventBus' : 'vertx-eventbus',
260 | 'global.EventBus' : 'vertx-eventbus'
261 | })
262 | ]
263 | },
264 |
265 | webpackMiddleware : {
266 | noInfo : true
267 | },
268 |
269 | coverageIstanbulReporter : (() => {
270 | if (isDefaultScope(scope)) {
271 | return {
272 | fixWebpackSourcePaths: true,
273 | reports: [
274 | 'text-summary',
275 | 'lcovonly',
276 | ],
277 | dir : 'build/coverage',
278 | lcovonly: {
279 | subdir : 'report',
280 | type : 'lcov',
281 | },
282 | };
283 | }
284 | })(),
285 |
286 | // web server port
287 | port : 9876,
288 |
289 | // cli runner port
290 | runnerPort : 9100,
291 |
292 | // enable / disable colors in the output (reporters and logs)
293 | colors : true,
294 |
295 | // level of logging
296 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
297 | logLevel : config.LOG_INFO,
298 |
299 | // enable / disable watching file and executing tests whenever any file changes
300 | autoWatch : true,
301 |
302 | // Start these browsers, currently available:
303 | // - Chrome
304 | // - ChromeCanary
305 | // - Firefox
306 | // - Opera
307 | // - Safari (only Mac)
308 | // - PhantomJS
309 | // - IE (only Windows)
310 | browsers : (() => {
311 | if (process.env.WATCH) {
312 | return [];
313 | }
314 |
315 | let browsers = [];
316 | if (sourcelabsConfig.enabled) {
317 | browsers = [...browsers, ...Object.keys(sourcelabsConfig.launchers)];
318 | } else if (process.env.TRAVIS) {
319 | browsers.push('ChromeHeadless');
320 | } else {
321 | if (process.env.NO_HEADLESS) {
322 | browsers.push('Chrome');
323 | } else {
324 | browsers.push('ChromeHeadless');
325 | }
326 | }
327 | return browsers;
328 | })(),
329 |
330 | // If browser does not capture in given timeout [ms], kill it
331 | captureTimeout : (() => {
332 | if (sourcelabsConfig.enabled) {
333 | return 5 * 60 * 1000; // default 60000
334 | } else {
335 | return 1 * 60 * 1000;
336 | }
337 | })(),
338 |
339 | // Continuous Integration mode
340 | // if true, it capture browsers, run tests and exit
341 | singleRun : process.env.WATCH ? false : true
342 | });
343 | };
344 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-vertxbus",
3 | "version": "6.4.1",
4 | "description": "AngularJS facade and service acting as a Vert.x SockJS client",
5 | "main": "dist/angular-vertxbus.js",
6 | "keywords": [
7 | "angular",
8 | "vertx",
9 | "facade",
10 | "websocket"
11 | ],
12 | "engines": {
13 | "node": ">*"
14 | },
15 | "devEngines": {
16 | "node": ">=8.9",
17 | "npm": ">=5.3"
18 | },
19 | "scripts": {
20 | "check-env": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json",
21 | "clean": "grunt clean",
22 | "clean-test-scopes": "for f in test_scopes/*; do (cd $f; rm -rf bower_components); done",
23 | "prepare": "bower install",
24 | "lint": "eslint --ignore-pattern node_modules/**,bower_components/** --ext .js src/** *.js",
25 | "test-headless": "karma start --browsers='PhantomJS' karma.conf.js",
26 | "test-karma": "karma start karma.conf.js",
27 | "test": "npm run -s check-env && npm run -s lint && grunt install-test && npm run -s test-karma",
28 | "test-scopes": "grunt install-test && for f in test_scopes/*; do TEST_SCOPE=\"`basename $f`\" npm run -s test; done",
29 | "start-it-web-server": "babel-node test/e2e/server.js",
30 | "install-it-vertx-server": "cd test/e2e/vertx && ./install-vertx.sh",
31 | "start-it-vertx-server": "cd test/e2e/vertx/app && ../runtime/vertx/bin/vertx run vertx3-config.js",
32 | "start-server": "npm-run-all --silent clean compile-dist install-it-vertx-server --parallel start-it-vertx-server start-it-web-server",
33 | "compile-dist": "webpack",
34 | "compile-distmin": "BUILD_MINIFIED=1 webpack",
35 | "compile": "grunt clean && npm run -s compile-dist && npm run -s compile-distmin && grunt build-post",
36 | "build": "npm run -s check-env && npm run -s test && npm run -s compile",
37 | "release": "npm run -s clean && npm run -s build && grunt release",
38 | "upload-github-release": "node build_tools/upload-github-release.js",
39 | "docs": "grunt docs"
40 | },
41 | "author": {
42 | "name": "Jan Philipp",
43 | "email": "knallisworld@googlemail.com"
44 | },
45 | "homepage": "https://github.com/knalli/angular-vertxbus",
46 | "repository": {
47 | "type": "git",
48 | "url": "git://github.com/knalli/angular-vertxbus"
49 | },
50 | "license": "MIT",
51 | "devDependencies": {
52 | "adm-zip": "^0.4.7",
53 | "babel-cli": "^6.26.0",
54 | "babel-core": "^6.26.0",
55 | "babel-eslint": "^8.2.2",
56 | "babel-loader": "^7.1.2",
57 | "babel-plugin-transform-runtime": "^6.5.2",
58 | "babel-polyfill": "^6.26.0",
59 | "babel-preset-env": "^1.6.1",
60 | "bower": "1.8.2",
61 | "browserslist": "^3.1.1",
62 | "eslint": "^4.18.1",
63 | "eslint-loader": "^1.6.1",
64 | "eslint-plugin-angular": "^3.2.0",
65 | "estraverse": "^4.1.0",
66 | "express": "^4.16.2",
67 | "fbjs-scripts": "^0.8.0",
68 | "grunt": "^1.0.2",
69 | "grunt-babel": "^7.0.0",
70 | "grunt-bower-install-simple": "~1.2.3",
71 | "grunt-cli": "^1.2.0",
72 | "grunt-contrib-clean": "~1.1.0",
73 | "grunt-contrib-concat": "^1.0.0",
74 | "grunt-conventional-changelog": "^6.1.0",
75 | "grunt-ngdocs": "^0.2.11",
76 | "imports-loader": "^0.8.0",
77 | "istanbul": "^0.4.0",
78 | "istanbul-instrumenter-loader": "^3.0.0",
79 | "jasmine-core": "^2.99.1",
80 | "jasmine-webpack-plugin": "^0.1.1",
81 | "karma": "^2.0.0",
82 | "karma-babel-preprocessor": "^7.0.0",
83 | "karma-chrome-launcher": "^2.0.0",
84 | "karma-coverage": "^1.0.0",
85 | "karma-coverage-istanbul-reporter": "^1.4.1",
86 | "karma-expect": "~1.1.3",
87 | "karma-firefox-launcher": "~1.1.0",
88 | "karma-jasmine": "^1.1.1",
89 | "karma-mocha": "^1.0.1",
90 | "karma-phantomjs-launcher": "^1.0.0",
91 | "karma-sauce-launcher": "^1.2.0",
92 | "karma-sourcemap-loader": "^0.3.7",
93 | "karma-webpack": "^2.0.9",
94 | "load-grunt-tasks": "^3.3.0",
95 | "lodash": "^4.17.5",
96 | "mocha": "^5.0.1",
97 | "ng-annotate-webpack-plugin": "^0.2.0",
98 | "npm-run-all": "^4.1.2",
99 | "phantomjs-prebuilt": "^2.1.16",
100 | "publish-release": "^1.5.0",
101 | "rimraf": "^2.6.2",
102 | "tar.gz": "^1.0.7",
103 | "transform-loader": "^0.2.3",
104 | "webpack": "^3.11.0"
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | const moduleName = 'knalli.angular-vertxbus';
2 |
3 | export {moduleName};
4 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import moduleName from './module';
2 |
3 | export default moduleName;
4 |
--------------------------------------------------------------------------------
/src/lib/VertxEventBusServiceProvider.js:
--------------------------------------------------------------------------------
1 | import EventBusDelegate from './service/delegate/EventBusDelegate';
2 | import NoopDelegate from './service/delegate/NoopDelegate';
3 | import Delegator from './service/Delegator';
4 |
5 | /**
6 | * @ngdoc service
7 | * @module knalli.angular-vertxbus
8 | * @name knalli.angular-vertxbus.vertxEventBusServiceProvider
9 | * @description
10 | * This is the configuration provider for {@link knalli.angular-vertxbus.vertxEventBusService}.
11 | */
12 |
13 | const DEFAULTS = {
14 | enabled : true,
15 | debugEnabled : false,
16 | authRequired : false,
17 | prefix : 'vertx-eventbus.',
18 | sockjsStateInterval : 10000,
19 | messageBuffer : 10000
20 | };
21 |
22 | let VertxEventBusServiceProvider = function () {
23 |
24 | // options (globally, application-wide)
25 | var options = angular.extend({}, DEFAULTS);
26 |
27 | /**
28 | * @ngdoc method
29 | * @module knalli.angular-vertxbus
30 | * @methodOf knalli.angular-vertxbus.vertxEventBusServiceProvider
31 | * @name .#enable
32 | *
33 | * @description
34 | * Enables or disables the service. This setup is immutable.
35 | *
36 | * @param {boolean} [value=true] service is enabled on startup
37 | * @returns {object} this
38 | */
39 | this.enable = (value = DEFAULTS.enabled) => {
40 | options.enabled = (value === true);
41 | return this;
42 | };
43 |
44 | /**
45 | * @ngdoc method
46 | * @module knalli.angular-vertxbus
47 | * @methodOf knalli.angular-vertxbus.vertxEventBusServiceProvider
48 | * @name .#useDebug
49 | *
50 | * @description
51 | * Enables a verbose mode in which certain events will be logged to `$log`.
52 | *
53 | * @param {boolean} [value=false] verbose mode (using `$log`)
54 | * @returns {object} this
55 | */
56 | this.useDebug = (value = DEFAULTS.debugEnabled) => {
57 | options.debugEnabled = (value === true);
58 | return this;
59 | };
60 |
61 | /**
62 | * @ngdoc method
63 | * @module knalli.angular-vertxbus
64 | * @methodOf knalli.angular-vertxbus.vertxEventBusServiceProvider
65 | * @name .#usePrefix
66 | *
67 | * @description
68 | * Overrides the default prefix which will be used for emitted events.
69 | *
70 | * @param {string} [value='vertx-eventbus.'] prefix used in event names
71 | * @returns {object} this
72 | */
73 | this.usePrefix = (value = DEFAULTS.prefix) => {
74 | options.prefix = value;
75 | return this;
76 | };
77 |
78 | /**
79 | * @ngdoc method
80 | * @methodOf knalli.angular-vertxbus.vertxEventBusServiceProvider
81 | * @name .#useSockJsStateInterval
82 | *
83 | *
84 | * @description
85 | * Overrides the interval of checking the connection is still valid (required for reconnecting automatically).
86 | *
87 | * @param {boolean} [value=10000] interval of checking the underlying connection's state (in ms)
88 | * @returns {object} this
89 | */
90 | this.useSockJsStateInterval = (value = DEFAULTS.sockjsStateInterval) => {
91 | options.sockjsStateInterval = value;
92 | return this;
93 | };
94 |
95 | /**
96 | * @ngdoc method
97 | * @methodOf knalli.angular-vertxbus.vertxEventBusServiceProvider
98 | * @name .#useMessageBuffer
99 | *
100 | * @description
101 | * Enables buffering of (sending) messages.
102 | *
103 | * The setting defines the total amount of buffered messages (`0` no buffering). A message will be buffered if
104 | * connection is still in progress, the connection is stale or a login is required/pending.
105 | *
106 | * @param {boolean} [value=0] allowed total amount of messages in the buffer
107 | * @returns {object} this
108 | */
109 | this.useMessageBuffer = (value = DEFAULTS.messageBuffer) => {
110 | options.messageBuffer = value;
111 | return this;
112 | };
113 |
114 | /**
115 | * @ngdoc method
116 | * @module knalli.angular-vertxbus
117 | * @methodOf knalli.angular-vertxbus.vertxEventBusServiceProvider
118 | * @name .#authHandler
119 | *
120 | * @description
121 | * Function or service reference name for function checking the authorization state.
122 | *
123 | * The result of the function must be a boolean or promise. The handler can (but is not required) to create authorization on demand.
124 | * If it is resolved, the authorization is valid.
125 | * If it is rejected, the authorization is invalid.
126 | *
127 | * @param {string|function} value authorization handler (either a function or a service name)
128 | * @returns {object} promise
129 | */
130 | this.authHandler = (value) => {
131 | options.authHandler = value;
132 | options.authRequired = !!value;
133 | return this;
134 | };
135 |
136 | /**
137 | * @ngdoc service
138 | * @module knalli.angular-vertxbus
139 | * @name knalli.angular-vertxbus.vertxEventBusService
140 | * @description
141 | * A service utilizing an underlying Vert.x Event Bus
142 | *
143 | * The advanced features of this service are:
144 | * - broadcasting the connection changes (vertx-eventbus.system.connected, vertx-eventbus.system.disconnected) on $rootScope
145 | * - registering all handlers again when a reconnect had been required
146 | * - supporting a promise when using send()
147 | * - adding aliases on (registerHandler), un (unregisterHandler) and emit (publish)
148 | *
149 | * Basic usage:
150 | *
151 | * module.controller('MyController', function('vertxEventService') {
152 | * vertxEventService.on('my.address', function(message) {
153 | * console.log("JSON Message received: ", message)
154 | * });
155 | * vertxEventService.publish('my.other.address', {type: 'foo', data: 'bar'});
156 | * });
157 | *
158 | *
159 | * Note the additional {@link knalli.angular-vertxbus.vertxEventBusServiceProvider configuration} of the module itself.
160 | *
161 | * @requires knalli.angular-vertxbus.vertxEventBus
162 | * @requires $rootScope
163 | * @requires $q
164 | * @requires $interval
165 | * @requires $log
166 | * @requires $injector
167 | */
168 | /* @ngInject */
169 | this.$get = ($rootScope, $q, $interval, vertxEventBus, $log, $injector) => {
170 | // Current options (merged defaults with application-wide settings)
171 | let instanceOptions = angular.extend({}, vertxEventBus.getOptions(), options);
172 | if (instanceOptions.enabled) {
173 | return new Delegator(
174 | new EventBusDelegate($rootScope, $interval, $log, $q, $injector, vertxEventBus, instanceOptions),
175 | $log
176 | );
177 | } else {
178 | return new Delegator(new NoopDelegate());
179 | }
180 | };
181 |
182 | };
183 |
184 | export default VertxEventBusServiceProvider;
185 |
--------------------------------------------------------------------------------
/src/lib/VertxEventBusWrapperProvider.js:
--------------------------------------------------------------------------------
1 | import EventBusAdapter from './adapter/EventBusAdapter';
2 | import NoopAdapter from './adapter/NoopAdapter';
3 | import ConnectionConfigHolder from './support/ConnectionConfigHolder';
4 |
5 | import EventBus from 'vertx-eventbus';
6 |
7 | /**
8 | * @ngdoc service
9 | * @module knalli.angular-vertxbus
10 | * @name knalli.angular-vertxbus.vertxEventBusProvider
11 | * @description
12 | * An AngularJS wrapper for projects using the VertX Event Bus
13 | */
14 |
15 | const DEFAULTS = {
16 | enabled : true,
17 | debugEnabled : false,
18 | initialConnectEnabled : true,
19 | urlServer : `${location.protocol}//${location.hostname}` + ((() => {
20 | if (location.port) {
21 | return `:${location.port}`;
22 | }
23 | })() || ''),
24 | urlPath : '/eventbus',
25 | reconnectEnabled : true,
26 | sockjsReconnectInterval : 10000,
27 | sockjsOptions : {}
28 | };
29 |
30 | let VertxEventBusWrapperProvider = function () {
31 |
32 | // options (globally, application-wide)
33 | var options = angular.extend({}, DEFAULTS);
34 |
35 | /**
36 | * @ngdoc method
37 | * @module knalli.angular-vertxbus
38 | * @methodOf knalli.angular-vertxbus.vertxEventBusProvider
39 | * @name .#enable
40 | *
41 | * @description
42 | * Enables or disables the service. This setup is immutable.
43 | *
44 | * @param {boolean} [value=true] service is enabled on startup
45 | * @returns {object} this
46 | */
47 | this.enable = (value = DEFAULTS.enabled) => {
48 | options.enabled = (value === true);
49 | return this;
50 | };
51 |
52 | /**
53 | * @ngdoc method
54 | * @module knalli.angular-vertxbus
55 | * @methodOf knalli.angular-vertxbus.vertxEventBusProvider
56 | * @name .#disableAutoConnect
57 | *
58 | * @description
59 | * Disables the auto connection feature.
60 | *
61 | * This feature will be only available if `enable == true`.
62 | *
63 | * @param {boolean} [value=true] auto connect on startup
64 | * @returns {object} this
65 | */
66 | this.disableAutoConnect = () => {
67 | options.initialConnectEnabled = false;
68 | return this;
69 | };
70 |
71 | /**
72 | * @ngdoc method
73 | * @module knalli.angular-vertxbus
74 | * @methodOf knalli.angular-vertxbus.vertxEventBusProvider
75 | * @name .#useDebug
76 | *
77 | * @description
78 | * Enables a verbose mode in which certain events will be logged to `$log`.
79 | *
80 | * @param {boolean} [value=false] verbose mode (using `$log`)
81 | * @returns {object} this
82 | */
83 | this.useDebug = (value = DEFAULTS.debugEnabled) => {
84 | options.debugEnabled = (value === true);
85 | return this;
86 | };
87 |
88 | /**
89 | * @ngdoc method
90 | * @module knalli.angular-vertxbus
91 | * @methodOf knalli.angular-vertxbus.vertxEventBusProvider
92 | * @name .#useUrlServer
93 | *
94 | * @description
95 | * Overrides the url part "server" for connecting. The default is based on
96 | * - `location.protocol`
97 | * - `location.hostname`
98 | * - `location.port`
99 | *
100 | * i.e. `http://domain.tld` or `http://domain.tld:port`
101 | *
102 | * @param {boolean} [value=$computed] server to connect (default based on `location.protocol`, `location.hostname` and `location.port`)
103 | * @returns {object} this
104 | */
105 | this.useUrlServer = (value = DEFAULTS.urlServer) => {
106 | options.urlServer = value;
107 | return this;
108 | };
109 |
110 | /**
111 | * @ngdoc method
112 | * @module knalli.angular-vertxbus
113 | * @methodOf knalli.angular-vertxbus.vertxEventBusProvider
114 | * @name .#useUrlPath
115 | *
116 | * @description
117 | * Overrides the url part "path" for connection.
118 | *
119 | * @param {boolean} [value='/eventbus'] path to connect
120 | * @returns {object} this
121 | */
122 | this.useUrlPath = (value = DEFAULTS.urlPath) => {
123 | options.urlPath = value;
124 | return this;
125 | };
126 |
127 | /**
128 | * @ngdoc method
129 | * @module knalli.angular-vertxbus
130 | * @methodOf knalli.angular-vertxbus.vertxEventBusProvider
131 | * @name .#useReconnect
132 | *
133 | * @description
134 | * Enables or disables the automatic reconnect handling.
135 | *
136 | * @param {boolean} [value=true] if a disconnect was being noted, a reconnect will be enforced automatically
137 | * @returns {object} this
138 | */
139 | this.useReconnect = (value = DEFAULTS.reconnectEnabled) => {
140 | options.reconnectEnabled = value;
141 | return this;
142 | };
143 |
144 | /**
145 | * @ngdoc method
146 | * @module knalli.angular-vertxbus
147 | * @methodOf knalli.angular-vertxbus.vertxEventBusProvider
148 | * @name .#useSockJsReconnectInterval
149 | *
150 | * @description
151 | * Overrides the timeout for reconnecting after a disconnect was found.
152 | *
153 | * @param {boolean} [value=10000] time between a disconnect and the next try to reconnect (in ms)
154 | * @returns {object} this
155 | */
156 | this.useSockJsReconnectInterval = (value = DEFAULTS.sockjsReconnectInterval) => {
157 | options.sockjsReconnectInterval = value;
158 | return this;
159 | };
160 |
161 | /**
162 | * @ngdoc method
163 | * @module knalli.angular-vertxbus
164 | * @methodOf knalli.angular-vertxbus.vertxEventBusProvider
165 | * @name .#useSockJsOptions
166 | *
167 | * @description
168 | * Sets additional params for the `SockJS` instance.
169 | *
170 | * Internally, it will be applied (as `options`) like `new SockJS(url, undefined, options)`.
171 | *
172 | * @param {boolean} [value={}] optional params for raw SockJS options
173 | * @returns {object} this
174 | */
175 | this.useSockJsOptions = (value = DEFAULTS.sockjsOptions) => {
176 | options.sockjsOptions = value;
177 | return this;
178 | };
179 |
180 | /**
181 | * @ngdoc service
182 | * @module knalli.angular-vertxbus
183 | * @name knalli.angular-vertxbus.vertxEventBus
184 | * @description
185 | * A stub representing the Vert.x EventBus (core functionality)
186 | *
187 | * Because the Event Bus cannot handle a reconnect (because of the underlaying SockJS), a
188 | * new instance of the bus have to be created.
189 | * This stub ensures only one object holding the current active instance of the bus.
190 | *
191 | * The stub supports theses Vert.x Event Bus APIs:
192 | * - {@link knalli.angular-vertxbus.vertxEventBus#methods_close close()}
193 | * - {@link knalli.angular-vertxbus.vertxEventBus#methods_send send(address, message, handler)}
194 | * - {@link knalli.angular-vertxbus.vertxEventBus#methods_publish publish(address, message)}
195 | * - {@link knalli.angular-vertxbus.vertxEventBus#methods_registerHandler registerHandler(adress, handler)}
196 | * - {@link knalli.angular-vertxbus.vertxEventBus#methods_unregisterHandler unregisterHandler(address, handler)}
197 | * - {@link knalli.angular-vertxbus.vertxEventBus#methods_readyState readyState()}
198 | *
199 | * Furthermore, the stub supports theses extra APIs:
200 | * - {@link knalli.angular-vertxbus.vertxEventBus#methods_reconnect reconnect()}
201 | *
202 | * @requires $timeout
203 | * @requires $log
204 | */
205 | /* @ngInject */
206 | this.$get = ($timeout, $log, $q) => {
207 | // Current options (merged defaults with application-wide settings)
208 | let instanceOptions = angular.extend({}, DEFAULTS, options);
209 | if (instanceOptions.enabled && EventBus) {
210 | if (instanceOptions.debugEnabled) {
211 | $log.debug('[Vert.x EB Stub] Enabled');
212 | }
213 |
214 | // aggregate server connection params
215 | instanceOptions.connectionConfig = new ConnectionConfigHolder({
216 | urlServer : instanceOptions.urlServer,
217 | urlPath : instanceOptions.urlPath
218 | });
219 | delete instanceOptions.urlServer;
220 | delete instanceOptions.urlPath;
221 |
222 | return new EventBusAdapter(EventBus, $timeout, $log, $q, instanceOptions);
223 | } else {
224 | if (instanceOptions.debugEnabled) {
225 | $log.debug('[Vert.x EB Stub] Disabled');
226 | }
227 | return new NoopAdapter(EventBus, $q);
228 | }
229 | };
230 |
231 | };
232 |
233 | export default VertxEventBusWrapperProvider;
234 |
--------------------------------------------------------------------------------
/src/lib/adapter/BaseAdapter.js:
--------------------------------------------------------------------------------
1 | export default class BaseAdapter {
2 |
3 | constructor($q) {
4 | this.$q = $q;
5 | }
6 |
7 | configureConnection() {
8 | }
9 |
10 | connect() {
11 | return this.$q.reject();
12 | }
13 |
14 | reconnect() {
15 | }
16 |
17 | close() {
18 | }
19 |
20 | send() {
21 | }
22 |
23 | publish() {
24 | }
25 |
26 | registerHandler() {
27 | }
28 |
29 | unregisterHandler() {
30 | }
31 |
32 | readyState() {
33 | }
34 |
35 | getOptions() {
36 | return {};
37 | }
38 |
39 | // empty: can be overriden by externals
40 | onopen() {
41 | }
42 |
43 | // empty: can be overriden by externals
44 | onclose() {
45 | }
46 |
47 | // private
48 | getDefaultHeaders() {
49 | return this.defaultHeaders;
50 | }
51 |
52 | /**
53 | * @ngdoc method
54 | * @module knalli.angular-vertxbus
55 | * @methodOf knalli.angular-vertxbus.vertxEventBus
56 | * @name .#applyDefaultHeaders
57 | *
58 | * @description
59 | * Stores the given default headers
60 | *
61 | * @param {object} headers additional standard headers
62 | */
63 | applyDefaultHeaders(headers = {}) {
64 | this.defaultHeaders = angular.extend({}, headers);
65 | }
66 |
67 | // private
68 | getMergedHeaders(headers = {}) {
69 | return angular.extend({}, this.defaultHeaders, headers);
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/lib/adapter/EventBusAdapter.js:
--------------------------------------------------------------------------------
1 | import {moduleName} from '../../config.js';
2 |
3 | import BaseAdapter from './BaseAdapter';
4 | import ConnectionConfigHolder from './../support/ConnectionConfigHolder';
5 |
6 | /**
7 | * @ngdoc service
8 | * @module global
9 | * @name global.EventBus
10 | *
11 | * @description
12 | * This is the interface of `EventBus`. It is not included.
13 | */
14 |
15 | /**
16 | * @ngdoc method
17 | * @module global
18 | * @methodOf global.EventBus
19 | * @name .#close
20 | */
21 |
22 | /**
23 | * @ngdoc method
24 | * @module global
25 | * @methodOf global.EventBus
26 | * @name .#send
27 | *
28 | * @param {string} address target address
29 | * @param {object} message payload message
30 | * @param {object=} headers headers
31 | * @param {function=} replyHandler optional callback
32 | * @param {function=} failureHandler optional callback
33 | */
34 |
35 | /**
36 | * @ngdoc method
37 | * @module global
38 | * @methodOf global.EventBus
39 | * @name .#publish
40 | *
41 | * @param {string} address target address
42 | * @param {object} message payload message
43 | * @param {object=} headers headers
44 | */
45 |
46 | /**
47 | * @ngdoc method
48 | * @module global
49 | * @methodOf global.EventBus
50 | * @name .#registerHandler
51 | *
52 | * @param {string} address target address
53 | * @param {function} handler callback handler
54 | * @param {object=} headers headers
55 | */
56 |
57 | /**
58 | * @ngdoc method
59 | * @module global
60 | * @methodOf global.EventBus
61 | * @name .#unregisterHandler
62 | *
63 | * @param {string} address target address
64 | * @param {function} handler callback handler to be removed
65 | * @param {object=} headers headers
66 | */
67 |
68 | /**
69 | * @ngdoc property
70 | * @module global
71 | * @propertyOf global.EventBus
72 | * @name .#onopen
73 | * @description
74 | * Defines the callback called on opening the connection.
75 | */
76 |
77 | /**
78 | * @ngdoc property
79 | * @module global
80 | * @propertyOf global.EventBus
81 | * @name .#onclose
82 | * @description
83 | * Defines the callback called on closing the connection.
84 | */
85 |
86 | /**
87 | * @ngdoc property
88 | * @module global
89 | * @propertyOf global.EventBus
90 | * @name .#onerror
91 | * @description
92 | * Defines the callback called on any error.
93 | */
94 |
95 | /**
96 | * @ngdoc property
97 | * @module global
98 | * @propertyOf global.EventBus
99 | * @name .#onreconnect
100 | * @description
101 | * Defines the callback called on reconnecting the connection (after onopen).
102 | */
103 |
104 | export default class EventBusAdapter extends BaseAdapter {
105 |
106 | constructor(EventBus,
107 | $timeout,
108 | $log,
109 | $q, {
110 | enabled,
111 | debugEnabled,
112 | initialConnectEnabled,
113 | connectionConfig,
114 | reconnectEnabled,
115 | sockjsReconnectInterval,
116 | sockjsOptions
117 | }) {
118 | super($q);
119 | // actual EventBus type
120 | this.EventBus = EventBus;
121 | this.$timeout = $timeout;
122 | this.$log = $log;
123 | this.$q = $q;
124 | this.options = {
125 | enabled,
126 | debugEnabled,
127 | initialConnectEnabled,
128 | connectionConfig,
129 | reconnectEnabled,
130 | sockjsReconnectInterval,
131 | sockjsOptions
132 | };
133 | this.nativeReconnectAvailable = false;
134 | this.disconnectTimeoutEnabled = true;
135 | this.applyDefaultHeaders();
136 | if (initialConnectEnabled) {
137 | // asap create connection
138 | this.connect();
139 | }
140 | }
141 |
142 | /**
143 | * @ngdoc method
144 | * @module knalli.angular-vertxbus
145 | * @methodOf knalli.angular-vertxbus.vertxEventBus
146 | * @name .#configureConnect
147 | *
148 | * @description
149 | * Reconfigure the connection details.
150 | *
151 | * @param {string} urlServer see {@link knalli.angular-vertxbus.vertxEventBusProvider#methods_useUrlServer vertxEventBusProvider.useUrlServer()}
152 | * @param {string} [urlPath=/eventbus] see {@link knalli.angular-vertxbus.vertxEventBusProvider#methods_useUrlPath vertxEventBusProvider.useUrlPath()}
153 | */
154 | configureConnection(urlServer, urlPath = '/eventbus') {
155 | this.options.connectionConfig = new ConnectionConfigHolder({urlServer, urlPath});
156 | return this;
157 | }
158 |
159 | connect() {
160 | // connect promise
161 | let deferred = this.$q.defer();
162 | // currently valid url
163 | let url = `${this.options.connectionConfig.urlServer}${this.options.connectionConfig.urlPath}`;
164 | if (this.options.debugEnabled) {
165 | this.$log.debug(`[Vert.x EB Stub] Enabled: connecting '${url}'`);
166 | }
167 | // Because we have rebuild an EventBus object (because it have to rebuild a SockJS object)
168 | // we must wrap the object. Therefore, we have to mimic the behavior of onopen and onclose each time.
169 | this.instance = new this.EventBus(url, this.options.sockjsOptions);
170 | // Since vertx-eventbus 3.5.0 support for "native" reconnect is available
171 | this.nativeReconnectAvailable = typeof this.instance.enableReconnect === 'function';
172 | if (this.nativeReconnectAvailable && this.options.reconnectEnabled) {
173 | this.instance.enableReconnect(true);
174 | }
175 | this.instance.onopen = () => {
176 | if (this.options.debugEnabled) {
177 | this.$log.debug('[Vert.x EB Stub] Connected');
178 | }
179 | if (angular.isFunction(this.onopen)) {
180 | this.onopen();
181 | }
182 | deferred.resolve();
183 | };
184 | // instance onClose handler
185 | this.instance.onclose = () => {
186 | // Since vertx-eventbus@3.5.0, the EventBus itself can handle reconnects
187 | if (this.nativeReconnectAvailable && this.instance.reconnectTimerID) {
188 | // 'reconnectTimerID' will be only set if reconnect is enabled
189 | if (this.options.debugEnabled) {
190 | this.$log.debug('[Vert.x EB Stub] Reconnect required, but seems to be handled by EventBus itself');
191 | }
192 | return;
193 | }
194 | if (this.options.debugEnabled) {
195 | this.$log.debug(`[Vert.x EB Stub] Reconnect in ${this.options.sockjsReconnectInterval}ms`);
196 | }
197 | if (angular.isFunction(this.onclose)) {
198 | this.onclose();
199 | }
200 | this.instance = undefined;
201 |
202 | if (!this.disconnectTimeoutEnabled) {
203 | // reconnect required asap
204 | if (this.options.debugEnabled) {
205 | this.$log.debug('[Vert.x EB Stub] Reconnect immediately');
206 | }
207 | this.disconnectTimeoutEnabled = true;
208 | this.connect();
209 | } else if (this.options.reconnectEnabled) {
210 | // automatic reconnect after timeout
211 | if (this.options.debugEnabled) {
212 | this.$log.debug(`[Vert.x EB Stub] Reconnect in ${this.options.sockjsReconnectInterval}ms`);
213 | }
214 | this.$timeout((() => this.connect()), this.options.sockjsReconnectInterval);
215 | }
216 | };
217 | // instance onError handler
218 | this.instance.onerror = (message) => {
219 | if (angular.isFunction(this.onerror)) {
220 | this.onerror(message);
221 | }
222 | };
223 | // instance onReconnect handler
224 | this.instance.onreconnect = (message) => {
225 | if (angular.isFunction(this.onreconnect)) {
226 | this.onreconnect(message);
227 | }
228 | };
229 | return deferred.promise;
230 | }
231 |
232 | /**
233 | * @ngdoc method
234 | * @module knalli.angular-vertxbus
235 | * @methodOf knalli.angular-vertxbus.vertxEventBus
236 | * @name .#reconnect
237 | *
238 | * @description
239 | * Reconnects the underlying connection.
240 | *
241 | * Unless a connection is open, it will connect using a new one.
242 | *
243 | * If a connection is already open, it will close this one and opens a new one. If `immediately` is `true`, the
244 | * default timeout for reconnect will be skipped.
245 | *
246 | * @param {boolean} [immediately=false] optionally enforce a reconnect asap instead of using the timeout
247 | */
248 | reconnect(immediately = false) {
249 | if (this.instance && this.instance.state === this.EventBus.OPEN) {
250 | if (immediately) {
251 | this.disconnectTimeoutEnabled = false;
252 | }
253 | this.instance.close();
254 | } else {
255 | this.connect();
256 | }
257 | }
258 |
259 | /**
260 | * @ngdoc method
261 | * @module knalli.angular-vertxbus
262 | * @methodOf knalli.angular-vertxbus.vertxEventBus
263 | * @name .#close
264 | *
265 | * @description
266 | * Closes the underlying connection.
267 | *
268 | * Note: If automatic reconnection is active, a new connection will be established after the {@link knalli.angular-vertxbus.vertxEventBusProvider#methods_useReconnect reconnect timeout}.
269 | *
270 | * See also:
271 | * - {@link EventBus#methods_close EventBus.close()}
272 | */
273 | close() {
274 | if (this.instance) {
275 | this.instance.close();
276 | }
277 | }
278 |
279 | /**
280 | * @ngdoc method
281 | * @module knalli.angular-vertxbus
282 | * @methodOf knalli.angular-vertxbus.vertxEventBus
283 | * @name .#send
284 | *
285 | * @description
286 | * Sends a message
287 | *
288 | * See also:
289 | * - {@link global.EventBus#methods_send EventBus.send()}
290 | *
291 | * @param {string} address target address
292 | * @param {object} message payload message
293 | * @param {object} headers optional headers
294 | * @param {function=} replyHandler optional callback
295 | */
296 | send(address, message, headers, replyHandler) {
297 | if (this.instance) {
298 | const mergedHeaders = this.getMergedHeaders(headers);
299 | this.instance.send(address, message, mergedHeaders, replyHandler);
300 | }
301 | }
302 |
303 | /**
304 | * @ngdoc method
305 | * @module knalli.angular-vertxbus
306 | * @methodOf knalli.angular-vertxbus.vertxEventBus
307 | * @name .#publish
308 | *
309 | * @description
310 | * Publishes a message
311 | *
312 | * See also:
313 | * - {@link global.EventBus#methods_publish EventBus.publish()}
314 | *
315 | * @param {string} address target address
316 | * @param {object} message payload message
317 | * @param {object=} headers optional headers
318 | */
319 | publish(address, message, headers) {
320 | if (this.instance) {
321 | const mergedHeaders = this.getMergedHeaders(headers);
322 | this.instance.publish(address, message, mergedHeaders);
323 | }
324 | }
325 |
326 | /**
327 | * @ngdoc method
328 | * @module knalli.angular-vertxbus
329 | * @methodOf knalli.angular-vertxbus.vertxEventBus
330 | * @name .#registerHandler
331 | *
332 | * @description
333 | * Registers a listener
334 | *
335 | * See also:
336 | * - {@link global.EventBus#methods_registerHandler EventBus.registerHandler()}
337 | *
338 | * @param {string} address target address
339 | * @param {object=} headers optional headers
340 | * @param {function} handler callback handler
341 | */
342 | registerHandler(address, headers, handler) {
343 | if (this.instance) {
344 | if (angular.isFunction(headers) && !handler) {
345 | handler = headers;
346 | headers = undefined;
347 | }
348 | const mergedHeaders = this.getMergedHeaders(headers);
349 | this.instance.registerHandler(address, mergedHeaders, handler);
350 | // and return the deregister callback
351 | let deconstructor = () => {
352 | this.unregisterHandler(address, mergedHeaders, handler);
353 | };
354 | deconstructor.displayName = `${moduleName}.wrapper.eventbus.registerHandler.deconstructor`;
355 | return deconstructor;
356 | }
357 | }
358 |
359 | /**
360 | * @ngdoc method
361 | * @module knalli.angular-vertxbus
362 | * @methodOf knalli.angular-vertxbus.vertxEventBus
363 | * @name .#unregisterHandler
364 | *
365 | * @description
366 | * Removes a registered a listener
367 | *
368 | * See also:
369 | * - {@link global.EventBus#methods_unregisterHandler EventBus.unregisterHandler()}
370 | *
371 | * @param {string} address target address
372 | * @param {object=} headers optional headers
373 | * @param {function} handler callback handler to be removed
374 | */
375 | unregisterHandler(address, headers, handler) {
376 | if (this.instance && this.instance.state === this.EventBus.OPEN) {
377 | if (angular.isFunction(headers) && !handler) {
378 | handler = headers;
379 | headers = undefined;
380 | }
381 | const mergedHeaders = this.getMergedHeaders(headers);
382 | this.instance.unregisterHandler(address, mergedHeaders, handler);
383 | }
384 | }
385 |
386 | /**
387 | * @ngdoc method
388 | * @module knalli.angular-vertxbus
389 | * @methodOf knalli.angular-vertxbus.vertxEventBus
390 | * @name .#readyState
391 | *
392 | * @description
393 | * Returns the current connection state
394 | *
395 | * @returns {number} value of vertx-eventbus connection states
396 | */
397 | readyState() {
398 | if (this.instance) {
399 | return this.instance.state;
400 | } else {
401 | return this.EventBus.CLOSED;
402 | }
403 | }
404 |
405 | get state() {
406 | return this.readyState();
407 | }
408 |
409 | // private
410 | getOptions() {
411 | // clone options
412 | return angular.extend({}, this.options);
413 | }
414 |
415 | }
416 |
--------------------------------------------------------------------------------
/src/lib/adapter/NoopAdapter.js:
--------------------------------------------------------------------------------
1 | import BaseAdapter from './BaseAdapter';
2 |
3 | export default class NoopAdapter extends BaseAdapter {
4 |
5 | constructor(EventBus, $q) {
6 | super($q);
7 | // actual EventBus type
8 | this.EventBus = EventBus;
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/lib/adapter/index.js:
--------------------------------------------------------------------------------
1 | import EventBusAdapter from './EventBusAdapter';
2 | import NoopAdapter from './NoopAdapter';
3 |
4 | export {EventBusAdapter, NoopAdapter};
5 |
--------------------------------------------------------------------------------
/src/lib/service/Delegator.js:
--------------------------------------------------------------------------------
1 | import {moduleName} from '../../config';
2 |
3 | export default class Delegator {
4 |
5 | constructor(delegate, $log) {
6 | this.delegate = delegate;
7 | this.$log = $log;
8 | this.handlers = {};
9 | this.delegate.observe({
10 | afterEventbusConnected: () => this.afterEventbusConnected()
11 | });
12 | }
13 |
14 | afterEventbusConnected() {
15 | for (let address in this.handlers) {
16 | if (Object.prototype.hasOwnProperty.call(this.handlers, address)) {
17 | let callbacks = this.handlers[address];
18 | if (callbacks && callbacks.length) {
19 | for (let {headers, callback} of callbacks) {
20 | this.delegate.registerHandler(address, headers, callback);
21 | }
22 | }
23 | }
24 | }
25 | }
26 |
27 | /**
28 | * @ngdoc method
29 | * @module knalli.angular-vertxbus
30 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
31 | * @name .#registerHandler
32 | *
33 | * @description
34 | * Registers a callback handler for the specified address match.
35 | *
36 | * @param {string} address target address
37 | * @param {object} headers optional headers
38 | * @param {function} callback handler with params `(message, replyTo)`
39 | * @returns {function} deconstructor
40 | */
41 | registerHandler(address, headers, callback) {
42 | if (!this.handlers[address]) {
43 | this.handlers[address] = [];
44 | }
45 | var handler = {headers, callback};
46 | this.handlers[address].push(handler);
47 | var unregisterFn = null;
48 | if (this.delegate.isConnectionOpen()) {
49 | this.delegate.registerHandler(address, headers, callback);
50 | unregisterFn = () => this.delegate.unregisterHandler(address, headers, callback);
51 | }
52 | // and return the deregister callback
53 | var deconstructor = () => {
54 | if (unregisterFn) {
55 | unregisterFn();
56 | unregisterFn = undefined;
57 | }
58 | // Remove from internal map
59 | if (this.handlers[address]) {
60 | var index = this.handlers[address].indexOf(handler);
61 | if (index > -1) {
62 | this.handlers[address].splice(index, 1);
63 | }
64 | if (this.handlers[address].length < 1) {
65 | this.handlers[address] = undefined;
66 | }
67 | }
68 | };
69 | deconstructor.displayName = `${moduleName}.service.registerHandler.deconstructor`;
70 | return deconstructor;
71 | }
72 |
73 | /**
74 | * @ngdoc method
75 | * @module knalli.angular-vertxbus
76 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
77 | * @name .#on
78 | *
79 | * @description
80 | * See (using {@link knalli.angular-vertxbus.vertxEventBusService#methods_registerHandler registerHandler()})
81 | */
82 | on(address, headers, callback) {
83 | return this.registerHandler(address, headers, callback);
84 | }
85 |
86 | /**
87 | * @ngdoc method
88 | * @module knalli.angular-vertxbus
89 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
90 | * @name .#addListener
91 | *
92 | * @description
93 | * See (using {@link knalli.angular-vertxbus.vertxEventBusService#methods_registerHandler registerHandler()})
94 | */
95 | addListener(address, headers, callback) {
96 | return this.registerHandler(address, headers, callback);
97 | }
98 |
99 | /**
100 | * @ngdoc method
101 | * @module knalli.angular-vertxbus
102 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
103 | * @name .#unregisterHandler
104 | *
105 | * @description
106 | * Removes a callback handler for the specified address match.
107 | *
108 | * @param {string} address target address
109 | * @param {object} headers optional headers
110 | * @param {function} callback handler with params `(message, replyTo)`
111 | */
112 | unregisterHandler(address, headers, callback) {
113 | // Remove from internal map
114 | if (this.handlers[address]) {
115 | var index = this.handlers[address].indexOf({headers, callback});
116 | if (index > -1) {
117 | this.handlers[address].splice(index, 1);
118 | }
119 | if (this.handlers[address].length < 1) {
120 | this.handlers[address] = undefined;
121 | }
122 | }
123 | // Remove from real instance
124 | if (this.delegate.isConnectionOpen()) {
125 | this.delegate.unregisterHandler(address, headers, callback);
126 | }
127 | }
128 |
129 | /**
130 | * @ngdoc method
131 | * @module knalli.angular-vertxbus
132 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
133 | * @name .#un
134 | *
135 | * @description
136 | * See (using {@link knalli.angular-vertxbus.vertxEventBusService#methods_registerHandler unregisterHandler()})
137 | */
138 | un(address, headers, callback) {
139 | return this.unregisterHandler(address, headers, callback);
140 | }
141 |
142 | /**
143 | * @ngdoc method
144 | * @module knalli.angular-vertxbus
145 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
146 | * @name .#removeListener
147 | *
148 | * @description
149 | * See (using {@link knalli.angular-vertxbus.vertxEventBusService#methods_registerHandler unregisterHandler()})
150 | */
151 | removeListener(address, headers, callback) {
152 | return this.unregisterHandler(address, headers, callback);
153 | }
154 |
155 | /**
156 | * @ngdoc method
157 | * @module knalli.angular-vertxbus
158 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
159 | * @name .#send
160 | *
161 | * @description
162 | * Sends a message to the specified address (using {@link knalli.angular-vertxbus.vertxEventBus#methods_send vertxEventBus.send()}).
163 | *
164 | * @param {string} address target address
165 | * @param {object} message payload message
166 | * @param {object} headers headers
167 | * @param {Object} options additional options
168 | * @param {number=} [options.timeout=10000] (in ms) after which the promise will be rejected
169 | * @param {boolean=} [options.expectReply=true] if false, the promise will be resolved directly and
170 | * no replyHandler will be created
171 | * @returns {object} promise
172 | */
173 | send(address, message, headers = {}, options = {timeout: 10000, expectReply: true}) {
174 | return this.delegate.send(address, message, headers, options.timeout, options.expectReply);
175 | }
176 |
177 | /**
178 | * @ngdoc method
179 | * @module knalli.angular-vertxbus
180 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
181 | * @name .#publish
182 | *
183 | * @description
184 | * Publishes a message to the specified address (using {@link knalli.angular-vertxbus.vertxEventBus#methods_publish vertxEventBus.publish()}).
185 | *
186 | * @param {string} address target address
187 | * @param {object} message payload message
188 | * @param {object=} headers headers
189 | * @returns {object} promise (resolved on either performed or queued)
190 | */
191 | publish(address, message, headers = {}) {
192 | return this.delegate.publish(address, message, headers);
193 | }
194 |
195 | /**
196 | * @ngdoc method
197 | * @module knalli.angular-vertxbus
198 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
199 | * @name .#emit
200 | *
201 | * @description
202 | * See (using {@link knalli.angular-vertxbus.vertxEventBusService#methods_publish publish()})
203 | */
204 | emit(address, message, headers = {}) {
205 | return this.publish(address, message, headers);
206 | }
207 |
208 | /**
209 | * @ngdoc method
210 | * @module knalli.angular-vertxbus
211 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
212 | * @name .#getConnectionState
213 | *
214 | * @description
215 | * Returns the current connection state. The state is being cached internally.
216 | *
217 | * @returns {number} state type of vertx.EventBus
218 | */
219 | getConnectionState() {
220 | return this.delegate.getConnectionState();
221 | }
222 |
223 | /**
224 | * @ngdoc method
225 | * @module knalli.angular-vertxbus
226 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
227 | * @name .#readyState
228 | *
229 | * @description
230 | * See (using {@link knalli.angular-vertxbus.vertxEventBusService#methods_getConnectionState getConnectionState()})
231 | */
232 | readyState() {
233 | return this.getConnectionState();
234 | }
235 |
236 |
237 |
238 | /**
239 | * @ngdoc method
240 | * @module knalli.angular-vertxbus
241 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
242 | * @name .#isConnectionOpen
243 | *
244 | * @description
245 | * Returns true if the current connection state ({@link knalli.angular-vertxbus.vertxEventBusService#methods_getConnectionState getConnectionState()}) is `OPEN`.
246 | *
247 | * @returns {boolean} connection open state
248 | */
249 | isConnectionOpen() {
250 | return this.isConnectionOpen();
251 | }
252 |
253 | /**
254 | * @ngdoc method
255 | * @module knalli.angular-vertxbus
256 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
257 | * @name .#isEnabled
258 | *
259 | * @description
260 | * Returns true if service is being enabled.
261 | *
262 | * @returns {boolean} state
263 | */
264 | isEnabled() {
265 | return this.delegate.isEnabled();
266 | }
267 |
268 | /**
269 | * @ngdoc method
270 | * @module knalli.angular-vertxbus
271 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
272 | * @name .#isConnected
273 | *
274 | * @description
275 | * Returns true if service (and the eventbus) is being connected.
276 | *
277 | * @returns {boolean} state
278 | */
279 | isConnected() {
280 | return this.delegate.isConnected();
281 | }
282 |
283 | /**
284 | * @ngdoc method
285 | * @module knalli.angular-vertxbus
286 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
287 | * @name .#isAuthorized
288 | *
289 | * @description
290 | * Returns true if the authorization is valid
291 | *
292 | * @returns {boolean} state
293 | */
294 | isAuthorized() {
295 | return this.delegate.isAuthorized();
296 | }
297 |
298 | /**
299 | * @ngdoc method
300 | * @module knalli.angular-vertxbus
301 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
302 | * @name .#isValidSession
303 | *
304 | * See (using {@link knalli.angular-vertxbus.vertxEventBusService#methods_isAuthorized isAuthorized()})
305 | */
306 | isValidSession() {
307 | return this.delegate.isAuthorized();
308 | }
309 |
310 | /**
311 | * @ngdoc method
312 | * @module knalli.angular-vertxbus
313 | * @methodOf knalli.angular-vertxbus.vertxEventBusService
314 | * @name .#getMessageQueueLength
315 | *
316 | * @description
317 | * Returns the current amount of messages in the internal buffer.
318 | *
319 | * @returns {number} amount
320 | */
321 | getMessageQueueLength() {
322 | return this.delegate.getMessageQueueLength();
323 | }
324 |
325 | }
326 |
--------------------------------------------------------------------------------
/src/lib/service/delegate/BaseDelegate.js:
--------------------------------------------------------------------------------
1 | export default class BaseDelegate {
2 |
3 | observe() {}
4 |
5 | getConnectionState() {
6 | return 3; // CLOSED
7 | }
8 |
9 | isConnectionOpen() {
10 | return false;
11 | }
12 |
13 | isAuthorized() {
14 | return false;
15 | }
16 |
17 | isEnabled() {
18 | return false;
19 | }
20 |
21 | isConnected() {
22 | return false;
23 | }
24 |
25 | send() {}
26 |
27 | publish() {}
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/lib/service/delegate/EventBusDelegate.js:
--------------------------------------------------------------------------------
1 | import {moduleName} from '../../../config';
2 |
3 | import Queue from './../../support/Queue';
4 | import SimpleMap from './../../support/SimpleMap';
5 | import BaseDelegate from './BaseDelegate';
6 |
7 | /**
8 | * @ngdoc event
9 | * @module knalli.angular-vertxbus
10 | * @eventOf knalli.angular-vertxbus.vertxEventBusService
11 | * @eventType broadcast on $rootScope
12 | * @name disconnected
13 | *
14 | * @description
15 | * After a connection was being terminated.
16 | *
17 | * Event name is `prefix + 'system.disconnected'` (see {@link knalli.angular-vertxbus.vertxEventBusServiceProvider#methods_usePrefix prefix})
18 | */
19 |
20 | /**
21 | * @ngdoc event
22 | * @module knalli.angular-vertxbus
23 | * @eventOf knalli.angular-vertxbus.vertxEventBusService
24 | * @eventType broadcast on $rootScope
25 | * @name connected
26 | *
27 | * @description
28 | * After a connection was being established
29 | *
30 | * Event name is `prefix + 'system.connected'` (see {@link knalli.angular-vertxbus.vertxEventBusServiceProvider#methods_usePrefix prefix})
31 | */
32 |
33 | /**
34 | * @ngdoc event
35 | * @module knalli.angular-vertxbus
36 | * @eventOf knalli.angular-vertxbus.vertxEventBusService
37 | * @eventType broadcast on $rootScope
38 | * @name reconnected
39 | *
40 | * @description
41 | * After a connection was being re-established
42 | *
43 | * Event name is `prefix + 'system.reconnected'` (see {@link knalli.angular-vertxbus.vertxEventBusServiceProvider#methods_usePrefix prefix})
44 | */
45 |
46 | /**
47 | * @ngdoc event
48 | * @module knalli.angular-vertxbus
49 | * @eventOf knalli.angular-vertxbus.vertxEventBusService
50 | * @eventType broadcast on $rootScope
51 | * @name login-succeeded
52 | *
53 | * @description
54 | * After a login has been validated successfully
55 | *
56 | * Event name is `prefix + 'system.login.succeeded'` (see {@link knalli.angular-vertxbus.vertxEventBusServiceProvider#methods_usePrefix prefix})
57 | *
58 | * @param {object} data data
59 | * @param {boolean} data.status must be `'ok'`
60 | */
61 |
62 | /**
63 | * @ngdoc event
64 | * @module knalli.angular-vertxbus
65 | * @eventOf knalli.angular-vertxbus.vertxEventBusService
66 | * @eventType broadcast on $rootScope
67 | * @name login-failed
68 | *
69 | * @description
70 | * After a login has been destroyed or was invalidated
71 | *
72 | * Event name is `prefix + 'system.login.failed'` (see {@link knalli.angular-vertxbus.vertxEventBusServiceProvider#methods_usePrefix prefix})
73 | *
74 | * @param {object} data data
75 | * @param {boolean} data.status must be not`'ok'`
76 | */
77 |
78 | export default class EventBusDelegate extends BaseDelegate {
79 |
80 | constructor($rootScope,
81 | $interval,
82 | $log,
83 | $q,
84 | $injector,
85 | eventBus,
86 | {
87 | enabled,
88 | debugEnabled,
89 | prefix,
90 | sockjsStateInterval,
91 | messageBuffer,
92 | authRequired,
93 | authHandler
94 | }) {
95 | super();
96 | this.$rootScope = $rootScope;
97 | this.$interval = $interval;
98 | this.$log = $log;
99 | this.$q = $q;
100 | this.eventBus = eventBus;
101 | this.options = {
102 | enabled,
103 | debugEnabled,
104 | prefix,
105 | sockjsStateInterval,
106 | messageBuffer,
107 | authRequired
108 | };
109 | if (angular.isFunction(authHandler)) {
110 | this.authHandler = authHandler;
111 | } else if (angular.isString(authHandler)) {
112 | try {
113 | this.authHandler = $injector.get(authHandler);
114 | } catch (e) {
115 | if (this.options.debugEnabled) {
116 | this.$log.debug('[Vert.x EB Service] Failed to resolve authHandler: %s', e.message);
117 | }
118 | }
119 | }
120 | this.connectionState = this.eventBus.EventBus.CLOSED;
121 | this.states = {
122 | connected : false,
123 | authorized : false
124 | };
125 | this.observers = [];
126 | // internal store of buffered messages
127 | this.messageQueue = new Queue(this.options.messageBuffer);
128 | // internal map of callbacks
129 | this.callbackMap = new SimpleMap();
130 | // asap
131 | this.initialize();
132 | }
133 |
134 | // internal
135 | initialize() {
136 | this.eventBus.onopen = () => this.onEventbusOpen();
137 | this.eventBus.onclose = () => this.onEventbusClose();
138 | this.eventBus.onreconnect = () => this.onEventbusReconnect();
139 |
140 | // Update the current connection state periodically.
141 | let connectionIntervalCheck = () => this.getConnectionState(true);
142 | connectionIntervalCheck.displayName = 'connectionIntervalCheck';
143 | this.$interval((() => connectionIntervalCheck()), this.options.sockjsStateInterval);
144 | }
145 |
146 | // internal
147 | onEventbusOpen() {
148 | let connectionStateFlipped = false;
149 | this.getConnectionState(true);
150 | if (!this.states.connected) {
151 | this.states.connected = true;
152 | connectionStateFlipped = true;
153 | }
154 | // Ensure all events will be re-attached
155 | this.afterEventbusConnected();
156 | // Everything is online and registered again, let's notify everybody
157 | if (connectionStateFlipped) {
158 | this.$rootScope.$broadcast(`${this.options.prefix}system.connected`);
159 | }
160 | this.$rootScope.$digest(); // explicitly
161 | // consume message queue?
162 | if (this.options.messageBuffer && this.messageQueue.size()) {
163 | while (this.messageQueue.size()) {
164 | let fn = this.messageQueue.first();
165 | if (angular.isFunction(fn)) {
166 | fn();
167 | }
168 | }
169 | this.$rootScope.$digest();
170 | }
171 | }
172 |
173 | // internal
174 | onEventbusClose() {
175 | this.getConnectionState(true);
176 | if (this.states.connected) {
177 | this.states.connected = false;
178 | this.$rootScope.$broadcast(`${this.options.prefix}system.disconnected`);
179 | }
180 | }
181 |
182 | // internal
183 | onEventbusReconnect() {
184 | // will be fired after a onEventbusOpen
185 | if (this.states.connected) {
186 | this.$rootScope.$broadcast(`${this.options.prefix}system.reconnected`);
187 | }
188 | }
189 |
190 | // internal
191 | observe(observer) {
192 | this.observers.push(observer);
193 | }
194 |
195 | // internal
196 | afterEventbusConnected() {
197 | for (let observer of this.observers) {
198 | if (angular.isFunction(observer.afterEventbusConnected)) {
199 | observer.afterEventbusConnected();
200 | }
201 | }
202 | }
203 |
204 | registerHandler(address, headers, callback) {
205 | if (angular.isFunction(headers) && !callback) {
206 | callback = headers;
207 | headers = undefined;
208 | }
209 | if (!angular.isFunction(callback)) {
210 | return;
211 | }
212 | if (this.options.debugEnabled) {
213 | this.$log.debug(`[Vert.x EB Service] Register handler for ${address}`);
214 | }
215 | var callbackWrapper = (err, {body}, replyTo) => {
216 | callback(body, replyTo);
217 | this.$rootScope.$digest();
218 | };
219 | callbackWrapper.displayName = `${moduleName}.service.delegate.live.registerHandler.callbackWrapper`;
220 | this.callbackMap.put(callback, callbackWrapper);
221 | return this.eventBus.registerHandler(address, headers, callbackWrapper);
222 | }
223 |
224 | unregisterHandler(address, headers, callback) {
225 | if (angular.isFunction(headers) && !callback) {
226 | callback = headers;
227 | headers = undefined;
228 | }
229 | if (!angular.isFunction(callback)) {
230 | return;
231 | }
232 | if (this.options.debugEnabled) {
233 | this.$log.debug(`[Vert.x EB Service] Unregister handler for ${address}`);
234 | }
235 | this.eventBus.unregisterHandler(address, headers, this.callbackMap.get(callback));
236 | this.callbackMap.remove(callback);
237 | }
238 |
239 | send(address, message, headers, timeout = 10000, expectReply = true) {
240 | let deferred = this.$q.defer();
241 | let next = () => {
242 | if (expectReply) {
243 | // Register timeout for promise rejecting
244 | let timer = this.$interval((() => {
245 | if (this.options.debugEnabled) {
246 | this.$log.debug(`[Vert.x EB Service] send('${address}') timed out`);
247 | }
248 | deferred.reject();
249 | }), timeout, 1);
250 | // Send message
251 | try {
252 | this.eventBus.send(address, message, headers, (err, reply) => {
253 | this.$interval.cancel(timer); // because it's resolved
254 | if (err) {
255 | deferred.reject(err);
256 | } else {
257 | deferred.resolve(reply);
258 | }
259 | });
260 | } catch (e) {
261 | this.$interval.cancel(timer); // because it's resolved
262 | deferred.reject(e);
263 | }
264 | } else {
265 | try {
266 | this.eventBus.send(address, message, headers);
267 | deferred.resolve();
268 | } catch (e) {
269 | deferred.reject(e);
270 | }
271 | }
272 | };
273 | next.displayName = `${moduleName}.service.delegate.live.send.next`;
274 | this.ensureOpenAuthConnection(next).then(null, deferred.reject);
275 | return deferred.promise;
276 | }
277 |
278 | publish(address, message, headers) {
279 | return this.ensureOpenAuthConnection(() => this.eventBus.publish(address, message, headers));
280 | }
281 |
282 | /**
283 | * Ensures the callback will be performed with an open connection.
284 | *
285 | * Unless an open connection was found, the callback will be queued in the message buffer (if available).
286 | *
287 | * @param {function} fn callback
288 | * @returns {object} promise (resolved on either performed or queued)
289 | */
290 | ensureOpenConnection(fn) {
291 | const deferred = this.$q.defer();
292 | if (this.isConnectionOpen()) {
293 | try {
294 | fn();
295 | } catch (e) {
296 | deferred.reject(e);
297 | }
298 | deferred.resolve({
299 | inQueue : false
300 | });
301 | } else if (this.options.messageBuffer) {
302 | this.messageQueue.push(fn);
303 | deferred.resolve({
304 | inQueue : true
305 | });
306 | } else {
307 | deferred.reject();
308 | }
309 | return deferred.promise;
310 | }
311 |
312 | /**
313 | * Ensures the callback will be performed with a valid session.
314 | *
315 | * Unless `authRequired` is enabled, this will be simple forward.
316 | *
317 | * Unless a valid session exist (but required), the callback will be not invoked.
318 | *
319 | * @param {function} fn callback
320 | * @returns {object} promise (resolved on either performed or queued)
321 | */
322 | ensureOpenAuthConnection(fn) {
323 | if (!this.options.authRequired) {
324 | // easy: no login required
325 | return this.ensureOpenConnection(fn);
326 | } else {
327 | let fnWrapper = () => {
328 | if (this.authHandler) {
329 | const onValidAuth = () => {
330 | this.states.authorized = true;
331 | fn();
332 | };
333 | const onInvalidAuth = () => {
334 | this.states.authorized = false;
335 | if (this.options.debugEnabled) {
336 | this.$log.debug('[Vert.x EB Service] Message was not sent due authHandler rejected');
337 | }
338 | };
339 | const authResult = this.authHandler(this.eventBus);
340 | if (!(authResult && angular.isFunction(authResult.then))) {
341 | if (this.options.debugEnabled) {
342 | this.$log.debug('[Vert.x EB Service] Message was not sent because authHandler is returning not a promise');
343 | }
344 | return false;
345 | }
346 | authResult.then(onValidAuth, onInvalidAuth);
347 | return true;
348 | } else {
349 | // ignore this message
350 | if (this.options.debugEnabled) {
351 | this.$log.debug('[Vert.x EB Service] Message was not sent because no authHandler is defined');
352 | }
353 | return false;
354 | }
355 | };
356 | fnWrapper.displayName = `${moduleName}.service.delegate.live.ensureOpenAuthConnection.fnWrapper`;
357 | return this.ensureOpenConnection(fnWrapper);
358 | }
359 | }
360 |
361 | /**
362 | * Returns the current connection state. The state is being cached internally.
363 | *
364 | * @param {boolean=} [immediate=false] if true, the connection state will be queried directly.
365 | * @returns {number} state type of vertx.EventBus
366 | */
367 | getConnectionState(immediate) {
368 | if (this.options.enabled) {
369 | if (immediate) {
370 | this.connectionState = this.eventBus.state;
371 | }
372 | } else {
373 | this.connectionState = this.eventBus.EventBus.CLOSED;
374 | }
375 | return this.connectionState;
376 | }
377 |
378 | /**
379 | * Returns true if the current connection state ({@link knalli.angular-vertxbus.vertxEventBusService#methods_getConnectionState getConnectionState()}) is `OPEN`.
380 | *
381 | * @returns {boolean} connection open state
382 | */
383 | isConnectionOpen() {
384 | return this.getConnectionState() === this.eventBus.EventBus.OPEN;
385 | }
386 |
387 | /**
388 | * Returns true if the session is valid
389 | *
390 | * @returns {boolean} state
391 | */
392 | isAuthorized() {
393 | return this.states.authorized;
394 | }
395 |
396 | // internal
397 | isConnected() {
398 | return this.states.connected;
399 | }
400 |
401 | isEnabled() {
402 | return this.options.enabled;
403 | }
404 |
405 | /**
406 | * Returns the current amount of messages in the internal buffer.
407 | *
408 | * @returns {number} amount
409 | */
410 | getMessageQueueLength() {
411 | return this.messageQueue.size();
412 | }
413 |
414 | }
415 |
--------------------------------------------------------------------------------
/src/lib/service/delegate/NoopDelegate.js:
--------------------------------------------------------------------------------
1 | import BaseDelegate from './BaseDelegate';
2 |
3 | export default class NoopDelegate extends BaseDelegate {}
4 |
--------------------------------------------------------------------------------
/src/lib/support/ConnectionConfigHolder.js:
--------------------------------------------------------------------------------
1 | export default class ConnectionConfigHolder {
2 |
3 | constructor({urlServer, urlPath}) {
4 | this._urlServer = urlServer;
5 | this._urlPath = urlPath;
6 | }
7 |
8 | get urlServer() {
9 | return this._urlServer;
10 | }
11 |
12 | get urlPath() {
13 | return this._urlPath;
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/lib/support/Queue.js:
--------------------------------------------------------------------------------
1 | /*
2 | Simple queue implementation
3 |
4 | FIFO: #push() + #first()
5 | LIFO: #push() + #last()
6 | */
7 | export default class Queue {
8 |
9 | constructor(maxSize = 10) {
10 | this.maxSize = maxSize;
11 | this.items = [];
12 | }
13 |
14 | push(item) {
15 | this.items.push(item);
16 | return this.recalibrateBufferSize();
17 | }
18 |
19 | recalibrateBufferSize() {
20 | while (this.items.length > this.maxSize) {
21 | this.first();
22 | }
23 | return this;
24 | }
25 |
26 | last() {
27 | return this.items.pop();
28 | }
29 |
30 | first() {
31 | return this.items.shift(0);
32 | }
33 |
34 | size() {
35 | return this.items.length;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/lib/support/SimpleMap.js:
--------------------------------------------------------------------------------
1 | /*
2 | Simple Map implementation
3 |
4 | This implementation allows usage of non serializable keys for values.
5 | */
6 | export default class SimpleMap {
7 |
8 | constructor() {
9 | this.clear();
10 | }
11 |
12 | // Stores the value under the key.
13 | // Chainable
14 | put(key, value) {
15 | var idx = this._indexForKey(key);
16 | if (idx > -1) {
17 | this.values[idx] = value;
18 | } else {
19 | this.keys.push(key);
20 | this.values.push(value);
21 | }
22 | return this;
23 | }
24 |
25 | // Returns value for key, otherwise undefined.
26 | get(key) {
27 | var idx = this._indexForKey(key);
28 | if (idx > -1) {
29 | return this.values[idx];
30 | }
31 | }
32 |
33 | // Returns true if the key exists.
34 | containsKey(key) {
35 | let idx = this._indexForKey(key);
36 | return idx > -1;
37 | }
38 |
39 | // Returns true if the value exists.
40 | containsValue(value) {
41 | let idx = this._indexForValue(value);
42 | return idx > -1;
43 | }
44 |
45 | // Removes the key and its value.
46 | remove(key) {
47 | let idx = this._indexForKey(key);
48 | if (idx > -1) {
49 | this.keys[idx] = undefined;
50 | this.values[idx] = undefined;
51 | }
52 |
53 | }
54 |
55 | // Clears all keys and values.
56 | clear() {
57 | this.keys = [];
58 | this.values = [];
59 | return this;
60 | }
61 |
62 | // Returns index of key, otherwise -1.
63 | _indexForKey(key) {
64 | for (let i in this.keys) {
65 | if (key === this.keys[i]) {
66 | return i;
67 | }
68 | }
69 | return -1;
70 | }
71 |
72 | _indexForValue(value) {
73 | for (let i in this.values) {
74 | if (value === this.values[i]) {
75 | return i;
76 | }
77 | }
78 | return -1;
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/lib/support/index.js:
--------------------------------------------------------------------------------
1 | import Queue from './Queue';
2 | import SimpleMap from './SimpleMap';
3 |
4 | export {Queue, SimpleMap};
5 |
--------------------------------------------------------------------------------
/src/module.js:
--------------------------------------------------------------------------------
1 | import {moduleName} from './config';
2 |
3 | import VertxEventBusWrapperProvider from './lib/VertxEventBusWrapperProvider';
4 | import VertxEventBusServiceProvider from './lib/VertxEventBusServiceProvider';
5 |
6 | /**
7 | * @ngdoc overview
8 | * @module knalli.angular-vertxbus
9 | * @name knalli.angular-vertxbus
10 | * @description
11 | *
12 | * Client side library using VertX Event Bus as an Angular Service module
13 | *
14 | * You have to define the module dependency, this module is named `knalli.angular-vertxbus`.
15 | *
16 | *
17 | * angular.module('app', ['knalli.angular-vertxbus'])
18 | * .controller('MyCtrl', function(vertxEventBus, vertxEventBusService) {
19 | *
20 | * // using the EventBus directly
21 | * vertxEventBus.send('my.address', {data: 123}, function (reply) {
22 | * // your reply comes here
23 | * });
24 | *
25 | * // using the service
26 | * vertxEventBusService.send('my.address', {data: 123}, {timeout: 500})
27 | * .then(function (reply) {
28 | * // your reply comes here
29 | * })
30 | * .catch(function (err) {
31 | * // something went wrong, no connection, no login, timed out, or so
32 | * });
33 | * });
34 | *
35 | *
36 | * The module itself provides following components:
37 | * - {@link knalli.angular-vertxbus.vertxEventBus vertxEventBus}: a low level wrapper around `vertx.EventBus`
38 | * - {@link knalli.angular-vertxbus.vertxEventBusService vertxEventBusService}: a high level service around the wrapper
39 | *
40 | * While the wrapper only provides one single instance (even on reconnects), the service supports automatically
41 | * reconnect management, authorization and a clean promise based api.
42 | *
43 | * If you are looking for a low integration of `vertxbus.EventBus` as an AngularJS component, the wrapper will be your
44 | * choice. The only difference (and requirement for the wrapper actually) is how it will manage and replace the
45 | * underlying instance of the current `vertx.EventBus`.
46 | *
47 | * However, if you are looking for a simple, clean and promised based high api, the service is much better you.
48 | */
49 | export default angular
50 |
51 | .module(moduleName, ['ng'])
52 |
53 | .provider('vertxEventBus', VertxEventBusWrapperProvider)
54 | .provider('vertxEventBusService', VertxEventBusServiceProvider)
55 |
56 | .name;
57 |
--------------------------------------------------------------------------------
/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jasmine": true,
4 | "node": true
5 | },
6 | "plugins": [
7 | "angular"
8 | ],
9 | "globals": {
10 | "inject": false
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/e2e/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 |
4 | app.use(express.static(__dirname + '/web'));
5 | app.use(express.static(__dirname + '/../../'));
6 |
7 | let server = app.listen(3000, () => console.log(`Listening on port ${server.address().port}`));
8 |
--------------------------------------------------------------------------------
/test/e2e/vertx/app/vertx3-config.js:
--------------------------------------------------------------------------------
1 | var Router = require("vertx-web-js/router");
2 | var SockJSHandler = require("vertx-web-js/sock_js_handler");
3 |
4 | var router = Router.router(vertx);
5 |
6 | var options = {
7 | 'inboundPermitteds' : [
8 | {
9 | 'address' : 'commands'
10 | }
11 | ],
12 | 'outboundPermitteds' : [
13 | {
14 | 'address' : 'what-time-is-it'
15 | }
16 | ]
17 | };
18 |
19 | // We need cookies, sessions and request bodies
20 | //router.route().handler(CookieHandler.create().handle);
21 | //router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)).handle);
22 |
23 | router.route("/eventbus/*").handler(SockJSHandler.create(vertx).bridge(options).handle);
24 |
25 | vertx.createHttpServer().requestHandler(router.accept).listen(8080);
26 |
27 | // de-knallisworld-mock
28 |
29 | vertx.eventBus().consumer('commands', function (message) {
30 | var headers = message.headers();
31 | var body = message.body();
32 | var token = headers.get('token');
33 | console.log('Intercepted token: ' + token);
34 | if (token && token.substring(0, 6) === 'VALID-') {
35 | if (body.type === 'PING') {
36 | message.reply({
37 | type : 'PONG'
38 | });
39 | } else {
40 | message.reply({
41 | type : 'NON-PING'
42 | });
43 | }
44 | } else {
45 | message.reply({
46 | type : 'NO_AUTH'
47 | });
48 | }
49 | });
50 |
51 | vertx.setPeriodic(1000, function (timerId) {
52 | vertx.eventBus().publish('what-time-is-it', {
53 | time : new Date().getTime()
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/test/e2e/vertx/install-vertx.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | ARCHIVE="vert.x-3.5.1-full.tar.gz"
3 | DIRECTORY="runtime"
4 | [ -e ${ARCHIVE} ] || wget https://bintray.com/artifact/download/vertx/downloads/${ARCHIVE}
5 | if [ ! -e ${DIRECTORY} ]; then
6 | mkdir ${DIRECTORY}; (cd ${DIRECTORY} && tar -zxf ../${ARCHIVE})
7 | fi
8 |
--------------------------------------------------------------------------------
/test/e2e/web/app.js:
--------------------------------------------------------------------------------
1 | /* global angular:false,console:false */
2 | (function () {
3 | 'use strict';
4 |
5 | angular.module('app', ['ng', 'knalli.angular-vertxbus'])
6 | .config(function(vertxEventBusProvider, vertxEventBusServiceProvider) {
7 | vertxEventBusProvider
8 | .useDebug(true)
9 | .useUrlServer('http://localhost:8080');
10 | vertxEventBusServiceProvider
11 | .useDebug(true)
12 | .authHandler('myCustomAuthHandler');
13 | })
14 | .run(function ($rootScope, vertxEventBus, vertxEventBusService, $interval) {
15 | $rootScope.sessionIsValid = false;
16 |
17 | $rootScope.moduleStats = {
18 | wrapper: {},
19 | service: {}
20 | };
21 | $interval(function () {
22 | try {
23 | $rootScope.moduleStats.wrapper.readyState = vertxEventBus.readyState();
24 | $rootScope.moduleStats.service.readyState = vertxEventBusService.readyState();
25 | $rootScope.moduleStats.service.getConnectionState = vertxEventBusService.getConnectionState();
26 | $rootScope.moduleStats.service.isEnabled = vertxEventBusService.isEnabled();
27 | $rootScope.moduleStats.service.isConnected = vertxEventBusService.isConnected();
28 | $rootScope.moduleStats.service.isAuthorized = vertxEventBusService.isAuthorized();
29 | } catch (e) {}
30 | }, 1000);
31 | })
32 | .filter('eventBusState', function () {
33 | var states = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
34 | return function (value) {
35 | return states[value] || value;
36 | };
37 | })
38 | .service('myCustomAuthHandler', function (vertxEventBus, $q) {
39 | var states = {
40 | enabled: false
41 | };
42 | var service = function () {
43 | console.log('authHandler invoked', states);
44 | return $q(function (resolve, reject) {
45 | if (states.enabled) {
46 | vertxEventBus.applyDefaultHeaders({
47 | token: 'VALID-123'
48 | });
49 | resolve();
50 | } else {
51 | reject();
52 | }
53 | });
54 | };
55 | service.start = function () {
56 | states.enabled = true;
57 | };
58 | service.stop = function () {
59 | states.enabled = false;
60 | vertxEventBus.applyDefaultHeaders({});
61 | };
62 | return service;
63 | })
64 | .controller('MyController', function($scope, vertxEventBus, vertxEventBusService, myCustomAuthHandler) {
65 | var me = this;
66 | var holder = {};
67 | me.timeServiceActive = false;
68 | me.registerTimeService = function () {
69 | holder.timeServiceDeconstructor = vertxEventBusService.on('what-time-is-it', function (data) {
70 | me.currentDateTime = new Date(data.time);
71 | });
72 | me.timeServiceActive = true;
73 | };
74 | me.deregisterTimeService = function () {
75 | holder.timeServiceDeconstructor();
76 | holder.timeServiceDeconstructor = undefined;
77 | me.timeServiceActive = false;
78 | };
79 | me.deregisterTimeService2x = function () {
80 | holder.timeServiceDeconstructor();
81 | holder.timeServiceDeconstructor();
82 | holder.timeServiceDeconstructor = undefined;
83 | me.timeServiceActive = false;
84 | };
85 |
86 | me.timeServiceActive = false;
87 | me.registerTimeService = function () {
88 | holder.timeServiceDeconstructor = vertxEventBusService.on('what-time-is-it', function (data) {
89 | me.currentDateTime = new Date(data.time);
90 | });
91 | me.timeServiceActive = true;
92 | };
93 | me.deregisterTimeService = function () {
94 | holder.timeServiceDeconstructor();
95 | holder.timeServiceDeconstructor = undefined;
96 | me.timeServiceActive = false;
97 | };
98 | me.refreshDefaultHeaders = function(token) {
99 | vertxEventBus.applyDefaultHeaders({
100 | token: token
101 | });
102 | };
103 | var sendCommand = function (type) {
104 | vertxEventBusService.send('commands', {type: type})
105 | .then(function (message) {
106 | console.log('Command succeeded: ' + message.body.type)
107 | }, function () {
108 | console.log('Command failed')
109 | });
110 | };
111 | me.sendPing = function () {
112 | sendCommand('PING');
113 | };
114 | me.sendNonPing = function () {
115 | sendCommand('INVALID');
116 | };
117 | me.enableAuthHandler = function () {
118 | myCustomAuthHandler.start();
119 | };
120 | me.disableAuthHandler = function () {
121 | myCustomAuthHandler.stop();
122 | };
123 | });
124 | }());
125 |
--------------------------------------------------------------------------------
/test/e2e/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | knalli.angular-vertxbus
4 |
5 |
6 |
7 |
8 |
28 |
29 |