Skip to content

Commit c406bef

Browse files
committed
Refactor Message/PresenceMessage.fromValues and related
Previously, fromValues() took an options object with a 'stringifyAction' bool property. If that was false, then it acted as a normal fromValues; if true, then it was effectively doing a partial fromDeserialized, parsing a wire protocol message into a message but without decoding the message.data. This seemed a bit silly. Removed the options object and replaced it with an explicit Message.fromWireProtocol. Also while doing that ended up changing a bunch of other things that seemed broken: - unnecessary type-assertions due to not having a wire-protocol type, or unnecessary use of any & unknown - channeloptions being type-asserted as cipheroptions, then type-assert back to channeloptions, which made no sense - crypto not being passed-in to history calls, so not getting decrypted
1 parent dce28af commit c406bef

17 files changed

+217
-165
lines changed

ably.d.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2516,9 +2516,8 @@ export interface PresenceMessageStatic {
25162516
* Initialises a `PresenceMessage` from a `PresenceMessage`-like object.
25172517
*
25182518
* @param values - The values to intialise the `PresenceMessage` from.
2519-
* @param stringifyAction - Whether to convert the `action` field from a number to a string.
25202519
*/
2521-
fromValues(values: PresenceMessage | Record<string, unknown>, stringifyAction?: boolean): PresenceMessage;
2520+
fromValues(values: Partial<Pick<PresenceMessage, 'clientId' | 'data' | 'extras'>>): PresenceMessage;
25222521
}
25232522

