Skip to content

Commit

Permalink
materialisation: Add new message attributes and actions handling
Browse files Browse the repository at this point in the history
- Added new message attributes, including `action`, `serial`, `refSerial`, `refType`, `updatedAt`, `deletedAt`, and `operation`.
Additionally, create functions to map message actions between string and number representations.
This update also changes the `fromValues` function to handle action transformations and increments the package version to 2.5.0.
  • Loading branch information
VeskeR authored and splindsay-92 committed Oct 10, 2024
2 parents 5bfb32f + b3af19a commit e6e9d57
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 14 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

This contains only the most important and/or user-facing changes; for a full changelog, see the commit history.

## [2.4.1](https://github.com/ably/ably-js/tree/2.4.1) (2024-10-04)

- Fix `usePresence` hook wasn't leaving presence if component unmounted during channel attaching state [\#1884](https://github.com/ably/ably-js/pull/1884)

## [2.4.0](https://github.com/ably/ably-js/tree/2.4.0) (2024-09-11)

- Add `wsConnectivityCheckUrl` client option [\#1862](https://github.com/ably/ably-js/pull/1862)
Expand Down
89 changes: 88 additions & 1 deletion ably.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2335,12 +2335,99 @@ export interface Message {
* Timestamp of when the message was received by Ably, as milliseconds since the Unix epoch.
*/
timestamp?: number;
/**
* The action type of the message, one of the {@link MessageAction} enum values.
*/
action?: MessageAction;
/**
* This message's unique serial.
*/
serial?: string;
/**
* The serial of the message that this message is a reference to.
*/
refSerial?: string;
/**
* The type of reference this message is, in relation to the message it references.
*/
refType?: string;
/**
* If an `update` operation was applied to this message, this will be the timestamp the update occurred.
*/
updatedAt?: number;
/**
* If a `deletion` operation was applied to this message, this will be the timestamp the deletion occurred.
*/
deletedAt?: number;
/**
* If this message resulted from an operation, this will contain the operation details.
*/
operation?: Operation;
}

/**
* Contains the details of an operation, such as update of deletion, supplied by the actioning client.
*/
export interface Operation {
/**
* The client ID of the client that initiated the operation.
*/
clientId?: string;
/**
* The description provided by the client that initiated the operation.
*/
description?: string;
/**
* A JSON object of string key-value pairs that may contain metadata associated with the operation.
*/
metadata?: Record<string, string>;
}

/**
* The namespace containing the different types of message actions.
*/
declare namespace MessageActions {
/**
* Message action has not been set.
*/
type MESSAGE_UNSET = 'MESSAGE_UNSET';
/**
* Message action for a newly created message.
*/
type MESSAGE_CREATE = 'MESSAGE_CREATE';
/**
* Message action for an updated message.
*/
type MESSAGE_UPDATE = 'MESSAGE_UPDATE';
/**
* Message action for a deleted message.
*/
type MESSAGE_DELETE = 'MESSAGE_DELETE';
/**
* Message action for a newly created annotation.
*/
type MESSAGE_ANNOTATION_CREATE = 'MESSAGE_ANNOTATION_CREATE';
/**
* Message action for a deleted annotation.
*/
type MESSAGE_ANNOTATION_DELETE = 'MESSAGE_ANNOTATION_DELETE';
}

/**
* Describes the possible action types used on an {@link Message}.
*/
export type MessageAction =
| MessageActions.MESSAGE_UNSET
| MessageActions.MESSAGE_CREATE
| MessageActions.MESSAGE_UPDATE
| MessageActions.MESSAGE_DELETE
| MessageActions.MESSAGE_ANNOTATION_CREATE
| MessageActions.MESSAGE_ANNOTATION_DELETE;

/**
* A message received from Ably.
*/
export type InboundMessage = Message & Required<Pick<Message, 'id' | 'timestamp'>>;
export type InboundMessage = Message & Required<Pick<Message, 'id' | 'timestamp' | 'serial' | 'action'>>;

/**
* Static utilities related to messages.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ably",
"description": "Realtime client library for Ably, the realtime messaging service",
"version": "2.4.0",
"version": "2.5.0",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/ably/ably-js/issues",
Expand Down
11 changes: 6 additions & 5 deletions src/common/lib/types/defaultmessage.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import Message, {
CipherOptions,
fromEncoded,
fromEncodedArray,
encode,
decode,
encode,
EncodingDecodingContext,
fromEncoded,
fromEncodedArray,
fromValues,
} from './message';
import * as API from '../../../../ably';
import Platform from 'common/platform';
Expand All @@ -25,8 +26,8 @@ export class DefaultMessage extends Message {
}

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

// Used by tests
Expand Down
59 changes: 55 additions & 4 deletions src/common/lib/types/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@ import * as API from '../../../../ably';
import { IUntypedCryptoStatic } from 'common/types/ICryptoStatic';
import { MsgPack } from 'common/types/msgpack';

const MessageActionNumberMap: Record<number, API.MessageAction> = {
0: 'MESSAGE_UNSET',
1: 'MESSAGE_CREATE',
2: 'MESSAGE_UPDATE',
3: 'MESSAGE_DELETE',
4: 'MESSAGE_ANNOTATION_CREATE',
5: 'MESSAGE_ANNOTATION_DELETE',
};

function toMessageActionString(actionNumber: number): API.MessageAction {
if (actionNumber in MessageActionNumberMap) {
return MessageActionNumberMap[actionNumber];
} else {
throw new ErrorInfo(`Unsupported action number: ${actionNumber}`, 40000, 400);
}
}

function toMessageActionNumber(messageAction: API.MessageAction): number {
for (const [key, value] of Object.entries(MessageActionNumberMap)) {
if (value === messageAction) {
return parseInt(key);
}
}
throw new ErrorInfo(`Unsupported action string: ${messageAction}`, 40000, 400);
}

export type CipherOptions = {
channelCipher: {
encrypt: Function;
Expand Down Expand Up @@ -82,7 +108,7 @@ export async function fromEncoded(
encoded: unknown,
inputOptions?: API.ChannelOptions,
): Promise<Message> {
const msg = fromValues(encoded);
const msg = fromValues(encoded as Message | Record<string, unknown>, true);
const options = normalizeCipherOptions(Crypto, logger, inputOptions ?? null);
/* if decoding fails at any point, catch and return the message decoded to
* the fullest extent possible */
Expand Down Expand Up @@ -260,7 +286,7 @@ export async function fromResponseBody(
}

for (let i = 0; i < body.length; i++) {
const msg = (body[i] = fromValues(body[i]));
const msg = (body[i] = fromValues(body[i], true));
try {
await decode(msg, options);
} catch (e) {
Expand All @@ -270,14 +296,17 @@ export async function fromResponseBody(
return body;
}

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

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

Expand All @@ -304,6 +333,13 @@ class Message {
encoding?: string | null;
extras?: any;
size?: number;
action?: API.MessageAction | number;
serial?: string;
refSerial?: string;
refType?: string;
updatedAt?: number;
deletedAt?: number;
operation?: API.Operation;

/**
* Overload toJSON() to intercept JSON.stringify()
Expand Down Expand Up @@ -334,6 +370,13 @@ class Message {
connectionId: this.connectionId,
connectionKey: this.connectionKey,
extras: this.extras,
serial: this.serial,
action: toMessageActionNumber(this.action as API.MessageAction),
refSerial: this.refSerial,
refType: this.refType,
updatedAt: this.updatedAt,
deletedAt: this.deletedAt,
operation: this.operation,
encoding,
data,
};
Expand All @@ -355,6 +398,14 @@ class Message {
else result += '; data (json)=' + JSON.stringify(this.data);
}
if (this.extras) result += '; extras=' + JSON.stringify(this.extras);

if (this.action) result += '; action=' + this.action;
if (this.serial) result += '; serial=' + this.serial;
if (this.refSerial) result += '; refSerial=' + this.refSerial;
if (this.refType) result += '; refType=' + this.refType;
if (this.updatedAt) result += '; updatedAt=' + this.updatedAt;
if (this.deletedAt) result += '; deletedAt=' + this.deletedAt;
if (this.operation) result += '; operation=' + JSON.stringify(this.operation);
result += ']';
return result;
}
Expand Down
2 changes: 1 addition & 1 deletion src/platform/react-hooks/src/AblyReactHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type ChannelNameAndOptions = {
export type ChannelNameAndAblyId = Pick<ChannelNameAndOptions, 'channelName' | 'ablyId'>;
export type ChannelParameters = string | ChannelNameAndOptions;

export const version = '2.4.0';
export const version = '2.4.1';

export function channelOptionsWithAgent(options?: Ably.ChannelOptions) {
return {
Expand Down

0 comments on commit e6e9d57

Please sign in to comment.