feat(WebWorker) Add channel support to MessageBus
closes #3661 and #3686
This commit is contained in:
94
modules/angular2/test/web-workers/shared/message_bus_spec.ts
Normal file
94
modules/angular2/test/web-workers/shared/message_bus_spec.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
createTestInjector,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {MessageBusInterface} from 'angular2/src/web-workers/shared/message_bus';
|
||||
import {createConnectedMessageBus} from './message_bus_util';
|
||||
|
||||
export function main() {
|
||||
/**
|
||||
* Tests the PostMessageBus in TypeScript and the IsolateMessageBus in Dart
|
||||
*/
|
||||
describe("MessageBus", () => {
|
||||
var bus: MessageBusInterface;
|
||||
|
||||
beforeEach(() => { bus = createConnectedMessageBus(); });
|
||||
|
||||
it("should pass messages in the same channel from sink to source",
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
const CHANNEL = "CHANNEL 1";
|
||||
const MESSAGE = "Test message";
|
||||
|
||||
var fromEmitter = bus.from(CHANNEL);
|
||||
ObservableWrapper.subscribe(fromEmitter, (message: any) => {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
async.done();
|
||||
});
|
||||
var toEmitter = bus.to(CHANNEL);
|
||||
ObservableWrapper.callNext(toEmitter, MESSAGE);
|
||||
}));
|
||||
|
||||
it("should broadcast", inject([AsyncTestCompleter], (async) => {
|
||||
const CHANNEL = "CHANNEL 1";
|
||||
const MESSAGE = "TESTING";
|
||||
const NUM_LISTENERS = 2;
|
||||
|
||||
var callCount = 0;
|
||||
var emitHandler = (message: any) => {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
callCount++;
|
||||
if (callCount == NUM_LISTENERS) {
|
||||
async.done();
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < NUM_LISTENERS; i++) {
|
||||
var emitter = bus.from(CHANNEL);
|
||||
ObservableWrapper.subscribe(emitter, emitHandler);
|
||||
}
|
||||
|
||||
var toEmitter = bus.to(CHANNEL);
|
||||
ObservableWrapper.callNext(toEmitter, MESSAGE);
|
||||
}));
|
||||
|
||||
it("should keep channels independent", inject([AsyncTestCompleter], (async) => {
|
||||
const CHANNEL_ONE = "CHANNEL 1";
|
||||
const CHANNEL_TWO = "CHANNEL 2";
|
||||
const MESSAGE_ONE = "This is a message on CHANNEL 1";
|
||||
const MESSAGE_TWO = "This is a message on CHANNEL 2";
|
||||
var callCount = 0;
|
||||
|
||||
var firstFromEmitter = bus.from(CHANNEL_ONE);
|
||||
ObservableWrapper.subscribe(firstFromEmitter, (message) => {
|
||||
expect(message).toEqual(MESSAGE_ONE);
|
||||
callCount++;
|
||||
if (callCount == 2) {
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
var secondFromEmitter = bus.from(CHANNEL_TWO);
|
||||
ObservableWrapper.subscribe(secondFromEmitter, (message) => {
|
||||
expect(message).toEqual(MESSAGE_TWO);
|
||||
callCount++;
|
||||
if (callCount == 2) {
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
var firstToEmitter = bus.to(CHANNEL_ONE);
|
||||
ObservableWrapper.callNext(firstToEmitter, MESSAGE_ONE);
|
||||
|
||||
var secondToEmitter = bus.to(CHANNEL_TWO);
|
||||
ObservableWrapper.callNext(secondToEmitter, MESSAGE_TWO);
|
||||
}));
|
||||
});
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
library angular2.test.web_workers.shared.message_bus_util;
|
||||
|
||||
import 'dart:isolate';
|
||||
import 'package:angular2/src/web-workers/shared/message_bus.dart'
|
||||
show MessageBusInterface;
|
||||
import 'package:angular2/src/web-workers/shared/isolate_message_bus.dart';
|
||||
|
||||
/*
|
||||
* Returns an IsolateMessageBus thats sink is connected to its own source.
|
||||
* Useful for testing the sink and source.
|
||||
*/
|
||||
MessageBusInterface createConnectedMessageBus() {
|
||||
var receivePort = new ReceivePort();
|
||||
var sendPort = receivePort.sendPort;
|
||||
|
||||
var sink = new IsolateMessageBusSink(sendPort);
|
||||
var source = new IsolateMessageBusSource(receivePort);
|
||||
|
||||
return new IsolateMessageBus(sink, source);
|
||||
}
|
30
modules/angular2/test/web-workers/shared/message_bus_util.ts
Normal file
30
modules/angular2/test/web-workers/shared/message_bus_util.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import {
|
||||
PostMessageBusSource,
|
||||
PostMessageBusSink,
|
||||
PostMessageBus
|
||||
} from 'angular2/src/web-workers/shared/post_message_bus';
|
||||
import {MessageBusInterface} from 'angular2/src/web-workers/shared/message_bus';
|
||||
|
||||
/*
|
||||
* Returns a PostMessageBus thats sink is connected to its own source.
|
||||
* Useful for testing the sink and source.
|
||||
*/
|
||||
export function createConnectedMessageBus(): MessageBusInterface {
|
||||
var mockPostMessage = new MockPostMessage();
|
||||
var source = new PostMessageBusSource(<any>mockPostMessage);
|
||||
var sink = new PostMessageBusSink(mockPostMessage);
|
||||
|
||||
return new PostMessageBus(sink, source);
|
||||
}
|
||||
|
||||
class MockPostMessage {
|
||||
private _listener: EventListener;
|
||||
|
||||
addEventListener(type: string, listener: EventListener, useCapture?: boolean): void {
|
||||
if (type === "message") {
|
||||
this._listener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
postMessage(data: any, transfer?:[ArrayBuffer]): void { this._listener(<any>{data: data}); }
|
||||
}
|
@ -13,8 +13,6 @@ import {
|
||||
import {IMPLEMENTS} from 'angular2/src/facade/lang';
|
||||
import {Serializer} from 'angular2/src/web-workers/shared/serializer';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
import {MessageBroker} from 'angular2/src/web-workers/worker/broker';
|
||||
import {MockMessageBus, MockMessageBusSink, MockMessageBusSource} from './worker_test_util';
|
||||
import {ON_WEB_WORKER} from 'angular2/src/web-workers/shared/api';
|
||||
import {bind} from 'angular2/di';
|
||||
import {RenderProtoViewRefStore} from 'angular2/src/web-workers/shared/render_proto_view_ref_store';
|
||||
@ -23,9 +21,13 @@ import {
|
||||
WebWorkerRenderViewRef
|
||||
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||
import {RenderEventDispatcher, RenderViewRef} from 'angular2/src/render/api';
|
||||
import {createPairedMessageBuses} from './worker_test_util';
|
||||
import {WebWorkerEventDispatcher} from 'angular2/src/web-workers/worker/event_dispatcher';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {EVENT_CHANNEL} from 'angular2/src/web-workers/shared/messaging_api';
|
||||
|
||||
export function main() {
|
||||
describe("MessageBroker", () => {
|
||||
describe("EventDispatcher", () => {
|
||||
beforeEachBindings(() => [
|
||||
bind(ON_WEB_WORKER)
|
||||
.toValue(true),
|
||||
@ -33,47 +35,41 @@ export function main() {
|
||||
RenderViewWithFragmentsStore
|
||||
]);
|
||||
|
||||
it("should dispatch events", inject([Serializer, NgZone], (serializer, zone) => {
|
||||
var bus = new MockMessageBus(new MockMessageBusSink(), new MockMessageBusSource());
|
||||
var broker = new MessageBroker(bus, serializer, zone);
|
||||
|
||||
var eventDispatcher = new SpyEventDispatcher();
|
||||
var viewRef = new WebWorkerRenderViewRef(0);
|
||||
serializer.allocateRenderViews(0); // serialize the ref so it's in the store
|
||||
viewRef =
|
||||
serializer.deserialize(serializer.serialize(viewRef, RenderViewRef), RenderViewRef);
|
||||
broker.registerEventDispatcher(viewRef, eventDispatcher);
|
||||
it("should dispatch events",
|
||||
inject([Serializer, NgZone, AsyncTestCompleter], (serializer, zone, async) => {
|
||||
var messageBuses = createPairedMessageBuses();
|
||||
var webWorkerEventDispatcher =
|
||||
new WebWorkerEventDispatcher(messageBuses.worker, serializer, zone);
|
||||
|
||||
var elementIndex = 15;
|
||||
var eventName = 'click';
|
||||
|
||||
bus.source.receive({
|
||||
'data': {
|
||||
'type': 'event',
|
||||
'value': {
|
||||
'viewRef': viewRef.serialize(),
|
||||
'elementIndex': elementIndex,
|
||||
'eventName': eventName,
|
||||
'locals': {'$event': {'target': {value: null}}}
|
||||
}
|
||||
}
|
||||
var eventDispatcher = new SpyEventDispatcher((elementIndex, eventName, locals) => {
|
||||
expect(elementIndex).toEqual(elementIndex);
|
||||
expect(eventName).toEqual(eventName);
|
||||
async.done();
|
||||
});
|
||||
|
||||
expect(eventDispatcher.wasDispatched).toBeTruthy();
|
||||
expect(eventDispatcher.elementIndex).toEqual(elementIndex);
|
||||
expect(eventDispatcher.eventName).toEqual(eventName);
|
||||
var viewRef = new WebWorkerRenderViewRef(0);
|
||||
serializer.allocateRenderViews(0); // serialize the ref so it's in the store
|
||||
viewRef =
|
||||
serializer.deserialize(serializer.serialize(viewRef, RenderViewRef), RenderViewRef);
|
||||
webWorkerEventDispatcher.registerEventDispatcher(viewRef, eventDispatcher);
|
||||
|
||||
ObservableWrapper.callNext(messageBuses.ui.to(EVENT_CHANNEL), {
|
||||
'viewRef': viewRef.serialize(),
|
||||
'elementIndex': elementIndex,
|
||||
'eventName': eventName,
|
||||
'locals': {'$event': {'target': {value: null}}}
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
class SpyEventDispatcher implements RenderEventDispatcher {
|
||||
wasDispatched: boolean = false;
|
||||
elementIndex: number;
|
||||
eventName: string;
|
||||
constructor(private _callback: Function) {}
|
||||
|
||||
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>) {
|
||||
this.wasDispatched = true;
|
||||
this.elementIndex = elementIndex;
|
||||
this.eventName = eventName;
|
||||
this._callback(elementIndex, eventName, locals);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
library angular2.test.web_workers.worker.mock_event_emitter;
|
||||
|
||||
import 'dart:core';
|
||||
import 'dart:async';
|
||||
import "package:angular2/src/facade/async.dart";
|
||||
|
||||
class MockEventEmitter extends EventEmitter {
|
||||
List<Function> _nextFns = new List();
|
||||
|
||||
@override
|
||||
StreamSubscription listen(void onData(dynamic line),
|
||||
{void onError(Error error), void onDone(), bool cancelOnError}) {
|
||||
_nextFns.add(onData);
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
void add(value) {
|
||||
_nextFns.forEach((fn) => fn(value));
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import {EventEmitter} from 'angular2/src/facade/async';
|
||||
import * as Rx from 'rx';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
export class MockEventEmitter extends EventEmitter {
|
||||
private _nextFns: List<Function> = [];
|
||||
|
||||
constructor() { super(); }
|
||||
|
||||
observer(generator: any): Rx.IDisposable {
|
||||
this._nextFns.push(generator.next);
|
||||
return null;
|
||||
}
|
||||
|
||||
next(value: any) {
|
||||
ListWrapper.forEach(this._nextFns, (fn) => { fn(value); });
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {DomTestbed, TestRootView, elRef} from '../../render/dom/dom_testbed';
|
||||
import {bind} from 'angular2/di';
|
||||
import {WebWorkerCompiler, WebWorkerRenderer} from "angular2/src/web-workers/worker/renderer";
|
||||
import {MessageBroker, UiArguments, FnArg} from "angular2/src/web-workers/worker/broker";
|
||||
import {MessageBrokerFactory, UiArguments, FnArg} from "angular2/src/web-workers/worker/broker";
|
||||
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||
import {isPresent, isBlank, BaseException, Type} from "angular2/src/facade/lang";
|
||||
import {MapWrapper, ListWrapper} from "angular2/src/facade/collection";
|
||||
@ -37,42 +37,47 @@ import {
|
||||
import {resolveInternalDomProtoView, DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {someComponent} from '../../render/dom/dom_renderer_integration_spec';
|
||||
import {WebWorkerMain} from 'angular2/src/web-workers/ui/impl';
|
||||
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||
import {MockMessageBus, MockMessageBusSink, MockMessageBusSource} from './worker_test_util';
|
||||
import {MessageBasedRenderCompiler} from 'angular2/src/web-workers/ui/render_compiler';
|
||||
import {MessageBasedRenderer} from 'angular2/src/web-workers/ui/renderer';
|
||||
import {
|
||||
createPairedMessageBuses
|
||||
} from './worker_test_util'
|
||||
|
||||
export function main() {
|
||||
function createBroker(workerSerializer: Serializer, uiSerializer: Serializer, tb: DomTestbed,
|
||||
uiRenderViewStore: RenderViewWithFragmentsStore,
|
||||
workerRenderViewStore: RenderViewWithFragmentsStore): MessageBroker {
|
||||
// set up the two message buses to pass messages to each other
|
||||
var uiMessageBus = new MockMessageBus(new MockMessageBusSink(), new MockMessageBusSource());
|
||||
var workerMessageBus = new MockMessageBus(new MockMessageBusSink(), new MockMessageBusSource());
|
||||
uiMessageBus.attachToBus(workerMessageBus);
|
||||
workerMessageBus.attachToBus(uiMessageBus);
|
||||
export function
|
||||
main() {
|
||||
function createBrokerFactory(workerSerializer: Serializer, uiSerializer: Serializer,
|
||||
tb: DomTestbed, uiRenderViewStore: RenderViewWithFragmentsStore,
|
||||
workerRenderViewStore: RenderViewWithFragmentsStore):
|
||||
MessageBrokerFactory {
|
||||
var messageBuses = createPairedMessageBuses();
|
||||
var uiMessageBus = messageBuses.ui;
|
||||
var workerMessageBus = messageBuses.worker;
|
||||
|
||||
// set up the worker side
|
||||
var broker = new MessageBroker(workerMessageBus, workerSerializer, null);
|
||||
var brokerFactory = new MessageBrokerFactory(workerMessageBus, workerSerializer);
|
||||
|
||||
// set up the ui side
|
||||
var webWorkerMain = new WebWorkerMain(tb.compiler, tb.renderer, uiRenderViewStore, uiSerializer,
|
||||
new AnchorBasedAppRootUrl(), null);
|
||||
webWorkerMain.attachToWebWorker(uiMessageBus);
|
||||
return broker;
|
||||
var renderCompiler = new MessageBasedRenderCompiler(uiMessageBus, uiSerializer, tb.compiler);
|
||||
var renderer =
|
||||
new MessageBasedRenderer(uiMessageBus, uiSerializer, uiRenderViewStore, tb.renderer);
|
||||
new WebWorkerMain(renderCompiler, renderer, null, null);
|
||||
|
||||
return brokerFactory;
|
||||
}
|
||||
|
||||
function createWorkerRenderer(workerSerializer: Serializer, uiSerializer: Serializer,
|
||||
tb: DomTestbed, uiRenderViewStore: RenderViewWithFragmentsStore,
|
||||
workerRenderViewStore: RenderViewWithFragmentsStore):
|
||||
WebWorkerRenderer {
|
||||
var broker =
|
||||
createBroker(workerSerializer, uiSerializer, tb, uiRenderViewStore, workerRenderViewStore);
|
||||
return new WebWorkerRenderer(broker, workerRenderViewStore);
|
||||
var brokerFactory = createBrokerFactory(workerSerializer, uiSerializer, tb, uiRenderViewStore,
|
||||
workerRenderViewStore);
|
||||
return new WebWorkerRenderer(brokerFactory, workerRenderViewStore, null);
|
||||
}
|
||||
|
||||
function createWorkerCompiler(workerSerializer: Serializer, uiSerializer: Serializer,
|
||||
tb: DomTestbed): WebWorkerCompiler {
|
||||
var broker = createBroker(workerSerializer, uiSerializer, tb, null, null);
|
||||
return new WebWorkerCompiler(broker);
|
||||
var brokerFactory = createBrokerFactory(workerSerializer, uiSerializer, tb, null, null);
|
||||
return new WebWorkerCompiler(brokerFactory);
|
||||
}
|
||||
|
||||
describe("Web Worker Compiler", function() {
|
@ -1,36 +1,60 @@
|
||||
import {StringMap, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
MessageBus,
|
||||
MessageBusSource,
|
||||
MessageBusInterface,
|
||||
MessageBusSink,
|
||||
SourceListener
|
||||
} from "angular2/src/web-workers/shared/message_bus";
|
||||
import {MapWrapper} from "angular2/src/facade/collection";
|
||||
MessageBusSource,
|
||||
MessageBus
|
||||
} from 'angular2/src/web-workers/shared/message_bus';
|
||||
import {MockEventEmitter} from './mock_event_emitter';
|
||||
|
||||
/**
|
||||
* Returns two MessageBus instances that are attached to each other.
|
||||
* Such that whatever goes into one's sink comes out the others source.
|
||||
*/
|
||||
export function createPairedMessageBuses(): PairedMessageBuses {
|
||||
var firstChannels: StringMap<string, MockEventEmitter> = {};
|
||||
var workerMessageBusSink = new MockMessageBusSink(firstChannels);
|
||||
var uiMessageBusSource = new MockMessageBusSource(firstChannels);
|
||||
|
||||
var secondChannels: StringMap<string, MockEventEmitter> = {};
|
||||
var uiMessageBusSink = new MockMessageBusSink(secondChannels);
|
||||
var workerMessageBusSource = new MockMessageBusSource(secondChannels);
|
||||
|
||||
return new PairedMessageBuses(new MockMessageBus(uiMessageBusSink, uiMessageBusSource),
|
||||
new MockMessageBus(workerMessageBusSink, workerMessageBusSource));
|
||||
}
|
||||
|
||||
export class PairedMessageBuses {
|
||||
constructor(public ui: MessageBusInterface, public worker: MessageBusInterface) {}
|
||||
}
|
||||
|
||||
export class MockMessageBusSource implements MessageBusSource {
|
||||
private _listenerStore: Map<int, SourceListener> = new Map<int, SourceListener>();
|
||||
private _numListeners: number = 0;
|
||||
constructor(private _channels: StringMap<string, MockEventEmitter>) {}
|
||||
|
||||
addListener(fn: SourceListener): int {
|
||||
this._listenerStore.set(++this._numListeners, fn);
|
||||
return this._numListeners;
|
||||
}
|
||||
|
||||
removeListener(index: int): void { MapWrapper.delete(this._listenerStore, index); }
|
||||
|
||||
receive(message: Object): void {
|
||||
MapWrapper.forEach(this._listenerStore, (fn: SourceListener, key: int) => { fn(message); });
|
||||
from(channel: string): MockEventEmitter {
|
||||
if (!StringMapWrapper.contains(this._channels, channel)) {
|
||||
this._channels[channel] = new MockEventEmitter();
|
||||
}
|
||||
return this._channels[channel];
|
||||
}
|
||||
}
|
||||
|
||||
export class MockMessageBusSink implements MessageBusSink {
|
||||
private _sendTo: MockMessageBusSource;
|
||||
constructor(private _channels: StringMap<string, MockEventEmitter>) {}
|
||||
|
||||
send(message: Object): void { this._sendTo.receive({'data': message}); }
|
||||
|
||||
attachToSource(source: MockMessageBusSource) { this._sendTo = source; }
|
||||
to(channel: string): MockEventEmitter {
|
||||
if (!StringMapWrapper.contains(this._channels, channel)) {
|
||||
this._channels[channel] = new MockEventEmitter();
|
||||
}
|
||||
return this._channels[channel];
|
||||
}
|
||||
}
|
||||
|
||||
export class MockMessageBus implements MessageBus {
|
||||
constructor(public sink: MockMessageBusSink, public source: MockMessageBusSource) {}
|
||||
attachToBus(bus: MockMessageBus) { this.sink.attachToSource(bus.source); }
|
||||
export class MockMessageBus extends MessageBus {
|
||||
constructor(public sink: MockMessageBusSink, public source: MockMessageBusSource) { super(); }
|
||||
|
||||
|
||||
to(channel: string): MockEventEmitter { return this.sink.to(channel); }
|
||||
|
||||
from(channel: string): MockEventEmitter { return this.source.from(channel); }
|
||||
}
|
||||
|
@ -11,7 +11,11 @@ import {
|
||||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {IMPLEMENTS, Type} from 'angular2/src/facade/lang';
|
||||
import {MessageBroker, UiArguments} from 'angular2/src/web-workers/worker/broker';
|
||||
import {
|
||||
MessageBroker,
|
||||
UiArguments,
|
||||
MessageBrokerFactory
|
||||
} from 'angular2/src/web-workers/worker/broker';
|
||||
import {WebWorkerXHRImpl} from "angular2/src/web-workers/worker/xhr_impl";
|
||||
import {PromiseWrapper} from "angular2/src/facade/async";
|
||||
|
||||
@ -25,14 +29,13 @@ export function main() {
|
||||
var messageBroker: any = new SpyMessageBroker();
|
||||
messageBroker.spy("runOnUiThread")
|
||||
.andCallFake((args: UiArguments, returnType: Type) => {
|
||||
expect(args.type).toEqual("xhr");
|
||||
expect(args.method).toEqual("get");
|
||||
expect(args.args.length).toEqual(1);
|
||||
expect(args.args[0].value).toEqual(URL);
|
||||
return PromiseWrapper.wrap(() => { return RESPONSE; });
|
||||
});
|
||||
|
||||
var xhrImpl = new WebWorkerXHRImpl(messageBroker);
|
||||
var xhrImpl = new WebWorkerXHRImpl(new MockMessageBrokerFactory(messageBroker));
|
||||
xhrImpl.get(URL).then((response) => {
|
||||
expect(response).toEqual(RESPONSE);
|
||||
async.done();
|
||||
@ -47,3 +50,8 @@ class SpyMessageBroker extends SpyObject {
|
||||
constructor() { super(MessageBroker); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m); }
|
||||
}
|
||||
|
||||
class MockMessageBrokerFactory extends MessageBrokerFactory {
|
||||
constructor(private _messageBroker: MessageBroker) { super(null, null); }
|
||||
createMessageBroker(channel: string) { return this._messageBroker; }
|
||||
}
|
||||
|
Reference in New Issue
Block a user