Skip to content

Commit

Permalink
Remove PresenceMessage-related static things in tree-shakable library
Browse files Browse the repository at this point in the history
As in 601b46b.

Resolves #1427.
  • Loading branch information
lawrence-forooghian committed Nov 14, 2023
1 parent 7cbd6fc commit 0e722b9
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 44 deletions.
3 changes: 3 additions & 0 deletions scripts/moduleReport.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ const functions = [
{ name: 'decodeEncryptedMessage', transitiveImports: ['Crypto'] },
{ name: 'decodeMessages', transitiveImports: [] },
{ name: 'decodeEncryptedMessages', transitiveImports: ['Crypto'] },
{ name: 'decodePresenceMessage', transitiveImports: [] },
{ name: 'decodePresenceMessages', transitiveImports: [] },
{ name: 'constructPresenceMessage', transitiveImports: [] },
];

function formatBytes(bytes) {
Expand Down
2 changes: 0 additions & 2 deletions src/common/lib/client/baseclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import ClientOptions, { NormalisedClientOptions } from '../../types/ClientOption
import * as API from '../../../../ably';

import Platform from '../../platform';
import PresenceMessage from '../types/presencemessage';
import { ModulesMap } from './modulesmap';
import { Rest } from './rest';
import { IUntypedCryptoStatic } from 'common/types/ICryptoStatic';
Expand Down Expand Up @@ -153,7 +152,6 @@ class BaseClient {
}

static Platform = Platform;
static PresenceMessage = PresenceMessage;
}

export default BaseClient;
2 changes: 2 additions & 0 deletions src/common/lib/client/defaultrealtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Platform from 'common/platform';
import { DefaultMessage } from '../types/defaultmessage';
import { MsgPack } from 'common/types/msgpack';
import RealtimePresence from './realtimepresence';
import { DefaultPresenceMessage } from '../types/defaultpresencemessage';

