Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclude PresenceMessage from BaseRealtime bundle #1505

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/common/lib/client/baserealtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@ import ProtocolMessage from '../types/protocolmessage';
import { ChannelOptions } from '../../types/channel';
import ClientOptions from '../../types/ClientOptions';
import * as API from '../../../../ably';
import { ModulesMap } from './modulesmap';
import RealtimePresence from './realtimepresence';
import { ModulesMap, RealtimePresenceModule } from './modulesmap';
import { TransportNames } from 'common/constants/TransportName';
import { TransportImplementations } from 'common/platform';

/**
`BaseRealtime` is an export of the tree-shakable version of the SDK, and acts as the base class for the `DefaultRealtime` class exported by the non tree-shakable version.
*/
class BaseRealtime extends BaseClient {
readonly _RealtimePresence: typeof RealtimePresence | null;
readonly _RealtimePresence: RealtimePresenceModule | null;
// Extra transport implementations available to this client, in addition to those in Platform.Transports.bundledImplementations
readonly _additionalTransportImplementations: TransportImplementations;
_channels: any;
Expand Down
10 changes: 9 additions & 1 deletion src/common/lib/client/defaultrealtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import RealtimePresence from './realtimepresence';
import { DefaultPresenceMessage } from '../types/defaultpresencemessage';
import initialiseWebSocketTransport from '../transport/websockettransport';
import { FilteredSubscriptions } from './filteredsubscriptions';
import {
fromValues as presenceMessageFromValues,
fromValuesArray as presenceMessagesFromValuesArray,
} from '../types/presencemessage';

/**
`DefaultRealtime` is the class that the non tree-shakable version of the SDK exports as `Realtime`. It ensures that this version of the SDK includes all of the functionality which is optionally available in the tree-shakable version.
Expand All @@ -26,7 +30,11 @@ export class DefaultRealtime extends BaseRealtime {
...allCommonModules,
Crypto: DefaultRealtime.Crypto ?? undefined,
MsgPack,
RealtimePresence,
RealtimePresence: {
RealtimePresence,
presenceMessageFromValues,
presenceMessagesFromValuesArray,
},
WebSocketTransport: initialiseWebSocketTransport,
MessageInteractions: FilteredSubscriptions,
});
Expand Down
15 changes: 14 additions & 1 deletion src/common/lib/client/modulesmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,25 @@ import { TransportInitialiser } from '../transport/connectionmanager';
import XHRRequest from 'platform/web/lib/http/request/xhrrequest';
import fetchRequest from 'platform/web/lib/http/request/fetchrequest';
import { FilteredSubscriptions } from './filteredsubscriptions';
import {
fromValues as presenceMessageFromValues,
fromValuesArray as presenceMessagesFromValuesArray,
} from '../types/presencemessage';

export interface PresenceMessageModule {
presenceMessageFromValues: typeof presenceMessageFromValues;
presenceMessagesFromValuesArray: typeof presenceMessagesFromValuesArray;
}

export type RealtimePresenceModule = PresenceMessageModule & {
RealtimePresence: typeof RealtimePresence;
};

export interface ModulesMap {
Rest?: typeof Rest;
Crypto?: IUntypedCryptoStatic;
MsgPack?: MsgPack;
RealtimePresence?: typeof RealtimePresence;
RealtimePresence?: RealtimePresenceModule;
WebSocketTransport?: TransportInitialiser;
XHRPolling?: TransportInitialiser;
XHRStreaming?: TransportInitialiser;
Expand Down
19 changes: 10 additions & 9 deletions src/common/lib/client/realtimechannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ import Message, {
} from '../types/message';
import ChannelStateChange from './channelstatechange';
import ErrorInfo, { IPartialErrorInfo, PartialErrorInfo } from '../types/errorinfo';
import PresenceMessage, {
fromValues as presenceMessageFromValues,
fromValuesArray as presenceMessagesFromValuesArray,
decode as decodePresenceMessage,
} from '../types/presencemessage';
import PresenceMessage, { decode as decodePresenceMessage } from '../types/presencemessage';
import ConnectionErrors from '../transport/connectionerrors';
import * as API from '../../../../ably';
import ConnectionManager from '../transport/connectionmanager';
Expand Down Expand Up @@ -109,7 +105,7 @@ class RealtimeChannel extends EventEmitter {
this.name = name;
this.channelOptions = normaliseChannelOptions(client._Crypto ?? null, options);
this.client = client;
this._presence = client._RealtimePresence ? new client._RealtimePresence(this) : null;
this._presence = client._RealtimePresence ? new client._RealtimePresence.RealtimePresence(this) : null;
this.connectionManager = client.connection.connectionManager;
this.state = 'initialized';
this.subscriptions = new EventEmitter();
Expand Down Expand Up @@ -509,8 +505,8 @@ class RealtimeChannel extends EventEmitter {
action: actions.PRESENCE,
channel: this.name,
presence: Utils.isArray(presence)
? presenceMessagesFromValuesArray(presence)
: [presenceMessageFromValues(presence)],
? this.client._RealtimePresence!.presenceMessagesFromValuesArray(presence)
: [this.client._RealtimePresence!.presenceMessageFromValues(presence)],
});
this.sendMessage(msg, callback);
}
Expand Down Expand Up @@ -585,7 +581,12 @@ class RealtimeChannel extends EventEmitter {
if (!message.presence) break;
// eslint-disable-next-line no-fallthrough
case actions.PRESENCE: {
const presence = message.presence as Array<PresenceMessage>;
const presence = message.presence;
owenpearson marked this conversation as resolved.
Show resolved Hide resolved

if (!presence) {
break;
}

const { id, connectionId, timestamp } = message;

const options = this.channelOptions;
Expand Down
5 changes: 4 additions & 1 deletion src/common/lib/transport/comettransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,10 @@ abstract class CometTransport extends Transport {
try {
const items = this.decodeResponse(responseData);
if (items && items.length)
for (let i = 0; i < items.length; i++) this.onProtocolMessage(protocolMessageFromDeserialized(items[i]));
for (let i = 0; i < items.length; i++)
this.onProtocolMessage(
protocolMessageFromDeserialized(items[i], this.connectionManager.realtime._RealtimePresence)
);
} catch (e) {
Logger.logAction(
Logger.LOG_ERROR,
Expand Down
6 changes: 5 additions & 1 deletion src/common/lib/transport/connectionmanager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1914,7 +1914,11 @@ class ConnectionManager extends EventEmitter {
return;
}
if (Logger.shouldLog(Logger.LOG_MICRO)) {
Logger.logAction(Logger.LOG_MICRO, 'ConnectionManager.send()', 'queueing msg; ' + stringifyProtocolMessage(msg));
Logger.logAction(
Logger.LOG_MICRO,
'ConnectionManager.send()',
'queueing msg; ' + stringifyProtocolMessage(msg, this.realtime._RealtimePresence)
);
}
this.queue(msg, callback);
}
Expand Down
3 changes: 2 additions & 1 deletion src/common/lib/transport/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ class Protocol extends EventEmitter {
Logger.logAction(
Logger.LOG_MICRO,
'Protocol.send()',
'sending msg; ' + stringifyProtocolMessage(pendingMessage.message)
'sending msg; ' +
stringifyProtocolMessage(pendingMessage.message, this.transport.connectionManager.realtime._RealtimePresence)
);
}
pendingMessage.sendAttempted = true;
Expand Down
2 changes: 1 addition & 1 deletion src/common/lib/transport/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ abstract class Transport extends EventEmitter {
'received on ' +
this.shortName +
': ' +
stringifyProtocolMessage(message) +
stringifyProtocolMessage(message, this.connectionManager.realtime._RealtimePresence) +
'; connectionId = ' +
this.connectionManager.connectionId
);
Expand Down
9 changes: 8 additions & 1 deletion src/common/lib/transport/websockettransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,14 @@ class WebSocketTransport extends Transport {
'data received; length = ' + data.length + '; type = ' + typeof data
);
try {
this.onProtocolMessage(deserializeProtocolMessage(data, this.connectionManager.realtime._MsgPack, this.format));
this.onProtocolMessage(
deserializeProtocolMessage(
data,
this.connectionManager.realtime._MsgPack,
this.connectionManager.realtime._RealtimePresence,
this.format
)
);
} catch (e) {
Logger.logAction(
Logger.LOG_ERROR,
Expand Down
39 changes: 31 additions & 8 deletions src/common/lib/types/protocolmessage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { MsgPack } from 'common/types/msgpack';
import { Types } from '../../../../ably';
import { PresenceMessageModule } from '../client/modulesmap';
import * as Utils from '../util/utils';
import ErrorInfo from './errorinfo';
import Message, { fromValues as messageFromValues, fromValuesArray as messagesFromValuesArray } from './message';
Expand Down Expand Up @@ -65,26 +66,46 @@ export const channelModes = ['PRESENCE', 'PUBLISH', 'SUBSCRIBE', 'PRESENCE_SUBSC

export const serialize = Utils.encodeBody;

export function deserialize(serialized: unknown, MsgPack: MsgPack | null, format?: Utils.Format): ProtocolMessage {
export function deserialize(
serialized: unknown,
MsgPack: MsgPack | null,
presenceMessageModule: PresenceMessageModule | null,
format?: Utils.Format
): ProtocolMessage {
const deserialized = Utils.decodeBody<Record<string, unknown>>(serialized, MsgPack, format);
return fromDeserialized(deserialized);
return fromDeserialized(deserialized, presenceMessageModule);
}

export function fromDeserialized(deserialized: Record<string, unknown>): ProtocolMessage {
export function fromDeserialized(
deserialized: Record<string, unknown>,
presenceMessageModule: PresenceMessageModule | null
): ProtocolMessage {
const error = deserialized.error;
if (error) deserialized.error = ErrorInfo.fromValues(error as ErrorInfo);
const messages = deserialized.messages as Message[];
if (messages) for (let i = 0; i < messages.length; i++) messages[i] = messageFromValues(messages[i]);
const presence = deserialized.presence as PresenceMessage[];
if (presence) for (let i = 0; i < presence.length; i++) presence[i] = presenceMessageFromValues(presence[i], true);
return Object.assign(new ProtocolMessage(), deserialized);

const presence = presenceMessageModule ? (deserialized.presence as PresenceMessage[]) : undefined;
if (presenceMessageModule) {
if (presence && presenceMessageModule)
for (let i = 0; i < presence.length; i++)
presence[i] = presenceMessageModule.presenceMessageFromValues(presence[i], true);
}
return Object.assign(new ProtocolMessage(), { ...deserialized, presence });
}

/**
* Used by the tests.
*/
export function fromDeserializedIncludingDependencies(deserialized: Record<string, unknown>): ProtocolMessage {
return fromDeserialized(deserialized, { presenceMessageFromValues, presenceMessagesFromValuesArray });
}

export function fromValues(values: unknown): ProtocolMessage {
return Object.assign(new ProtocolMessage(), values);
}

export function stringify(msg: any): string {
export function stringify(msg: any, presenceMessageModule: PresenceMessageModule | null): string {
let result = '[ProtocolMessage';
if (msg.action !== undefined) result += '; action=' + ActionName[msg.action] || msg.action;

Expand All @@ -96,7 +117,8 @@ export function stringify(msg: any): string {
}

if (msg.messages) result += '; messages=' + toStringArray(messagesFromValuesArray(msg.messages));
if (msg.presence) result += '; presence=' + toStringArray(presenceMessagesFromValuesArray(msg.presence));
if (msg.presence && presenceMessageModule)
result += '; presence=' + toStringArray(presenceMessageModule.presenceMessagesFromValuesArray(msg.presence));
if (msg.error) result += '; error=' + ErrorInfo.fromValues(msg.error).toString();
if (msg.auth && msg.auth.accessToken) result += '; token=' + msg.auth.accessToken;
if (msg.flags) result += '; flags=' + flagNames.filter(msg.hasFlag).join(',');
Expand Down Expand Up @@ -128,6 +150,7 @@ class ProtocolMessage {
channelSerial?: string | null;
msgSerial?: number;
messages?: Message[];
// This will be undefined if we skipped decoding this property due to user not requesting presence functionality — see `fromDeserialized`
presence?: PresenceMessage[];
auth?: unknown;
connectionDetails?: Record<string, unknown>;
Expand Down
2 changes: 1 addition & 1 deletion src/platform/nativescript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DefaultRest } from '../../common/lib/client/defaultrest';
import { DefaultRealtime } from '../../common/lib/client/defaultrealtime';
import Platform from '../../common/platform';
import ErrorInfo from '../../common/lib/types/errorinfo';
import { fromDeserialized as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';

// Platform Specific
import BufferUtils from '../web/lib/util/bufferutils';
Expand Down
2 changes: 1 addition & 1 deletion src/platform/nodejs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DefaultRest } from '../../common/lib/client/defaultrest';
import { DefaultRealtime } from '../../common/lib/client/defaultrealtime';
import Platform from '../../common/platform';
import ErrorInfo from '../../common/lib/types/errorinfo';
import { fromDeserialized as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';

// Platform Specific
import BufferUtils from './lib/util/bufferutils';
Expand Down
2 changes: 1 addition & 1 deletion src/platform/react-native/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DefaultRest } from '../../common/lib/client/defaultrest';
import { DefaultRealtime } from '../../common/lib/client/defaultrealtime';
import Platform from '../../common/platform';
import ErrorInfo from '../../common/lib/types/errorinfo';
import { fromDeserialized as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';

// Platform Specific
import BufferUtils from '../web/lib/util/bufferutils';
Expand Down
2 changes: 1 addition & 1 deletion src/platform/web/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DefaultRest } from '../../common/lib/client/defaultrest';
import { DefaultRealtime } from '../../common/lib/client/defaultrealtime';
import Platform from '../../common/platform';
import ErrorInfo from '../../common/lib/types/errorinfo';
import { fromDeserialized as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';

// Platform Specific
import BufferUtils from './lib/util/bufferutils';
Expand Down
15 changes: 14 additions & 1 deletion src/platform/web/modules/realtimepresence.ts
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
export { default as RealtimePresence } from '../../../common/lib/client/realtimepresence';
import { RealtimePresenceModule } from 'common/lib/client/modulesmap';
import { default as realtimePresenceClass } from '../../../common/lib/client/realtimepresence';
import {
fromValues as presenceMessageFromValues,
fromValuesArray as presenceMessagesFromValuesArray,
} from '../../../common/lib/types/presencemessage';

const RealtimePresence: RealtimePresenceModule = {
RealtimePresence: realtimePresenceClass,
presenceMessageFromValues,
presenceMessagesFromValuesArray,
};

export { RealtimePresence };
23 changes: 23 additions & 0 deletions test/browser/modules.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,29 @@ describe('browser/modules', function () {

expect(() => channel.presence).to.throw('RealtimePresence module not provided');
});

it('doesn’t break when it receives a PRESENCE ProtocolMessage', async () => {
const rxClient = new BaseRealtime(ablyClientOptions(), { WebSocketTransport, FetchRequest });
const rxChannel = rxClient.channels.get('channel');

await rxChannel.attach();

const receivedMessagePromise = new Promise((resolve) => rxChannel.subscribe(resolve));

const txClient = new BaseRealtime(ablyClientOptions({ clientId: randomString() }), {
WebSocketTransport,
FetchRequest,
RealtimePresence,
});
const txChannel = txClient.channels.get('channel');

await txChannel.publish('message', 'body');
await txChannel.presence.enter();

// The idea being here that in order for receivedMessagePromise to resolve, rxClient must have first processed the PRESENCE ProtocolMessage that resulted from txChannel.presence.enter()

await receivedMessagePromise;
});
});

describe('BaseRealtime with RealtimePresence', () => {
Expand Down
Loading