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

[ECO-5116] Added new fields to the ARTMessage. #1998

Merged
merged 1 commit into from
Nov 26, 2024
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
1 change: 1 addition & 0 deletions Source/ARTBaseMessage.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ - (id)copyWithZone:(NSZone *)zone {
message->_data = [self.data copy];
message->_connectionId = self.connectionId;
message->_encoding = self.encoding;
message->_extras = self.extras;
return message;
}

Expand Down
43 changes: 35 additions & 8 deletions Source/ARTJsonLikeEncoder.m
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ - (NSString *)formatAsString {
}

- (ARTMessage *)decodeMessage:(NSData *)data error:(NSError **)error {
return [self messageFromDictionary:[self decodeDictionary:data error:error]];
return [self messageFromDictionary:[self decodeDictionary:data error:error] protocolMessage:nil];
}

- (NSArray *)decodeMessages:(NSData *)data error:(NSError **)error {
return [self messagesFromArray:[self decodeArray:data error:error]];
return [self messagesFromArray:[self decodeArray:data error:error] protocolMessage:nil];
}

- (NSData *)encodeMessage:(ARTMessage *)message error:(NSError **)error {
Expand Down Expand Up @@ -266,7 +266,15 @@ - (NSArray *)decodeStats:(NSData *)data error:(NSError **)error {
return [self statsFromArray:[self decodeArray:data error:error]];
}

- (ARTMessage *)messageFromDictionary:(NSDictionary *)input {
- (nullable NSString *)versionFromInput:(NSDictionary *)input withProtocolMessage:(nullable ARTProtocolMessage *)protocolMessage {
if (protocolMessage.channelSerial == nil) {
return nil;
}
int index = [[input artNumber:@"_index"] intValue];
return [NSString stringWithFormat:@"%@:%03d", protocolMessage.channelSerial, index]; // TM2p <channelSerial>:<padded_index>
}

- (ARTMessage *)messageFromDictionary:(NSDictionary *)input protocolMessage:(ARTProtocolMessage *)protocolMessage {
ARTLogVerbose(_logger, @"RS:%p ARTJsonLikeEncoder<%@>: messageFromDictionary %@", _rest, [_delegate formatAsString], input);
if (![input isKindOfClass:[NSDictionary class]]) {
return nil;
Expand All @@ -275,24 +283,34 @@ - (ARTMessage *)messageFromDictionary:(NSDictionary *)input {
ARTMessage *message = [[ARTMessage alloc] init];
message.id = [input artString:@"id"];
message.name = [input artString:@"name"];
message.action = ([input artNumber:@"action"] ?: [[NSNumber alloc] initWithInt:ARTMessageActionCreate]).integerValue;
message.version = [input artString:@"version"] ?: [self versionFromInput:input withProtocolMessage:protocolMessage]; // TM2p
message.serial = [input artString:@"serial"];
if (!message.serial && message.action == ARTMessageActionCreate) { // TM2k
message.serial = message.version;
}
message.clientId = [input artString:@"clientId"];
message.data = [input objectForKey:@"data"];
message.encoding = [input artString:@"encoding"];;
message.encoding = [input artString:@"encoding"];
message.timestamp = [input artTimestamp:@"timestamp"];
message.createdAt = [input artTimestamp:@"createdAt"];
if (!message.createdAt && message.action == ARTMessageActionCreate) { // TM2o
message.createdAt = message.timestamp;
}
message.connectionId = [input artString:@"connectionId"];
message.extras = [input objectForKey:@"extras"];

return message;
}

- (NSArray *)messagesFromArray:(NSArray *)input {
- (NSArray *)messagesFromArray:(NSArray *)input protocolMessage:(ARTProtocolMessage *)protocolMessage {
if (![input isKindOfClass:[NSArray class]]) {
return nil;
}

NSMutableArray *output = [NSMutableArray array];
for (NSDictionary *item in input) {
ARTMessage *message = [self messageFromDictionary:item];
ARTMessage *message = [self messageFromDictionary:item protocolMessage:protocolMessage];
if (!message) {
return nil;
}
Expand Down Expand Up @@ -734,8 +752,6 @@ - (ARTProtocolMessage *)protocolMessageFromDictionary:(NSDictionary *)input {
message.id = [input artString:@"id"];
message.msgSerial = [input artNumber:@"msgSerial"];
message.timestamp = [input artTimestamp:@"timestamp"];
message.messages = [self messagesFromArray:[input objectForKey:@"messages"]];
message.presence = [self presenceMessagesFromArray:[input objectForKey:@"presence"]];
message.connectionKey = [input artString:@"connectionKey"];
message.flags = [[input artNumber:@"flags"] longLongValue];
message.connectionDetails = [self connectionDetailsFromDictionary:[input valueForKey:@"connectionDetails"]];
Expand All @@ -747,6 +763,17 @@ - (ARTProtocolMessage *)protocolMessageFromDictionary:(NSDictionary *)input {
message.error = [ARTErrorInfo createWithCode:[[error artNumber:@"code"] intValue] status:[[error artNumber:@"statusCode"] intValue] message:[error artString:@"message"]];
}

NSMutableArray *messages = [[input objectForKey:@"messages"] mutableCopy];

// There is probably a better way to do this, but I have limited time to implement TM2p
for (int i = 0; i < messages.count; i++) {
NSMutableDictionary *msgDict = [messages[i] mutableCopy];
msgDict[@"_index"] = @(i);
messages[i] = msgDict;
}
message.messages = [self messagesFromArray:messages protocolMessage:message];
maratal marked this conversation as resolved.
Show resolved Hide resolved
message.presence = [self presenceMessagesFromArray:[input objectForKey:@"presence"]];

return message;
}

Expand Down
9 changes: 6 additions & 3 deletions Source/ARTMessage.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ - (NSString *)description {
- (id)copyWithZone:(NSZone *)zone {
ARTMessage *message = [super copyWithZone:zone];
message.name = self.name;
message.extras = self.extras;
message.action = self.action;
message.serial = self.serial;
maratal marked this conversation as resolved.
Show resolved Hide resolved
message.version = self.version;
message.createdAt = self.createdAt;
return message;
}

Expand All @@ -67,7 +70,7 @@ + (instancetype)fromEncoded:(NSDictionary *)jsonObject channelOptions:(ARTChanne
return nil;
}

ARTMessage *message = [jsonEncoder messageFromDictionary:jsonObject];
ARTMessage *message = [jsonEncoder messageFromDictionary:jsonObject protocolMessage:nil];

NSError *decodeError = nil;
message = [message decodeWithEncoder:decoder error:&decodeError];
Expand Down Expand Up @@ -97,7 +100,7 @@ + (instancetype)fromEncoded:(NSDictionary *)jsonObject channelOptions:(ARTChanne
return nil;
}

NSArray<ARTMessage *> *messages = [jsonEncoder messagesFromArray:jsonArray];
NSArray<ARTMessage *> *messages = [jsonEncoder messagesFromArray:jsonArray protocolMessage:nil];

NSMutableArray<ARTMessage *> *decodedMessages = [NSMutableArray array];
for (ARTMessage *message in messages) {
Expand Down
4 changes: 2 additions & 2 deletions Source/PrivateHeaders/Ably/ARTJsonLikeEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ NS_ASSUME_NONNULL_BEGIN

@interface ARTJsonLikeEncoder ()

- (nullable ARTMessage *)messageFromDictionary:(NSDictionary *)input;
- (nullable NSArray *)messagesFromArray:(NSArray *)input;
- (nullable ARTMessage *)messageFromDictionary:(NSDictionary *)input protocolMessage:(nullable ARTProtocolMessage *)protocolMessage;
- (nullable NSArray *)messagesFromArray:(NSArray *)input protocolMessage:(nullable ARTProtocolMessage *)protocolMessage;

- (nullable ARTPresenceMessage *)presenceMessageFromDictionary:(NSDictionary *)input;
- (nullable NSArray *)presenceMessagesFromArray:(NSArray *)input;
Expand Down
48 changes: 48 additions & 0 deletions Source/include/Ably/ARTMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,41 @@
#import <Ably/ARTTypes.h>
#import <Ably/ARTChannelOptions.h>

/**
* The namespace containing the different types of message actions.
*/
NS_SWIFT_SENDABLE
typedef NS_ENUM(NSUInteger, ARTMessageAction) {
/**
* Message action has not been set.
*/
ARTMessageActionUnset,
/**
* Message action for a newly created message.
*/
ARTMessageActionCreate,
/**
* Message action for an updated message.
*/
ARTMessageActionUpdate,
/**
* Message action for a deleted message.
*/
ARTMessageActionDelete,
/**
* Message action for a newly created annotation.
*/
ARTMessageActionAnnotationCreate,
/**
* Message action for a deleted annotation.
*/
ARTMessageActionAnnotationDelete,
/**
* Message action for a meta-message that contains channel occupancy information.
*/
ARTMessageActionMetaOccupancy,
};

NS_ASSUME_NONNULL_BEGIN

/**
Expand All @@ -14,6 +49,19 @@ NS_ASSUME_NONNULL_BEGIN
/// The event name, if available
@property (nullable, readwrite, nonatomic) NSString *name;

/// The action type of the message, one of the `ARTMessageAction` enum values.
@property (readwrite, nonatomic) ARTMessageAction action;

/// 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.
@property (nullable, readwrite, nonatomic) NSString *version;

/// This message's unique serial (an identifier that will be the same in all future updates of this message).
@property (nullable, readwrite, nonatomic) NSString *serial;
maratal marked this conversation as resolved.
Show resolved Hide resolved
maratal marked this conversation as resolved.
Show resolved Hide resolved

maratal marked this conversation as resolved.
Show resolved Hide resolved
/// The timestamp of the very first version of a given message.
@property (nonatomic, nullable) NSDate *createdAt;

/**
* Construct an `ARTMessage` object with an event name and payload.
*
Expand Down
2 changes: 1 addition & 1 deletion Test/Tests/RestClientChannelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ class RestClientChannelTests: XCTestCase {
XCTAssertTrue((client.internal.encoders["application/json"] as! ARTJsonLikeEncoder).message(from: [
"data": "foo",
"extras": ["push": ["notification": ["title": "Hello from Ably!"]]],
])?.extras == extras)
], protocolMessage: nil)?.extras == extras)

waitUntil(timeout: testTimeout) { done in
channel.publish("name", data: "some data", extras: extras) { error in
Expand Down
104 changes: 104 additions & 0 deletions Test/Tests/UtilitiesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -484,4 +484,108 @@ class UtilitiesTests: XCTestCase {
let expectedSize = "{\"test\":\"test\"}".count + "{\"push\":{\"key\":\"value\"}}".count + clientId.count + message.name!.count
XCTAssertEqual(message.messageSize(), expectedSize)
}

// TM2k
func test__025_Utilities__message_received_with_action_create_does_not_contain_a_serial_takes_it_from_version() throws {
beforeEach__Utilities__JSON_Encoder()
var json = """
{
"messages": [
{
"action": 1,
"version": "123"
}
]
}
"""
var data = json.data(using: .utf8)!
var pm = try jsonEncoder.decodeProtocolMessage(data)
var messages = try XCTUnwrap(pm.messages)
XCTAssert(messages[0].action == .create)
XCTAssert(messages[0].version == "123")
XCTAssert(messages[0].serial == "123")

// should only apply to creates
json = """
{
"messages": [
{
"action": 0,
"version": "123"
}
]
}
"""
data = json.data(using: .utf8)!
pm = try jsonEncoder.decodeProtocolMessage(data)
messages = try XCTUnwrap(pm.messages)
XCTAssert(messages[0].action == .unset)
XCTAssert(messages[0].version == "123")
XCTAssertNil(messages[0].serial)
}

// TM2p
func test__026_Utilities__message_received_over_a_realtime_transport_does_not_contain_a_version() throws {
beforeEach__Utilities__JSON_Encoder()
let json = """
{
"channelSerial": "foo",
"messages": [
{
"serial": "12345"
},
{
"serial": "123456"
}
]
}
"""
let data = json.data(using: .utf8)!
let pm = try jsonEncoder.decodeProtocolMessage(data)
let messages = try XCTUnwrap(pm.messages)
XCTAssert(messages[0].action == .create)
XCTAssert(messages[0].version == "foo:000")
XCTAssert(messages[0].serial == "12345")
XCTAssert(messages[1].action == .create)
XCTAssert(messages[1].version == "foo:001")
XCTAssert(messages[1].serial == "123456")
}

// TM2o
func test__027_Utilities__message_received_with_action_create_does_not_contain_createdAt() throws {
beforeEach__Utilities__JSON_Encoder()
var json = """
{
"messages": [
{
"action": 1,
"timestamp": "1234512345",
}
maratal marked this conversation as resolved.
Show resolved Hide resolved
]
}
"""
var data = json.data(using: .utf8)!
var pm = try jsonEncoder.decodeProtocolMessage(data)
var messages = try XCTUnwrap(pm.messages)
XCTAssert(messages[0].action == .create)
XCTAssertNotNil(messages[0].createdAt)
XCTAssertEqual(messages[0].createdAt, messages[0].timestamp)

// should only apply to creates
json = """
{
"messages": [
{
"action": 0,
"timestamp": "1234512345",
}
]
}
"""
data = json.data(using: .utf8)!
pm = try jsonEncoder.decodeProtocolMessage(data)
messages = try XCTUnwrap(pm.messages)
XCTAssert(messages[0].action == .unset)
XCTAssertNil(messages[0].createdAt)
}
}
Loading