25242523
/**

src/common/lib/client/defaultrealtime.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { FilteredSubscriptions } from './filteredsubscriptions';
1414
import {
1515
fromValues as presenceMessageFromValues,
1616
fromValuesArray as presenceMessagesFromValuesArray,
17+
fromWireProtocol as presenceMessageFromWireProtocol,
1718
} from '../types/presencemessage';
1819
import { Http } from 'common/types/http';
1920
import Defaults from '../util/defaults';
@@ -39,6 +40,7 @@ export class DefaultRealtime extends BaseRealtime {
3940
RealtimePresence,
4041
presenceMessageFromValues,
4142
presenceMessagesFromValuesArray,
43+
presenceMessageFromWireProtocol,
4244
},
4345
WebSocketTransport,
4446
MessageInteractions: FilteredSubscriptions,

src/common/lib/client/modularplugins.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import { FilteredSubscriptions } from './filteredsubscriptions';
88
import {
99
fromValues as presenceMessageFromValues,
1010
fromValuesArray as presenceMessagesFromValuesArray,
11+
fromWireProtocol as presenceMessageFromWireProtocol,
1112
} from '../types/presencemessage';
1213
import { TransportCtor } from '../transport/transport';
1314
import * as PushPlugin from 'plugins/push';
1415

1516
export interface PresenceMessagePlugin {
1617
presenceMessageFromValues: typeof presenceMessageFromValues;
1718
presenceMessagesFromValuesArray: typeof presenceMessagesFromValuesArray;
19+
presenceMessageFromWireProtocol: typeof presenceMessageFromWireProtocol;
1820
}
1921

2022
export type RealtimePresencePlugin = PresenceMessagePlugin & {

src/common/lib/client/restchannelmixin.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as API from '../../../../ably';
22
import RestChannel from './restchannel';
33
import RealtimeChannel from './realtimechannel';
44
import * as Utils from '../util/utils';
5-
import Message, { fromResponseBody as messageFromResponseBody } from '../types/message';
5+
import Message, { WireProtocolMessage, _fromEncodedArray } from '../types/message';
66
import Defaults from '../util/defaults';
77
import PaginatedResource, { PaginatedResult } from './paginatedresource';
88
import Resource from './resource';
@@ -30,19 +30,16 @@ export class RestChannelMixin {
3030

3131
Utils.mixin(headers, client.options.headers);
3232

33-
const options = channel.channelOptions;
3433
return new PaginatedResource(client, this.basePath(channel) + '/messages', headers, envelope, async function (
3534
body,
3635
headers,
3736
unpacked,
3837
) {
39-
return await messageFromResponseBody(
40-
body as Message[],
41-
options,
42-
channel.logger,
43-
client._MsgPack,
44-
unpacked ? undefined : format,
45-
);
38+
const decoded: WireProtocolMessage[] = unpacked
39+
? (body as WireProtocolMessage[])
40+
: Utils.decodeBody(body, client._MsgPack, format);
41+
42+
return _fromEncodedArray(decoded, channel);
4643
}).get(params as Record<string, unknown>);
4744
}
4845

src/common/lib/client/restpresence.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as Utils from '../util/utils';
22
import Logger from '../util/logger';
33
import PaginatedResource, { PaginatedResult } from './paginatedresource';
4-
import PresenceMessage, { fromResponseBody as presenceMessageFromResponseBody } from '../types/presencemessage';
5-
import { CipherOptions } from '../types/message';
4+
import PresenceMessage, { WireProtocolPresenceMessage, fromEncodedArray } from '../types/presencemessage';
5+
import Platform from '../../platform';
66
import RestChannel from './restchannel';
77
import Defaults from '../util/defaults';
88

@@ -33,13 +33,11 @@ class RestPresence {
3333
headers,
3434
envelope,
3535
async (body, headers, unpacked) => {
36-
return await presenceMessageFromResponseBody(
37-
body as Record<string, unknown>[],
38-
options as CipherOptions,
39-
this.logger,
40-
client._MsgPack,
41-
unpacked ? undefined : format,
42-
);
36+
const decoded: WireProtocolPresenceMessage[] = unpacked
37+
? (body as WireProtocolPresenceMessage[])
38+
: Utils.decodeBody(body, client._MsgPack, format);
39+
40+
return fromEncodedArray(this.logger, Platform.Crypto, decoded, options);
4341
},
4442
).get(params);
4543
}

src/common/lib/client/restpresencemixin.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import RealtimePresence from './realtimepresence';
33
import * as Utils from '../util/utils';
44
import Defaults from '../util/defaults';
55
import PaginatedResource, { PaginatedResult } from './paginatedresource';
6-
import PresenceMessage, { fromResponseBody as presenceMessageFromResponseBody } from '../types/presencemessage';
7-
import { CipherOptions } from '../types/message';
6+
import PresenceMessage, { WireProtocolPresenceMessage, _fromEncodedArray } from '../types/presencemessage';
87
import { RestChannelMixin } from './restchannelmixin';
98

109
export class RestPresenceMixin {
@@ -23,19 +22,18 @@ export class RestPresenceMixin {
2322

2423
Utils.mixin(headers, client.options.headers);
2524

26-
const options = presence.channel.channelOptions;
27-
return new PaginatedResource(client, this.basePath(presence) + '/history', headers, envelope, async function (
28-
body,
25+
return new PaginatedResource(
26+
client,
27+
this.basePath(presence) + '/history',
2928
headers,
30-
unpacked,
31-
) {
32-
return await presenceMessageFromResponseBody(
33-
body as Record<string, unknown>[],
34-
options as CipherOptions,
35-
presence.logger,
36-
client._MsgPack,
37-
unpacked ? undefined : format,
38-
);
39-
}).get(params);
29+
envelope,
30+
async (body, headers, unpacked) => {
31+
const decoded: WireProtocolPresenceMessage[] = unpacked
32+
? (body as WireProtocolPresenceMessage[])
33+
: Utils.decodeBody(body, client._MsgPack, format);
34+
35+
return _fromEncodedArray(decoded, presence.channel);
36+
},
37+
).get(params);
4038
}
4139
}

src/common/lib/types/defaultmessage.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,41 @@
11
import Message, {
2+
WireProtocolMessage,
23
CipherOptions,
34
decode,
45
encode,
56
EncodingDecodingContext,
67
fromEncoded,
78
fromEncodedArray,
89
fromValues,
10+
fromWireProtocol,
911
} from './message';
1012
import * as API from '../../../../ably';
1113
import Platform from 'common/platform';
1214
import PresenceMessage from './presencemessage';
1315
import { ChannelOptions } from 'common/types/channel';
1416
import Logger from '../util/logger';
17+
import type { Properties } from '../util/utils';
1518

1619
/**
1720
`DefaultMessage` is the class returned by `DefaultRest` and `DefaultRealtime`’s `Message` static property. It introduces the static methods described in the `MessageStatic` interface of the public API of the non tree-shakable version of the library.
1821
*/
1922
export class DefaultMessage extends Message {
2023
static async fromEncoded(encoded: unknown, inputOptions?: API.ChannelOptions): Promise<Message> {
21-
return fromEncoded(Logger.defaultLogger, Platform.Crypto, encoded, inputOptions);
24+
return fromEncoded(Logger.defaultLogger, Platform.Crypto, encoded as WireProtocolMessage, inputOptions);
2225
}
2326

2427
static async fromEncodedArray(encodedArray: Array<unknown>, options?: API.ChannelOptions): Promise<Message[]> {
25-
return fromEncodedArray(Logger.defaultLogger, Platform.Crypto, encodedArray, options);
28+
return fromEncodedArray(Logger.defaultLogger, Platform.Crypto, encodedArray as WireProtocolMessage[], options);
2629
}
2730

2831
// Used by tests
29-
static fromValues(values: Message | Record<string, unknown>, options?: { stringifyAction?: boolean }): Message {
30-
return fromValues(values, options);
32+
static fromValues(values: Properties<Message>): Message {
33+
return fromValues(values);
34+
}
35+
36+
// Used by tests
37+
static fromWireProtocol(values: WireProtocolMessage): Message {
38+
return fromWireProtocol(values);
3139
}
3240

3341
// Used by tests
Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,35 @@
11
import * as API from '../../../../ably';
22
import Logger from '../util/logger';
3-
import PresenceMessage, { fromEncoded, fromEncodedArray, fromValues } from './presencemessage';
3+
import PresenceMessage, {
4+
fromEncoded,
5+
fromEncodedArray,
6+
fromValues,
7+
WireProtocolPresenceMessage,
8+
} from './presencemessage';
9+
import Platform from 'common/platform';
10+
import type { Properties } from '../util/utils';
411

512
/**
613
`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.
714
*/
815
export class DefaultPresenceMessage extends PresenceMessage {
916
static async fromEncoded(encoded: unknown, inputOptions?: API.ChannelOptions): Promise<PresenceMessage> {
10-
return fromEncoded(Logger.defaultLogger, encoded, inputOptions);
17+
return fromEncoded(Logger.defaultLogger, Platform.Crypto, encoded as WireProtocolPresenceMessage, inputOptions);
1118
}
1219

1320
static async fromEncodedArray(
1421
encodedArray: Array<unknown>,
1522
options?: API.ChannelOptions,
1623
): Promise<PresenceMessage[]> {
17-
return fromEncodedArray(Logger.defaultLogger, encodedArray, options);
24+
return fromEncodedArray(
25+
Logger.defaultLogger,
26+
Platform.Crypto,
27+
encodedArray as WireProtocolPresenceMessage[],
28+
options,
29+
);
1830
}
1931

20-
static fromValues(values: PresenceMessage | Record<string, unknown>, stringifyAction?: boolean): PresenceMessage {
21-
return fromValues(values, stringifyAction);
32+
static fromValues(values: Properties<PresenceMessage>): PresenceMessage {
33+
return fromValues(values);
2234
}
2335
}

src/common/lib/types/message.ts

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import Platform from 'common/platform';
22
import Logger from '../util/logger';
33
import ErrorInfo from './errorinfo';
4-
import { ChannelOptions } from '../../types/channel';
54
import PresenceMessage from './presencemessage';
65
import * as Utils from '../util/utils';
76
import { Bufferlike as BrowserBufferlike } from '../../../platform/web/lib/util/bufferutils';
87
import * as API from '../../../../ably';
9-
import { IUntypedCryptoStatic } from 'common/types/ICryptoStatic';
10-
import { MsgPack } from 'common/types/msgpack';
8+
9+
import type { IUntypedCryptoStatic } from 'common/types/ICryptoStatic';
10+
import type { ChannelOptions } from '../../types/channel';
11+
import type { Properties } from '../util/utils';
12+
import type RestChannel from '../client/restchannel';
13+
import type RealtimeChannel from '../client/realtimechannel';
14+
type Channel = RestChannel | RealtimeChannel;
1115

1216
const MessageActionArray: API.MessageAction[] = [
1317
'message.unset',
@@ -56,6 +60,8 @@ export type EncodingDecodingContext = {
5660
baseEncodedPreviousPayload?: Buffer | BrowserBufferlike;
5761
};
5862

63+
export type WireProtocolMessage = Omit<Message, 'action'> & { action: number };
64+
5965
function normaliseContext(context: CipherOptions | EncodingDecodingContext | ChannelOptions): EncodingDecodingContext {
6066
if (!context || !(context as EncodingDecodingContext).channelOptions) {
6167
return {
@@ -67,7 +73,7 @@ function normaliseContext(context: CipherOptions | EncodingDecodingContext | Cha
6773
return context as EncodingDecodingContext;
6874
}
6975

70-
function normalizeCipherOptions(
76+
export function normalizeCipherOptions(
7177
Crypto: IUntypedCryptoStatic | null,
7278
logger: Logger,
7379
options: API.ChannelOptions | null,
@@ -103,10 +109,10 @@ function getMessageSize(msg: Message) {
103109
export async function fromEncoded(
104110
logger: Logger,
105111
Crypto: IUntypedCryptoStatic | null,
106-
encoded: unknown,
112+
encoded: WireProtocolMessage,
107113
inputOptions?: API.ChannelOptions,
108114
): Promise<Message> {
109-
const msg = fromValues(encoded as Message | Record<string, unknown>, { stringifyAction: true });
115+
const msg = fromWireProtocol(encoded);
110116
const options = normalizeCipherOptions(Crypto, logger, inputOptions ?? null);
111117
/* if decoding fails at any point, catch and return the message decoded to
112118
* the fullest extent possible */
@@ -121,7 +127,7 @@ export async function fromEncoded(
121127
export async function fromEncodedArray(
122128
logger: Logger,
123129
Crypto: IUntypedCryptoStatic | null,
124-
encodedArray: Array<unknown>,
130+
encodedArray: Array<WireProtocolMessage>,
125131
options?: API.ChannelOptions,
126132
): Promise<Message[]> {
127133
return Promise.all(
@@ -131,6 +137,26 @@ export async function fromEncodedArray(
131137
);
132138
}
133139

140+
// these forms of the functions are used internally when we have a channel instance
141+
// already, so don't need to normalise channel options
142+
export async function _fromEncoded(encoded: WireProtocolMessage, channel: Channel): Promise<Message> {
143+
const msg = fromWireProtocol(encoded);
144+
try {
145+
await decode(msg, channel.channelOptions);
146+
} catch (e) {
147+
Logger.logAction(channel.logger, Logger.LOG_ERROR, 'Message._fromEncoded()', (e as Error).toString());
148+
}
149+
return msg;
150+
}
151+
152+
export async function _fromEncodedArray(encodedArray: WireProtocolMessage[], channel: Channel): Promise<Message[]> {
153+
return Promise.all(
154+
encodedArray.map(function (encoded) {
155+
return _fromEncoded(encoded, channel);
156+
}),
157+
);
158+
}
159+
134160
async function encrypt<T extends Message | PresenceMessage>(msg: T, options: CipherOptions): Promise<T> {
135161
let data = msg.data,
136162
encoding = msg.encoding,
@@ -272,45 +298,17 @@ export async function decode(
272298
context.baseEncodedPreviousPayload = lastPayload;
273299
}
274300

275-
export async function fromResponseBody(
276-
body: Array<Message>,
277-
options: ChannelOptions | EncodingDecodingContext,
278-
logger: Logger,
279-
MsgPack: MsgPack | null,
280-
format?: Utils.Format,
281-
): Promise<Message[]> {
282-
if (format) {
283-
body = Utils.decodeBody(body, MsgPack, format);
284-
}
285-
286-
for (let i = 0; i < body.length; i++) {
287-
const msg = (body[i] = fromValues(body[i], { stringifyAction: true }));
288-
try {
289-
await decode(msg, options);
290-
} catch (e) {
291-
Logger.logAction(logger, Logger.LOG_ERROR, 'Message.fromResponseBody()', (e as Error).toString());
292-
}
293-
}
294-
return body;
301+
export function fromValues(values: Properties<Message>): Message {
302+
return Object.assign(new Message(), values);
295303
}
296304

297-
export function fromValues(
298-
values: Message | Record<string, unknown>,
299-
options?: { stringifyAction?: boolean },
300-
): Message {
301-
const stringifyAction = options?.stringifyAction;
302-
if (stringifyAction) {
303-
const action = toMessageActionString(values.action as number) || values.action;
304-
return Object.assign(new Message(), { ...values, action });
305-
}
306-
return Object.assign(new Message(), values);
305+
export function fromWireProtocol(values: WireProtocolMessage): Message {
306+
const action = toMessageActionString(values.action as number) || values.action;
307+
return Object.assign(new Message(), { ...values, action });
307308
}
308309

309-
export function fromValuesArray(values: unknown[]): Message[] {
310-
const count = values.length,
311-
result = new Array(count);
312-
for (let i = 0; i < count; i++) result[i] = fromValues(values[i] as Record<string, unknown>);
313-
return result;
310+
export function fromValuesArray(values: Properties<Message>[]): Message[] {
311+
return values.map(fromValues);
314312
}
315313

316314
/* This should be called on encode()d (and encrypt()d) Messages (as it
@@ -336,7 +334,7 @@ class Message {
336334
encoding?: string | null;
337335
extras?: any;
338336
size?: number;
339-
action?: API.MessageAction | number;
337+
action?: API.MessageAction;
340338
serial?: string;
341339
refSerial?: string;
342340
refType?: string;

0 commit comments

Comments
 (0)