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

[SDK-3824] Remove PresenceMessage-related static things in tree-shakable library #1429

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
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);
});
});
});
});
Loading