grafana/public/app/features/live/centrifuge/service.ts

244 lines
8.0 KiB
TypeScript
Raw Normal View History

import { Centrifuge, State } from 'centrifuge';
import { BehaviorSubject, Observable, share, startWith } from 'rxjs';
import {
DataQueryError,
DataQueryResponse,
LiveChannelAddress,
LiveChannelConnectionState,
LiveChannelId,
toLiveChannelId,
} from '@grafana/data';
import { FetchResponse } from '@grafana/runtime/src/services/backendSrv';
import {
GrafanaLiveSrv,
LiveDataStreamOptions,
LiveQueryDataOptions,
StreamingFrameAction,
StreamingFrameOptions,
} from '@grafana/runtime/src/services/live';
import { BackendDataSourceResponse } from '@grafana/runtime/src/utils/queryResponse';
import { StreamingResponseData } from '../data/utils';
import { LiveDataStream } from './LiveDataStream';
import { CentrifugeLiveChannel } from './channel';
Live: move centrifuge service to a web worker (#41090) * Fix: make webpack pickup workers written in TS * Add comlink to dependencies * Temporary fix: copy paste `toDataQueryError` from @grafana/runtime to avoid web dependencies * Implemented comlink-based centrifuge worker & worker proxy * Temporary fix: implement comlink transferHandlers for subscriptions and streamingdataframes * Move liveTimer filtering from CentrifugeService into GrafanaLiveService * Switch from CentrifugeService to CentrifugeServiceWorkerProxy in GrafanaLive * Naming fix * Refactor: move liveTimer-based data filtering from GrafanaLiveService to CentrifugeServiceWorker * observe dataStream on an async scheduler * Fix: - Unsubscribe is now propagated from the main thread to the worker, - improve worker&workerProxy types * Fix: Prettify types * Fix: Add error & complete observers * Docs: Add comment explaining the `subscriberTransferHandler` * Fix: Replace `StreamingDataFrameHandler` with explicitly converting StreamingDataFrame to a DataFrameDTO * Refactor: move liveTimer filtering to service.ts to make it easy to implement a `live-service-web-worker` feature flag * Feat: add `live-service-web-worker` feature flag * Fix: extract toDataQueryError.ts to a separate file within `@grafana-runtime` to avoid having a dependency from webworker to the whole package (@grafana-runtime/index.ts) * Update public/app/features/dashboard/dashgrid/liveTimer.ts Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * Fix: fixed default import class in worker file * Fix: cast worker as Endpoint * Migrate from worker-loader to webpack native worker support v1 - broken prod build * Fix: Use custom path in HtmlWebpackPlugin * Fix: Loading workers from CDNs * Fix: Avoid issues with jest ESM support by mocking `createWorker` files * Fix: move the custom mockWorker rendering layout to `test/mocks` Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
2021-11-09 11:05:01 -06:00
export type CentrifugeSrvDeps = {
grafanaAuthToken: string | null;
appUrl: string;
orgId: number;
orgRole: string;
liveEnabled: boolean;
Live: move centrifuge service to a web worker (#41090) * Fix: make webpack pickup workers written in TS * Add comlink to dependencies * Temporary fix: copy paste `toDataQueryError` from @grafana/runtime to avoid web dependencies * Implemented comlink-based centrifuge worker & worker proxy * Temporary fix: implement comlink transferHandlers for subscriptions and streamingdataframes * Move liveTimer filtering from CentrifugeService into GrafanaLiveService * Switch from CentrifugeService to CentrifugeServiceWorkerProxy in GrafanaLive * Naming fix * Refactor: move liveTimer-based data filtering from GrafanaLiveService to CentrifugeServiceWorker * observe dataStream on an async scheduler * Fix: - Unsubscribe is now propagated from the main thread to the worker, - improve worker&workerProxy types * Fix: Prettify types * Fix: Add error & complete observers * Docs: Add comment explaining the `subscriberTransferHandler` * Fix: Replace `StreamingDataFrameHandler` with explicitly converting StreamingDataFrame to a DataFrameDTO * Refactor: move liveTimer filtering to service.ts to make it easy to implement a `live-service-web-worker` feature flag * Feat: add `live-service-web-worker` feature flag * Fix: extract toDataQueryError.ts to a separate file within `@grafana-runtime` to avoid having a dependency from webworker to the whole package (@grafana-runtime/index.ts) * Update public/app/features/dashboard/dashgrid/liveTimer.ts Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * Fix: fixed default import class in worker file * Fix: cast worker as Endpoint * Migrate from worker-loader to webpack native worker support v1 - broken prod build * Fix: Use custom path in HtmlWebpackPlugin * Fix: Loading workers from CDNs * Fix: Avoid issues with jest ESM support by mocking `createWorker` files * Fix: move the custom mockWorker rendering layout to `test/mocks` Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
2021-11-09 11:05:01 -06:00
dataStreamSubscriberReadiness: Observable<boolean>;
};
export type StreamingDataQueryResponse = Omit<DataQueryResponse, 'data'> & { data: [StreamingResponseData] };
Live: move centrifuge service to a web worker (#41090) * Fix: make webpack pickup workers written in TS * Add comlink to dependencies * Temporary fix: copy paste `toDataQueryError` from @grafana/runtime to avoid web dependencies * Implemented comlink-based centrifuge worker & worker proxy * Temporary fix: implement comlink transferHandlers for subscriptions and streamingdataframes * Move liveTimer filtering from CentrifugeService into GrafanaLiveService * Switch from CentrifugeService to CentrifugeServiceWorkerProxy in GrafanaLive * Naming fix * Refactor: move liveTimer-based data filtering from GrafanaLiveService to CentrifugeServiceWorker * observe dataStream on an async scheduler * Fix: - Unsubscribe is now propagated from the main thread to the worker, - improve worker&workerProxy types * Fix: Prettify types * Fix: Add error & complete observers * Docs: Add comment explaining the `subscriberTransferHandler` * Fix: Replace `StreamingDataFrameHandler` with explicitly converting StreamingDataFrame to a DataFrameDTO * Refactor: move liveTimer filtering to service.ts to make it easy to implement a `live-service-web-worker` feature flag * Feat: add `live-service-web-worker` feature flag * Fix: extract toDataQueryError.ts to a separate file within `@grafana-runtime` to avoid having a dependency from webworker to the whole package (@grafana-runtime/index.ts) * Update public/app/features/dashboard/dashgrid/liveTimer.ts Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * Fix: fixed default import class in worker file * Fix: cast worker as Endpoint * Migrate from worker-loader to webpack native worker support v1 - broken prod build * Fix: Use custom path in HtmlWebpackPlugin * Fix: Loading workers from CDNs * Fix: Avoid issues with jest ESM support by mocking `createWorker` files * Fix: move the custom mockWorker rendering layout to `test/mocks` Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
2021-11-09 11:05:01 -06:00
export type CentrifugeSrv = Omit<GrafanaLiveSrv, 'publish' | 'getDataStream' | 'getQueryData'> & {
getDataStream: (options: LiveDataStreamOptions) => Observable<StreamingDataQueryResponse>;
getQueryData: (
options: LiveQueryDataOptions
) => Promise<
| { data: BackendDataSourceResponse | undefined }
| FetchResponse<BackendDataSourceResponse | undefined>
| DataQueryError
>;
};
Live: move centrifuge service to a web worker (#41090) * Fix: make webpack pickup workers written in TS * Add comlink to dependencies * Temporary fix: copy paste `toDataQueryError` from @grafana/runtime to avoid web dependencies * Implemented comlink-based centrifuge worker & worker proxy * Temporary fix: implement comlink transferHandlers for subscriptions and streamingdataframes * Move liveTimer filtering from CentrifugeService into GrafanaLiveService * Switch from CentrifugeService to CentrifugeServiceWorkerProxy in GrafanaLive * Naming fix * Refactor: move liveTimer-based data filtering from GrafanaLiveService to CentrifugeServiceWorker * observe dataStream on an async scheduler * Fix: - Unsubscribe is now propagated from the main thread to the worker, - improve worker&workerProxy types * Fix: Prettify types * Fix: Add error & complete observers * Docs: Add comment explaining the `subscriberTransferHandler` * Fix: Replace `StreamingDataFrameHandler` with explicitly converting StreamingDataFrame to a DataFrameDTO * Refactor: move liveTimer filtering to service.ts to make it easy to implement a `live-service-web-worker` feature flag * Feat: add `live-service-web-worker` feature flag * Fix: extract toDataQueryError.ts to a separate file within `@grafana-runtime` to avoid having a dependency from webworker to the whole package (@grafana-runtime/index.ts) * Update public/app/features/dashboard/dashgrid/liveTimer.ts Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * Fix: fixed default import class in worker file * Fix: cast worker as Endpoint * Migrate from worker-loader to webpack native worker support v1 - broken prod build * Fix: Use custom path in HtmlWebpackPlugin * Fix: Loading workers from CDNs * Fix: Avoid issues with jest ESM support by mocking `createWorker` files * Fix: move the custom mockWorker rendering layout to `test/mocks` Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
2021-11-09 11:05:01 -06:00
export type DataStreamSubscriptionKey = string;
Live: move centrifuge service to a web worker (#41090) * Fix: make webpack pickup workers written in TS * Add comlink to dependencies * Temporary fix: copy paste `toDataQueryError` from @grafana/runtime to avoid web dependencies * Implemented comlink-based centrifuge worker & worker proxy * Temporary fix: implement comlink transferHandlers for subscriptions and streamingdataframes * Move liveTimer filtering from CentrifugeService into GrafanaLiveService * Switch from CentrifugeService to CentrifugeServiceWorkerProxy in GrafanaLive * Naming fix * Refactor: move liveTimer-based data filtering from GrafanaLiveService to CentrifugeServiceWorker * observe dataStream on an async scheduler * Fix: - Unsubscribe is now propagated from the main thread to the worker, - improve worker&workerProxy types * Fix: Prettify types * Fix: Add error & complete observers * Docs: Add comment explaining the `subscriberTransferHandler` * Fix: Replace `StreamingDataFrameHandler` with explicitly converting StreamingDataFrame to a DataFrameDTO * Refactor: move liveTimer filtering to service.ts to make it easy to implement a `live-service-web-worker` feature flag * Feat: add `live-service-web-worker` feature flag * Fix: extract toDataQueryError.ts to a separate file within `@grafana-runtime` to avoid having a dependency from webworker to the whole package (@grafana-runtime/index.ts) * Update public/app/features/dashboard/dashgrid/liveTimer.ts Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * Fix: fixed default import class in worker file * Fix: cast worker as Endpoint * Migrate from worker-loader to webpack native worker support v1 - broken prod build * Fix: Use custom path in HtmlWebpackPlugin * Fix: Loading workers from CDNs * Fix: Avoid issues with jest ESM support by mocking `createWorker` files * Fix: move the custom mockWorker rendering layout to `test/mocks` Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
2021-11-09 11:05:01 -06:00
const defaultStreamingFrameOptions: Readonly<StreamingFrameOptions> = {
maxLength: 100,
maxDelta: Infinity,
action: StreamingFrameAction.Append,
};
const dataStreamShutdownDelayInMs = 5000;
Live: move centrifuge service to a web worker (#41090) * Fix: make webpack pickup workers written in TS * Add comlink to dependencies * Temporary fix: copy paste `toDataQueryError` from @grafana/runtime to avoid web dependencies * Implemented comlink-based centrifuge worker & worker proxy * Temporary fix: implement comlink transferHandlers for subscriptions and streamingdataframes * Move liveTimer filtering from CentrifugeService into GrafanaLiveService * Switch from CentrifugeService to CentrifugeServiceWorkerProxy in GrafanaLive * Naming fix * Refactor: move liveTimer-based data filtering from GrafanaLiveService to CentrifugeServiceWorker * observe dataStream on an async scheduler * Fix: - Unsubscribe is now propagated from the main thread to the worker, - improve worker&workerProxy types * Fix: Prettify types * Fix: Add error & complete observers * Docs: Add comment explaining the `subscriberTransferHandler` * Fix: Replace `StreamingDataFrameHandler` with explicitly converting StreamingDataFrame to a DataFrameDTO * Refactor: move liveTimer filtering to service.ts to make it easy to implement a `live-service-web-worker` feature flag * Feat: add `live-service-web-worker` feature flag * Fix: extract toDataQueryError.ts to a separate file within `@grafana-runtime` to avoid having a dependency from webworker to the whole package (@grafana-runtime/index.ts) * Update public/app/features/dashboard/dashgrid/liveTimer.ts Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * Fix: fixed default import class in worker file * Fix: cast worker as Endpoint * Migrate from worker-loader to webpack native worker support v1 - broken prod build * Fix: Use custom path in HtmlWebpackPlugin * Fix: Loading workers from CDNs * Fix: Avoid issues with jest ESM support by mocking `createWorker` files * Fix: move the custom mockWorker rendering layout to `test/mocks` Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
2021-11-09 11:05:01 -06:00
export class CentrifugeService implements CentrifugeSrv {
readonly open = new Map<string, CentrifugeLiveChannel>();
private readonly liveDataStreamByChannelId: Record<LiveChannelId, LiveDataStream> = {};
readonly centrifuge: Centrifuge;
readonly connectionState: BehaviorSubject<boolean>;
readonly connectionBlocker: Promise<void>;
private readonly dataStreamSubscriberReadiness: Observable<boolean>;
constructor(private deps: CentrifugeSrvDeps) {
this.dataStreamSubscriberReadiness = deps.dataStreamSubscriberReadiness.pipe(share(), startWith(true));
let liveUrl = `${deps.appUrl.replace(/^http/, 'ws')}/api/live/ws`;
const token = deps.grafanaAuthToken;
if (token !== null && token !== '') {
liveUrl += '?auth_token=' + token;
}
this.centrifuge = new Centrifuge(liveUrl, {
timeout: 30000,
});
// orgRole is set when logged in *or* anonymous users can use grafana
if (deps.liveEnabled && deps.orgRole !== '') {
this.centrifuge.connect(); // do connection
}
this.connectionState = new BehaviorSubject<boolean>(this.centrifuge.state === State.Connected);
this.connectionBlocker = new Promise<void>((resolve) => {
if (this.centrifuge.state === State.Connected) {
return resolve();
}
const connectListener = () => {
resolve();
this.centrifuge.removeListener('connected', connectListener);
};
this.centrifuge.addListener('connected', connectListener);
});
// Register global listeners
this.centrifuge.on('connected', this.onConnect);
this.centrifuge.on('connecting', this.onDisconnect);
this.centrifuge.on('disconnected', this.onDisconnect);
this.centrifuge.on('publication', this.onServerSideMessage);
}
//----------------------------------------------------------
// Internal functions
//----------------------------------------------------------
Live: move centrifuge service to a web worker (#41090) * Fix: make webpack pickup workers written in TS * Add comlink to dependencies * Temporary fix: copy paste `toDataQueryError` from @grafana/runtime to avoid web dependencies * Implemented comlink-based centrifuge worker & worker proxy * Temporary fix: implement comlink transferHandlers for subscriptions and streamingdataframes * Move liveTimer filtering from CentrifugeService into GrafanaLiveService * Switch from CentrifugeService to CentrifugeServiceWorkerProxy in GrafanaLive * Naming fix * Refactor: move liveTimer-based data filtering from GrafanaLiveService to CentrifugeServiceWorker * observe dataStream on an async scheduler * Fix: - Unsubscribe is now propagated from the main thread to the worker, - improve worker&workerProxy types * Fix: Prettify types * Fix: Add error & complete observers * Docs: Add comment explaining the `subscriberTransferHandler` * Fix: Replace `StreamingDataFrameHandler` with explicitly converting StreamingDataFrame to a DataFrameDTO * Refactor: move liveTimer filtering to service.ts to make it easy to implement a `live-service-web-worker` feature flag * Feat: add `live-service-web-worker` feature flag * Fix: extract toDataQueryError.ts to a separate file within `@grafana-runtime` to avoid having a dependency from webworker to the whole package (@grafana-runtime/index.ts) * Update public/app/features/dashboard/dashgrid/liveTimer.ts Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * Fix: fixed default import class in worker file * Fix: cast worker as Endpoint * Migrate from worker-loader to webpack native worker support v1 - broken prod build * Fix: Use custom path in HtmlWebpackPlugin * Fix: Loading workers from CDNs * Fix: Avoid issues with jest ESM support by mocking `createWorker` files * Fix: move the custom mockWorker rendering layout to `test/mocks` Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
2021-11-09 11:05:01 -06:00
private onConnect = (context: any) => {
this.connectionState.next(true);
};
Live: move centrifuge service to a web worker (#41090) * Fix: make webpack pickup workers written in TS * Add comlink to dependencies * Temporary fix: copy paste `toDataQueryError` from @grafana/runtime to avoid web dependencies * Implemented comlink-based centrifuge worker & worker proxy * Temporary fix: implement comlink transferHandlers for subscriptions and streamingdataframes * Move liveTimer filtering from CentrifugeService into GrafanaLiveService * Switch from CentrifugeService to CentrifugeServiceWorkerProxy in GrafanaLive * Naming fix * Refactor: move liveTimer-based data filtering from GrafanaLiveService to CentrifugeServiceWorker * observe dataStream on an async scheduler * Fix: - Unsubscribe is now propagated from the main thread to the worker, - improve worker&workerProxy types * Fix: Prettify types * Fix: Add error & complete observers * Docs: Add comment explaining the `subscriberTransferHandler` * Fix: Replace `StreamingDataFrameHandler` with explicitly converting StreamingDataFrame to a DataFrameDTO * Refactor: move liveTimer filtering to service.ts to make it easy to implement a `live-service-web-worker` feature flag * Feat: add `live-service-web-worker` feature flag * Fix: extract toDataQueryError.ts to a separate file within `@grafana-runtime` to avoid having a dependency from webworker to the whole package (@grafana-runtime/index.ts) * Update public/app/features/dashboard/dashgrid/liveTimer.ts Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * Fix: fixed default import class in worker file * Fix: cast worker as Endpoint * Migrate from worker-loader to webpack native worker support v1 - broken prod build * Fix: Use custom path in HtmlWebpackPlugin * Fix: Loading workers from CDNs * Fix: Avoid issues with jest ESM support by mocking `createWorker` files * Fix: move the custom mockWorker rendering layout to `test/mocks` Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
2021-11-09 11:05:01 -06:00
private onDisconnect = (context: any) => {
this.connectionState.next(false);
};
Live: move centrifuge service to a web worker (#41090) * Fix: make webpack pickup workers written in TS * Add comlink to dependencies * Temporary fix: copy paste `toDataQueryError` from @grafana/runtime to avoid web dependencies * Implemented comlink-based centrifuge worker & worker proxy * Temporary fix: implement comlink transferHandlers for subscriptions and streamingdataframes * Move liveTimer filtering from CentrifugeService into GrafanaLiveService * Switch from CentrifugeService to CentrifugeServiceWorkerProxy in GrafanaLive * Naming fix * Refactor: move liveTimer-based data filtering from GrafanaLiveService to CentrifugeServiceWorker * observe dataStream on an async scheduler * Fix: - Unsubscribe is now propagated from the main thread to the worker, - improve worker&workerProxy types * Fix: Prettify types * Fix: Add error & complete observers * Docs: Add comment explaining the `subscriberTransferHandler` * Fix: Replace `StreamingDataFrameHandler` with explicitly converting StreamingDataFrame to a DataFrameDTO * Refactor: move liveTimer filtering to service.ts to make it easy to implement a `live-service-web-worker` feature flag * Feat: add `live-service-web-worker` feature flag * Fix: extract toDataQueryError.ts to a separate file within `@grafana-runtime` to avoid having a dependency from webworker to the whole package (@grafana-runtime/index.ts) * Update public/app/features/dashboard/dashgrid/liveTimer.ts Co-authored-by: Leon Sorokin <leeoniya@gmail.com> * Fix: fixed default import class in worker file * Fix: cast worker as Endpoint * Migrate from worker-loader to webpack native worker support v1 - broken prod build * Fix: Use custom path in HtmlWebpackPlugin * Fix: Loading workers from CDNs * Fix: Avoid issues with jest ESM support by mocking `createWorker` files * Fix: move the custom mockWorker rendering layout to `test/mocks` Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
2021-11-09 11:05:01 -06:00
private onServerSideMessage = (context: any) => {
console.log('Publication from server-side channel', context);
};
/**
* Get a channel. If the scope, namespace, or path is invalid, a shutdown
* channel will be returned with an error state indicated in its status
*/
private getChannel<TMessage>(addr: LiveChannelAddress): CentrifugeLiveChannel<TMessage> {
const id = `${this.deps.orgId}/${addr.scope}/${addr.namespace}/${addr.path}`;
let channel = this.open.get(id);
if (channel != null) {
return channel;
}
channel = new CentrifugeLiveChannel(id, addr);
if (channel.currentStatus.state === LiveChannelConnectionState.Invalid) {
return channel;
}
channel.shutdownCallback = () => {
this.open.delete(id);
// without a call to `removeSubscription`, the subscription will remain in centrifuge's internal registry
this.centrifuge.removeSubscription(this.centrifuge.getSubscription(id));
};
this.open.set(id, channel);
// Initialize the channel in the background
this.initChannel(channel).catch((err) => {
if (channel) {
channel.currentStatus.state = LiveChannelConnectionState.Invalid;
channel.shutdownWithError(err);
}
this.open.delete(id);
});
// return the not-yet initialized channel
return channel;
}
private async initChannel(channel: CentrifugeLiveChannel): Promise<void> {
if (this.centrifuge.state !== State.Connected) {
await this.connectionBlocker;
}
const subscription = this.centrifuge.newSubscription(channel.id, {
data: channel.addr.data,
});
channel.subscription = subscription;
channel.initalize();
subscription.subscribe();
return;
}
//----------------------------------------------------------
// Exported functions
//----------------------------------------------------------
/**
* Listen for changes to the connection state
*/
getConnectionState = () => {
return this.connectionState.asObservable();
};
/**
* Watch for messages in a channel
*/
getStream: CentrifugeSrv['getStream'] = <T>(address: LiveChannelAddress) => {
return this.getChannel<T>(address).getStream();
};
private createSubscriptionKey = (options: LiveDataStreamOptions): DataStreamSubscriptionKey =>
options.key ?? `xstr/${streamCounter++}`;
private getLiveDataStream = (options: LiveDataStreamOptions): LiveDataStream => {
const channelId = toLiveChannelId(options.addr);
const existingStream = this.liveDataStreamByChannelId[channelId];
if (existingStream) {
return existingStream;
}
const channel = this.getChannel(options.addr);
this.liveDataStreamByChannelId[channelId] = new LiveDataStream({
channelId,
onShutdown: () => {
delete this.liveDataStreamByChannelId[channelId];
},
liveEventsObservable: channel.getStream(),
subscriberReadiness: this.dataStreamSubscriberReadiness,
defaultStreamingFrameOptions,
shutdownDelayInMs: dataStreamShutdownDelayInMs,
});
return this.liveDataStreamByChannelId[channelId];
};
/**
* Connect to a channel and return results as DataFrames
*/
getDataStream: CentrifugeSrv['getDataStream'] = (options) => {
const subscriptionKey = this.createSubscriptionKey(options);
const stream = this.getLiveDataStream(options);
return stream.get(options, subscriptionKey);
};
/**
* Executes a query over the live websocket. Query response can contain live channels we can subscribe to for further updates
*
* Since the initial request and subscription are on the same socket, this will support HA setups
*/
getQueryData: CentrifugeSrv['getQueryData'] = async (options) => {
if (this.centrifuge.state !== State.Connected) {
await this.connectionBlocker;
}
return this.centrifuge.rpc('grafana.query', options.body);
};
/**
* For channels that support presence, this will request the current state from the server.
*
* Join and leave messages will be sent to the open stream
*/
getPresence: CentrifugeSrv['getPresence'] = (address) => {
return this.getChannel(address).getPresence();
};
}
// This is used to give a unique key for each stream. The actual value does not matter
let streamCounter = 0;