diff --git a/ably.d.ts b/ably.d.ts index be483f1c5..228f90b78 100644 --- a/ably.d.ts +++ b/ably.d.ts @@ -2332,6 +2332,7 @@ 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; /** @@ -2339,27 +2340,32 @@ export interface Message { */ 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; } diff --git a/src/common/lib/client/realtimechannel.ts b/src/common/lib/client/realtimechannel.ts index 24ffe62cf..dad5eede3 100644 --- a/src/common/lib/client/realtimechannel.ts +++ b/src/common/lib/client/realtimechannel.ts @@ -622,6 +622,7 @@ class RealtimeChannel extends EventEmitter { firstMessage = messages[0], lastMessage = messages[messages.length - 1], id = message.id, + channelSerial = message.channelSerial, connectionId = message.connectionId, timestamp = message.timestamp; @@ -670,9 +671,14 @@ class RealtimeChannel extends EventEmitter { if (!msg.connectionId) msg.connectionId = connectionId; if (!msg.timestamp) msg.timestamp = timestamp; if (!msg.id) msg.id = id + ':' + i; + if (!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._lastPayload.protocolMessageChannelSerial = channelSerial; this.onEvent(messages); break; } diff --git a/src/common/lib/types/message.ts b/src/common/lib/types/message.ts index 3e522c7b2..ebaddbe1c 100644 --- a/src/common/lib/types/message.ts +++ b/src/common/lib/types/message.ts @@ -281,7 +281,9 @@ export function fromValues(values: Properties): Message { export function fromWireProtocol(values: WireProtocolMessage): Message { const action = toMessageActionString(values.action as number) || values.action; - return Object.assign(new Message(), { ...values, action }); + const res = Object.assign(new Message(), { ...values, action }); + res.expandFields(); + return res; } export function fromValuesArray(values: Properties[]): Message[] { @@ -315,8 +317,8 @@ class Message { serial?: string; refSerial?: string; refType?: string; - updatedAt?: number; - updateSerial?: string; + createdAt?: number; + version?: string; operation?: API.Operation; /** @@ -352,14 +354,27 @@ class Message { action: toMessageActionNumber(this.action as API.MessageAction) || this.action, refSerial: this.refSerial, refType: this.refType, - updatedAt: this.updatedAt, - updateSerial: this.updateSerial, + createdAt: this.createdAt, + version: this.version, operation: this.operation, encoding, data, }; } + expandFields() { + if (this.action === 'message.create') { + // TM2k + if (this.version && !this.serial) { + this.serial = this.version; + } + // TM2o + if (this.timestamp && !this.createdAt) { + this.createdAt = this.timestamp; + } + } + } + toString(): string { let result = '[Message'; if (this.name) result += '; name=' + this.name; @@ -379,10 +394,10 @@ class Message { if (this.action) result += '; action=' + this.action; if (this.serial) result += '; serial=' + this.serial; + if (this.version) result += '; version=' + this.version; if (this.refSerial) result += '; refSerial=' + this.refSerial; if (this.refType) result += '; refType=' + this.refType; - if (this.updatedAt) result += '; updatedAt=' + this.updatedAt; - if (this.updateSerial) result += '; updateSerial=' + this.updateSerial; + if (this.createdAt) result += '; createdAt=' + this.createdAt; if (this.operation) result += '; operation=' + JSON.stringify(this.operation); result += ']'; return result; diff --git a/test/realtime/message.test.js b/test/realtime/message.test.js index 1490bdd7b..9f54cce7d 100644 --- a/test/realtime/message.test.js +++ b/test/realtime/message.test.js @@ -1310,6 +1310,25 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async expect(message.toJSON()).to.deep.contains(expectedJSON); }); }); + + /** + * @spec TM2k + * @spec TM2o + */ + it('create message should fill out serial and createdAt from version/timestamp', function () { + const values = { action: 1, timestamp: 12345, version: 'foo' }; + const message = Message.fromWireProtocol(values); + expect(message.timestamp).to.equal(12345); + expect(message.createdAt).to.equal(12345); + expect(message.version).to.equal('foo'); + expect(message.serial).to.equal('foo'); + + // should only apply to creates + const update = { action: 2, timestamp: 12345, version: 'foo' }; + const updateMessage = Message.fromWireProtocol(update); + expect(updateMessage.createdAt).to.equal(undefined); + expect(updateMessage.serial).to.equal(undefined); + }); }); /**