/**
`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 Down Expand Up @@ -39,6 +40,7 @@ export class DefaultRealtime extends BaseRealtime {
}

static Message = DefaultMessage;
static PresenceMessage = DefaultPresenceMessage;

static _MsgPack: MsgPack | null = null;
}
2 changes: 2 additions & 0 deletions src/common/lib/client/defaultrest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { allCommonModules } from './modulesmap';
import Platform from 'common/platform';
import { DefaultMessage } from '../types/defaultmessage';
import { MsgPack } from 'common/types/msgpack';
import { DefaultPresenceMessage } from '../types/defaultpresencemessage';

/**
`DefaultRest` is the class that the non tree-shakable version of the SDK exports as `Rest`. It ensures that this version of the SDK includes all of the functionality which is optionally available in the tree-shakable version.
Expand Down Expand Up @@ -35,6 +36,7 @@ export class DefaultRest extends BaseRest {
}

static Message = DefaultMessage;
static PresenceMessage = DefaultPresenceMessage;

static _MsgPack: MsgPack | null = null;
}
4 changes: 2 additions & 2 deletions src/common/lib/client/realtimechannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import RealtimePresence from './realtimepresence';
import Message, { CipherOptions } from '../types/message';
import ChannelStateChange from './channelstatechange';
import ErrorInfo, { IPartialErrorInfo, PartialErrorInfo } from '../types/errorinfo';
import PresenceMessage from '../types/presencemessage';
import PresenceMessage, { fromValues as presenceMessageFromValues } from '../types/presencemessage';
import ConnectionErrors from '../transport/connectionerrors';
import * as API from '../../../../ably';
import ConnectionManager from '../transport/connectionmanager';
Expand Down Expand Up @@ -591,7 +591,7 @@ class RealtimeChannel extends Channel {
channel: this.name,
presence: Utils.isArray(presence)
? PresenceMessage.fromValuesArray(presence)
: [PresenceMessage.fromValues(presence)],
: [presenceMessageFromValues(presence)],
});
this.sendMessage(msg, callback);
}
Expand Down
10 changes: 5 additions & 5 deletions src/common/lib/client/realtimepresence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as Utils from '../util/utils';
import Presence from './presence';
import EventEmitter from '../util/eventemitter';
import Logger from '../util/logger';
import PresenceMessage from '../types/presencemessage';
import PresenceMessage, { fromValues as presenceMessageFromValues } from '../types/presencemessage';
import ErrorInfo, { IPartialErrorInfo, PartialErrorInfo } from '../types/errorinfo';
import RealtimeChannel from './realtimechannel';
import Multicaster from '../util/multicaster';
Expand Down Expand Up @@ -342,7 +342,7 @@ class RealtimePresence extends Presence {
}

for (let i = 0; i < presenceSet.length; i++) {
const presence = PresenceMessage.fromValues(presenceSet[i]);
const presence = presenceMessageFromValues(presenceSet[i]);
switch (presence.action) {
case 'leave':
if (members.remove(presence)) {
Expand Down Expand Up @@ -480,7 +480,7 @@ class RealtimePresence extends Presence {
_synthesizeLeaves(items: PresenceMessage[]): void {
const subscriptions = this.subscriptions;
Utils.arrForEach(items, function (item) {
const presence = PresenceMessage.fromValues({
const presence = presenceMessageFromValues({
action: 'leave',
connectionId: item.connectionId,
clientId: item.clientId,
Expand Down Expand Up @@ -568,7 +568,7 @@ class PresenceMap extends EventEmitter {

put(item: PresenceMessage) {
if (item.action === 'enter' || item.action === 'update') {
item = PresenceMessage.fromValues(item);
item = presenceMessageFromValues(item);
item.action = 'present';
}
const map = this.map,
Expand Down Expand Up @@ -606,7 +606,7 @@ class PresenceMap extends EventEmitter {

/* RTP2f */
if (this.syncInProgress) {
item = PresenceMessage.fromValues(item);
item = presenceMessageFromValues(item);
item.action = 'absent';
map[key] = item;
} else {
Expand Down
22 changes: 22 additions & 0 deletions src/common/lib/types/defaultpresencemessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as API from '../../../../ably';
import PresenceMessage, { fromEncoded, fromEncodedArray, fromValues } from './presencemessage';

/**
`DefaultPresenceMessage` is the class returned by `DefaultRest` and `DefaultRealtime`’s `PresenceMessage` static property. It introduces the static methods described in the `PresenceMessageStatic` interface of the public API of the non tree-shakable version of the library.
*/
export class DefaultPresenceMessage extends PresenceMessage {
static async fromEncoded(encoded: unknown, inputOptions?: API.Types.ChannelOptions): Promise<PresenceMessage> {
return fromEncoded(encoded, inputOptions);
}

static async fromEncodedArray(
encodedArray: Array<unknown>,
options?: API.Types.ChannelOptions
): Promise<PresenceMessage[]> {
return fromEncodedArray(encodedArray, options);
}

static fromValues(values: PresenceMessage | Record<string, unknown>, stringifyAction?: boolean): PresenceMessage {
return fromValues(values, stringifyAction);
}
}
69 changes: 36 additions & 33 deletions src/common/lib/types/presencemessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,39 @@ function toActionValue(actionString: string) {
return PresenceMessage.Actions.indexOf(actionString);
}

export async function fromEncoded(encoded: unknown, options?: API.Types.ChannelOptions): Promise<PresenceMessage> {
const msg = fromValues(encoded as PresenceMessage | Record<string, unknown>, true);
/* if decoding fails at any point, catch and return the message decoded to
* the fullest extent possible */
try {
await PresenceMessage.decode(msg, options ?? {});
} catch (e) {
Logger.logAction(Logger.LOG_ERROR, 'PresenceMessage.fromEncoded()', (e as Error).toString());
}
return msg;
}

export async function fromEncodedArray(
encodedArray: unknown[],
options?: API.Types.ChannelOptions
): Promise<PresenceMessage[]> {
return Promise.all(
encodedArray.map(function (encoded) {
return fromEncoded(encoded, options);
})
);
}

export function fromValues(
values: PresenceMessage | Record<string, unknown>,
stringifyAction?: boolean
): PresenceMessage {
if (stringifyAction) {
values.action = PresenceMessage.Actions[values.action as number];
}
return Object.assign(new PresenceMessage(), values);
}

class PresenceMessage {
action?: string | number;
id?: string;
Expand Down Expand Up @@ -121,7 +154,7 @@ class PresenceMessage {
}

for (let i = 0; i < body.length; i++) {
const msg = (messages[i] = PresenceMessage.fromValues(body[i], true));
const msg = (messages[i] = fromValues(body[i], true));
try {
await PresenceMessage.decode(msg, options);
} catch (e) {
Expand All @@ -131,48 +164,18 @@ class PresenceMessage {
return messages;
}

static fromValues(values: PresenceMessage | Record<string, unknown>, stringifyAction?: boolean): PresenceMessage {
if (stringifyAction) {
values.action = PresenceMessage.Actions[values.action as number];
}
return Object.assign(new PresenceMessage(), values);
}

static fromValuesArray(values: unknown[]): PresenceMessage[] {
const count = values.length,
result = new Array(count);
for (let i = 0; i < count; i++) result[i] = PresenceMessage.fromValues(values[i] as Record<string, unknown>);
for (let i = 0; i < count; i++) result[i] = fromValues(values[i] as Record<string, unknown>);
return result;
}

static async fromEncoded(encoded: unknown, options?: API.Types.ChannelOptions): Promise<PresenceMessage> {
const msg = PresenceMessage.fromValues(encoded as PresenceMessage | Record<string, unknown>, true);
/* if decoding fails at any point, catch and return the message decoded to
* the fullest extent possible */
try {
await PresenceMessage.decode(msg, options ?? {});
} catch (e) {
Logger.logAction(Logger.LOG_ERROR, 'PresenceMessage.fromEncoded()', (e as Error).toString());
}
return msg;
}

static async fromEncodedArray(
encodedArray: unknown[],
options?: API.Types.ChannelOptions
): Promise<PresenceMessage[]> {
return Promise.all(
encodedArray.map(function (encoded) {
return PresenceMessage.fromEncoded(encoded, options);
})
);
}

static fromData(data: unknown): PresenceMessage {
if (data instanceof PresenceMessage) {
return data;
}
return PresenceMessage.fromValues({
return fromValues({
data,
});
}
Expand Down
4 changes: 2 additions & 2 deletions src/common/lib/types/protocolmessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Types } from '../../../../ably';
import * as Utils from '../util/utils';
import ErrorInfo from './errorinfo';
import Message from './message';
import PresenceMessage from './presencemessage';
import PresenceMessage, { fromValues as presenceMessageFromValues } from './presencemessage';

const actions = {
HEARTBEAT: 0,
Expand Down Expand Up @@ -121,7 +121,7 @@ class ProtocolMessage {
const messages = deserialized.messages as Message[];
if (messages) for (let i = 0; i < messages.length; i++) messages[i] = Message.fromValues(messages[i]);
const presence = deserialized.presence as PresenceMessage[];
if (presence) for (let i = 0; i < presence.length; i++) presence[i] = PresenceMessage.fromValues(presence[i], true);
if (presence) for (let i = 0; i < presence.length; i++) presence[i] = presenceMessageFromValues(presence[i], true);
return Object.assign(new ProtocolMessage(), deserialized);
};

Expand Down
1 change: 1 addition & 0 deletions src/platform/web/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ if (Platform.Config.noUpgrade) {

export * from './modules/crypto';
export * from './modules/message';
export * from './modules/presencemessage';
export * from './modules/msgpack';
export * from './modules/realtimepresence';
export { Rest } from '../../common/lib/client/rest';
Expand Down
8 changes: 8 additions & 0 deletions src/platform/web/modules/presencemessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as API from '../../../../ably';
import { fromEncoded, fromEncodedArray, fromValues } from '../../../common/lib/types/presencemessage';

// The type assertions for the functions below are due to https://github.com/ably/ably-js/issues/1421

export const decodePresenceMessage = fromEncoded as API.Types.PresenceMessageStatic['fromEncoded'];
export const decodePresenceMessages = fromEncodedArray as API.Types.PresenceMessageStatic['fromEncodedArray'];
export const constructPresenceMessage = fromValues as API.Types.PresenceMessageStatic['fromValues'];
46 changes: 46 additions & 0 deletions test/browser/modules.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import {
Crypto,
MsgPack,
RealtimePresence,
decodePresenceMessage,
decodePresenceMessages,
constructPresenceMessage,
} from '../../build/modules/index.js';

describe('browser/modules', function () {
Expand Down Expand Up @@ -351,4 +354,47 @@ describe('browser/modules', function () {
});
});
});

describe('PresenceMessage standalone functions', () => {
describe('decodePresenceMessage', () => {
it('decodes a presence message’s data', async () => {
const buffer = BufferUtils.utf8Encode('foo');
const encodedMessage = { data: BufferUtils.base64Encode(buffer), encoding: 'base64' };

const decodedMessage = await decodePresenceMessage(encodedMessage);

expect(BufferUtils.areBuffersEqual(decodedMessage.data, buffer)).to.be.true;
expect(decodedMessage.encoding).to.be.null;
});
});

describe('decodeMessages', () => {
it('decodes presence messages’ data', async () => {
const buffers = ['foo', 'bar'].map((data) => BufferUtils.utf8Encode(data));
const encodedMessages = buffers.map((buffer) => ({
data: BufferUtils.base64Encode(buffer),
encoding: 'base64',
}));

const decodedMessages = await decodePresenceMessages(encodedMessages);

for (let i = 0; i < decodedMessages.length; i++) {
const decodedMessage = decodedMessages[i];

expect(BufferUtils.areBuffersEqual(decodedMessage.data, buffers[i])).to.be.true;
expect(decodedMessage.encoding).to.be.null;
}
});
});

describe('constructPresenceMessage', () => {
it('creates a PresenceMessage instance', async () => {
const extras = { foo: 'bar' };
const presenceMessage = constructPresenceMessage({ extras });

expect(presenceMessage.constructor.name).to.contain('PresenceMessage');
expect(presenceMessage.extras).to.equal(extras);
});
});
});
});

0 comments on commit 0e722b9

Please sign in to comment.