Skip to content

Commit

Permalink
Merge pull request #1923 from ably/mutable-message-field-changes
Browse files Browse the repository at this point in the history
Mutable message field changes and Message/PresenceMessage.fromValues refactoring
  • Loading branch information
SimonWoolf authored Dec 9, 2024
2 parents 416c9b0 + a35dfa8 commit 174953b
Show file tree
Hide file tree
Showing 20 changed files with 308 additions and 182 deletions.
25 changes: 15 additions & 10 deletions ably.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2332,34 +2332,40 @@ export interface Message {
name?: string;
/**
* Timestamp of when the message was received by Ably, as milliseconds since the Unix epoch.
* (This is the timestamp of the current version of the message)
*/
timestamp?: number;
/**
* The action type of the message, one of the {@link MessageAction} enum values.
*/
action?: MessageAction;
/**
* This message's unique serial.
* This message's unique serial (an identifier that will be the same in all future
* updates of this message).
*/
serial?: string;
/**
* The serial of the message that this message is a reference to.
* If this message references another, the serial of that message.
*/
refSerial?: string;
/**
* The type of reference this message is, in relation to the message it references.
* If this message references another, the type of reference that is.
*/
refType?: string;
/**
* If an `update` operation was applied to this message, this will be the timestamp the update occurred.
* The timestamp of the very first version of a given message (will differ from
* createdAt only if the message has been updated or deleted).
*/
updatedAt?: number;
createdAt?: number;
/**
* The serial of the operation that updated this message.
* The version of the message, lexicographically-comparable with other versions (that
* share the same serial) Will differ from the serial only if the message has been
* updated or deleted.
*/
updateSerial?: string;
version?: string;
/**
* If this message resulted from an operation, this will contain the operation details.
* In the case of an updated or deleted message, this will contain metadata about the
* update or delete operation.
*/
operation?: Operation;
}
Expand Down Expand Up @@ -2516,9 +2522,8 @@ export interface PresenceMessageStatic {
* Initialises a `PresenceMessage` from a `PresenceMessage`-like object.
*
* @param values - The values to intialise the `PresenceMessage` from.
* @param stringifyAction - Whether to convert the `action` field from a number to a string.
*/
fromValues(values: PresenceMessage | Record<string, unknown>, stringifyAction?: boolean): PresenceMessage;
fromValues(values: Partial<Pick<PresenceMessage, 'clientId' | 'data' | 'extras'>>): PresenceMessage;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions modular.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
* | `MessageStatic.fromEncodedArray()` | [`decodeMessages()`](../functions/modular.decodeMessages.html) |
* | `MessageStatic.fromEncodedArray()` | [`decodeEncryptedMessages()`](../functions/modular.decodeEncryptedMessages.html) |
* | `PresenceMessageStatic.fromEncoded()` | [`decodePresenceMessage()`](../functions/modular.decodePresenceMessage.html) |
* | `PresenceMessageStatic.fromEncoded()` | [`decodeEncryptedPresenceMessage()`](../functions/modular.decodeEncryptedPresenceMessage.html) |
* | `PresenceMessageStatic.fromEncodedArray()` | [`decodeEncryptedPresenceMessages()`](../functions/modular.decodeEncryptedPresenceMessages.html) |
* | `PresenceMessageStatic.fromEncodedArray()` | [`decodePresenceMessages()`](../functions/modular.decodePresenceMessages.html) |
* | `PresenceMessageStatic.fromValues()` | [`constructPresenceMessage()`](../functions/modular.constructPresenceMessage.html) |
*
Expand Down Expand Up @@ -60,6 +62,8 @@ export declare const decodeMessages: MessageStatic['fromEncodedArray'];
export declare const decodeEncryptedMessages: MessageStatic['fromEncodedArray'];
export declare const decodePresenceMessage: PresenceMessageStatic['fromEncoded'];
export declare const decodePresenceMessages: PresenceMessageStatic['fromEncodedArray'];
export declare const decodeEncryptedPresenceMessage: PresenceMessageStatic['fromEncoded'];
export declare const decodeEncryptedPresenceMessages: PresenceMessageStatic['fromEncodedArray'];
export declare const constructPresenceMessage: PresenceMessageStatic['fromValues'];

/**
Expand Down
2 changes: 2 additions & 0 deletions scripts/moduleReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const functions = [
{ name: 'decodeEncryptedMessages', transitiveImports: ['Crypto'] },
{ name: 'decodePresenceMessage', transitiveImports: [] },
{ name: 'decodePresenceMessages', transitiveImports: [] },
{ name: 'decodeEncryptedPresenceMessage', transitiveImports: ['Crypto'] },
{ name: 'decodeEncryptedPresenceMessages', transitiveImports: ['Crypto'] },
{ name: 'constructPresenceMessage', transitiveImports: [] },
];

Expand Down
2 changes: 2 additions & 0 deletions src/common/lib/client/defaultrealtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { PresenceMap } from './presencemap';
import {
fromValues as presenceMessageFromValues,
fromValuesArray as presenceMessagesFromValuesArray,
fromWireProtocol as presenceMessageFromWireProtocol,
} from '../types/presencemessage';
import { Http } from 'common/types/http';
import Defaults from '../util/defaults';
Expand All @@ -40,6 +41,7 @@ export class DefaultRealtime extends BaseRealtime {
RealtimePresence,
presenceMessageFromValues,
presenceMessagesFromValuesArray,
presenceMessageFromWireProtocol,
},
WebSocketTransport,
MessageInteractions: FilteredSubscriptions,
Expand Down
2 changes: 2 additions & 0 deletions src/common/lib/client/modularplugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import { FilteredSubscriptions } from './filteredsubscriptions';
import {
fromValues as presenceMessageFromValues,
fromValuesArray as presenceMessagesFromValuesArray,
fromWireProtocol as presenceMessageFromWireProtocol,
} from '../types/presencemessage';
import { TransportCtor } from '../transport/transport';
import * as PushPlugin from 'plugins/push';

export interface PresenceMessagePlugin {
presenceMessageFromValues: typeof presenceMessageFromValues;
presenceMessagesFromValuesArray: typeof presenceMessagesFromValuesArray;
presenceMessageFromWireProtocol: typeof presenceMessageFromWireProtocol;
}

export type RealtimePresencePlugin = PresenceMessagePlugin & {
Expand Down
15 changes: 13 additions & 2 deletions src/common/lib/client/realtimechannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,8 @@ class RealtimeChannel extends EventEmitter {

const messages = message.messages as Array<Message>,
firstMessage = messages[0],
lastMessage = messages[messages.length - 1];
lastMessage = messages[messages.length - 1],
channelSerial = message.channelSerial;

if (
firstMessage.extras &&
Expand Down Expand Up @@ -652,6 +653,16 @@ class RealtimeChannel extends EventEmitter {
return;
}

for (let i = 0; i < messages.length; i++) {
const msg = messages[i];
if (channelSerial && !msg.version) {
msg.version = channelSerial + ':' + i.toString().padStart(3, '0');
// already done in fromWireProtocol -- but for realtime messages the source
// fields might be copied from the protocolmessage, so need to do it again
msg.expandFields();
}
}

this._lastPayload.messageId = lastMessage.id;
this._lastPayload.protocolMessageChannelSerial = message.channelSerial;
this.onEvent(messages);
Expand Down Expand Up @@ -720,7 +731,7 @@ class RealtimeChannel extends EventEmitter {

if (!msg.connectionId) msg.connectionId = connectionId;
if (!msg.timestamp) msg.timestamp = timestamp;
if (!msg.id) msg.id = id + ':' + i;
if (id && !msg.id) msg.id = id + ':' + i;
}

return { unrecoverableError: false };
Expand Down
15 changes: 6 additions & 9 deletions src/common/lib/client/restchannelmixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as API from '../../../../ably';
import RestChannel from './restchannel';
import RealtimeChannel from './realtimechannel';
import * as Utils from '../util/utils';
import Message, { fromResponseBody as messageFromResponseBody } from '../types/message';
import Message, { WireProtocolMessage, _fromEncodedArray } from '../types/message';
import Defaults from '../util/defaults';
import PaginatedResource, { PaginatedResult } from './paginatedresource';
import Resource from './resource';
Expand Down Expand Up @@ -30,19 +30,16 @@ export class RestChannelMixin {

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

const options = channel.channelOptions;
return new PaginatedResource(client, this.basePath(channel) + '/messages', headers, envelope, async function (
body,
headers,
unpacked,
) {
return await messageFromResponseBody(
body as Message[],
options,
channel.logger,
client._MsgPack,
unpacked ? undefined : format,
);
const decoded: WireProtocolMessage[] = unpacked
? (body as WireProtocolMessage[])
: Utils.decodeBody(body, client._MsgPack, format);

return _fromEncodedArray(decoded, channel);
}).get(params as Record<string, unknown>);
}

Expand Down
16 changes: 6 additions & 10 deletions src/common/lib/client/restpresence.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import * as Utils from '../util/utils';
import Logger from '../util/logger';
import PaginatedResource, { PaginatedResult } from './paginatedresource';
import PresenceMessage, { fromResponseBody as presenceMessageFromResponseBody } from '../types/presencemessage';
import { CipherOptions } from '../types/message';
import PresenceMessage, { WireProtocolPresenceMessage, _fromEncodedArray } from '../types/presencemessage';
import RestChannel from './restchannel';
import Defaults from '../util/defaults';

Expand All @@ -26,20 +25,17 @@ class RestPresence {

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

const options = this.channel.channelOptions;
return new PaginatedResource(
client,
this.channel.client.rest.presenceMixin.basePath(this),
headers,
envelope,
async (body, headers, unpacked) => {
return await presenceMessageFromResponseBody(
body as Record<string, unknown>[],
options as CipherOptions,
this.logger,
client._MsgPack,
unpacked ? undefined : format,
);
const decoded: WireProtocolPresenceMessage[] = unpacked
? (body as WireProtocolPresenceMessage[])
: Utils.decodeBody(body, client._MsgPack, format);

return _fromEncodedArray(decoded, this.channel);
},
).get(params);
}
Expand Down
28 changes: 13 additions & 15 deletions src/common/lib/client/restpresencemixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import RealtimePresence from './realtimepresence';
import * as Utils from '../util/utils';
import Defaults from '../util/defaults';
import PaginatedResource, { PaginatedResult } from './paginatedresource';
import PresenceMessage, { fromResponseBody as presenceMessageFromResponseBody } from '../types/presencemessage';
import { CipherOptions } from '../types/message';
import PresenceMessage, { WireProtocolPresenceMessage, _fromEncodedArray } from '../types/presencemessage';
import { RestChannelMixin } from './restchannelmixin';

export class RestPresenceMixin {
Expand All @@ -23,19 +22,18 @@ export class RestPresenceMixin {

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

const options = presence.channel.channelOptions;
return new PaginatedResource(client, this.basePath(presence) + '/history', headers, envelope, async function (
body,
return new PaginatedResource(
client,
this.basePath(presence) + '/history',
headers,
unpacked,
) {
return await presenceMessageFromResponseBody(
body as Record<string, unknown>[],
options as CipherOptions,
presence.logger,
client._MsgPack,
unpacked ? undefined : format,
);
}).get(params);
envelope,
async (body, headers, unpacked) => {
const decoded: WireProtocolPresenceMessage[] = unpacked
? (body as WireProtocolPresenceMessage[])
: Utils.decodeBody(body, client._MsgPack, format);

return _fromEncodedArray(decoded, presence.channel);
},
).get(params);
}
}
16 changes: 12 additions & 4 deletions src/common/lib/types/defaultmessage.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
import Message, {
WireProtocolMessage,
CipherOptions,
decode,
encode,
EncodingDecodingContext,
fromEncoded,
fromEncodedArray,
fromValues,
fromWireProtocol,
} from './message';
import * as API from '../../../../ably';
import Platform from 'common/platform';
import PresenceMessage from './presencemessage';
import { ChannelOptions } from 'common/types/channel';
import Logger from '../util/logger';
import type { Properties } from '../util/utils';

/**
`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.
*/
export class DefaultMessage extends Message {
static async fromEncoded(encoded: unknown, inputOptions?: API.ChannelOptions): Promise<Message> {
return fromEncoded(Logger.defaultLogger, Platform.Crypto, encoded, inputOptions);
return fromEncoded(Logger.defaultLogger, Platform.Crypto, encoded as WireProtocolMessage, inputOptions);
}

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

// Used by tests
static fromValues(values: Message | Record<string, unknown>, options?: { stringifyAction?: boolean }): Message {
return fromValues(values, options);
static fromValues(values: Properties<Message>): Message {
return fromValues(values);
}

// Used by tests
static fromWireProtocol(values: WireProtocolMessage): Message {
return fromWireProtocol(values);
}

// Used by tests
Expand Down
22 changes: 17 additions & 5 deletions src/common/lib/types/defaultpresencemessage.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import * as API from '../../../../ably';
import Logger from '../util/logger';
import PresenceMessage, { fromEncoded, fromEncodedArray, fromValues } from './presencemessage';
import PresenceMessage, {
fromEncoded,
fromEncodedArray,
fromValues,
WireProtocolPresenceMessage,
} from './presencemessage';
import Platform from 'common/platform';
import type { Properties } from '../util/utils';

/**
`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.ChannelOptions): Promise<PresenceMessage> {
return fromEncoded(Logger.defaultLogger, encoded, inputOptions);
return fromEncoded(Logger.defaultLogger, Platform.Crypto, encoded as WireProtocolPresenceMessage, inputOptions);
}

static async fromEncodedArray(
encodedArray: Array<unknown>,
options?: API.ChannelOptions,
): Promise<PresenceMessage[]> {
return fromEncodedArray(Logger.defaultLogger, encodedArray, options);
return fromEncodedArray(
Logger.defaultLogger,
Platform.Crypto,
encodedArray as WireProtocolPresenceMessage[],
options,
);
}

static fromValues(values: PresenceMessage | Record<string, unknown>, stringifyAction?: boolean): PresenceMessage {
return fromValues(values, stringifyAction);
static fromValues(values: Properties<PresenceMessage>): PresenceMessage {
return fromValues(values);
}
}
Loading

0 comments on commit 174953b

Please sign in to comment.