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

Feature/1415 channel lifecycle status #1420

Merged
merged 15 commits into from
Jul 20, 2022
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
4 changes: 4 additions & 0 deletions Source/ARTEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ NS_ASSUME_NONNULL_BEGIN
// DeviceDetails
- (nullable NSData *)encodeDeviceDetails:(ARTDeviceDetails *)deviceDetails error:(NSError *_Nullable *_Nullable)error;
- (nullable ARTDeviceDetails *)decodeDeviceDetails:(NSData *)data error:(NSError *_Nullable *_Nullable)error;

// ChannelDetails
- (nullable ARTChannelDetails *)decodeChannelDetails:(NSData *)data error:(NSError *_Nullable *_Nullable)error;

- (nullable NSArray<ARTDeviceDetails *> *)decodeDevicesDetails:(NSData *)data error:(NSError * __autoreleasing *)error;
- (nullable ARTDeviceIdentityTokenDetails *)decodeDeviceIdentityTokenDetails:(NSData *)data error:(NSError * __autoreleasing *)error;

Expand Down
49 changes: 41 additions & 8 deletions Source/ARTJsonLikeEncoder.m
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ - (ARTDeviceDetails *)decodeDeviceDetails:(NSData *)data error:(NSError **)error
return [self deviceDetailsFromDictionary:[self decodeDictionary:data error:nil] error:error];
}

- (ARTChannelDetails *)decodeChannelDetails:(NSData *)data error:(NSError **)error {
return [self channelDetailsFromDictionary:[self decodeDictionary:data error:error]];
}

- (NSArray<ARTDeviceDetails *> *)decodeDevicesDetails:(NSData *)data error:(NSError * __autoreleasing *)error {
return [self devicesDetailsFromArray:[self decodeArray:data error:nil] error:error];
}
Expand Down Expand Up @@ -742,15 +746,44 @@ - (ARTConnectionDetails *)connectionDetailsFromDictionary:(NSDictionary *)input
if (!input) {
return nil;
}
return [[ARTConnectionDetails alloc] initWithClientId:[input artString:@"clientId"]
connectionKey:[input artString:@"connectionKey"]
maxMessageSize:[input artInteger:@"maxMessageSize"]
maxFrameSize:[input artInteger:@"maxFrameSize"]
maxInboundRate:[input artInteger:@"maxInboundRate"]
connectionStateTtl:millisecondsToTimeInterval([input artInteger:@"connectionStateTtl"])
serverId:[input artString:@"serverId"]
maxIdleInterval:millisecondsToTimeInterval([input artInteger:@"maxIdleInterval"])];
}

- (ARTChannelMetrics *)channelMetricsFromDictionary:(NSDictionary *)input {
return [[ARTChannelMetrics alloc] initWithConnections:[input artInteger:@"connections"]
publishers:[input artInteger:@"publishers"]
subscribers:[input artInteger:@"subscribers"]
presenceConnections:[input artInteger:@"presenceConnections"]
presenceMembers:[input artInteger:@"presenceMembers"]
presenceSubscribers:[input artInteger:@"presenceSubscribers"]];;
}

- (ARTChannelOccupancy *)channelOccupancyFromDictionary:(NSDictionary *)input {
NSDictionary *metricsDict = [input valueForKey:@"metrics"];
ARTChannelMetrics *metrics = [self channelMetricsFromDictionary:metricsDict];
ARTChannelOccupancy *occupancy = [[ARTChannelOccupancy alloc] initWithMetrics:metrics];
return occupancy;
}

- (ARTChannelStatus *)channelStatusFromDictionary:(NSDictionary *)input {
NSDictionary *occupancyDict = [input valueForKey:@"occupancy"];
ARTChannelOccupancy *occupancy = [self channelOccupancyFromDictionary:occupancyDict];
ARTChannelStatus *status = [[ARTChannelStatus alloc] initWithOccupancy:occupancy active:[input artBoolean:@"isActive"]];
return status;
}

return [[ARTConnectionDetails alloc] initWithClientId:[input artString:@"clientId"]
connectionKey:[input artString:@"connectionKey"]
maxMessageSize:[input artInteger:@"maxMessageSize"]
maxFrameSize:[input artInteger:@"maxFrameSize"]
maxInboundRate:[input artInteger:@"maxInboundRate"]
connectionStateTtl:millisecondsToTimeInterval([input artInteger:@"connectionStateTtl"])
serverId:[input artString:@"serverId"]
maxIdleInterval:millisecondsToTimeInterval([input artInteger:@"maxIdleInterval"])];
- (ARTChannelDetails *)channelDetailsFromDictionary:(NSDictionary *)input {
NSDictionary *statusDict = [input valueForKey:@"status"];
ARTChannelStatus *status = [self channelStatusFromDictionary:statusDict];
ARTChannelDetails *details = [[ARTChannelDetails alloc] initWithChannelId:[input artString:@"channelId"] status:status];
return details;
}

- (NSArray *)statsFromArray:(NSArray *)input {
Expand Down
2 changes: 2 additions & 0 deletions Source/ARTRestChannel.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ NS_ASSUME_NONNULL_BEGIN

- (BOOL)history:(nullable ARTDataQuery *)query callback:(ARTPaginatedMessagesCallback)callback error:(NSError *_Nullable *_Nullable)errorPtr;

- (void)status:(ARTChannelDetailsCallback)callback;

- (void)setOptions:(ARTChannelOptions *_Nullable)options;

@end
Expand Down
65 changes: 65 additions & 0 deletions Source/ARTRestChannel.m
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ - (BOOL)history:(nullable ARTDataQuery *)query callback:(ARTPaginatedMessagesCal
return [_internal history:query callback:callback error:errorPtr];
}

- (void)status:(ARTChannelDetailsCallback)callback {
[_internal status:callback];
}

- (void)publish:(nullable NSString *)name data:(nullable id)data {
[_internal publish:name data:data];
}
Expand Down Expand Up @@ -214,6 +218,67 @@ - (BOOL)history:(ARTDataQuery *)query callback:(ARTPaginatedMessagesCallback)cal
return ret;
}

- (void)status:(ARTChannelDetailsCallback)callback {
if (callback) {
ARTChannelDetailsCallback userCallback = callback;
callback = ^(ARTChannelDetails *details, ARTErrorInfo *_Nullable error) {
dispatch_async(self->_userQueue, ^{
userCallback(details, error);
});
};
}
dispatch_async(_queue, ^{
NSURL *url = [NSURL URLWithString:self->_basePath];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
lawrence-forooghian marked this conversation as resolved.
Show resolved Hide resolved

[self.logger debug:__FILE__ line:__LINE__ message:@"RS:%p C:%p (%@) channel details request %@", self->_rest, self, self.name, request];

[self->_rest executeRequest:request withAuthOption:ARTAuthenticationOn completion:^(NSHTTPURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable error) {

if (response.statusCode == 200 /*OK*/) {
NSError *decodeError = nil;
id<ARTEncoder> decoder = self->_rest.encoders[response.MIMEType];
if (decoder == nil) {
NSString* errorMessage = [NSString stringWithFormat:@"Decoder for MIMEType '%@' wasn't found.", response.MIMEType];
[self.logger debug:__FILE__ line:__LINE__ message:@"%@: %@", NSStringFromClass(self.class), errorMessage];
if (callback) {
callback(nil, [ARTErrorInfo createWithCode:ARTErrorUnableToDecodeMessage message:errorMessage]);
}
}
else {
ARTChannelDetails *channelDetails = [decoder decodeChannelDetails:data error:&decodeError];
if (decodeError) {
[self.logger debug:__FILE__ line:__LINE__ message:@"%@: decode channel details failed (%@)", NSStringFromClass(self.class), error.localizedDescription];
if (callback) {
callback(nil, [ARTErrorInfo createFromNSError:decodeError]);
}
}
else {
[self.logger debug:__FILE__ line:__LINE__ message:@"%@: successfully got channel details %@", NSStringFromClass(self.class), channelDetails.channelId];
if (callback) {
callback(channelDetails, nil);
}
}
}
}
else {
[self.logger debug:__FILE__ line:__LINE__ message:@"%@: get channel details failed (%@)", NSStringFromClass(self.class), error.localizedDescription];
ARTErrorInfo *errorInfo = nil;
if (error) {
if (self->_rest.options.addRequestIds) {
errorInfo = [ARTErrorInfo wrap:[ARTErrorInfo createFromNSError:error] prepend:[NSString stringWithFormat:@"Request '%@' failed with ", request.URL]];
} else {
errorInfo = [ARTErrorInfo createFromNSError:error];
}
}
if (callback) {
callback(nil, errorInfo);
}
}
}];
});
}

- (void)internalPostMessages:(id)data callback:(ARTCallback)callback {
if (callback) {
ARTCallback userCallback = callback;
Expand Down
55 changes: 55 additions & 0 deletions Source/ARTTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,60 @@ NSString *generateNonce(void);

@end

#pragma mark - ARTChannelMetrics

@interface ARTChannelMetrics : NSObject

@property (nonatomic, readonly) NSInteger connections;
@property (nonatomic, readonly) NSInteger publishers;
@property (nonatomic, readonly) NSInteger subscribers;
@property (nonatomic, readonly) NSInteger presenceConnections;
@property (nonatomic, readonly) NSInteger presenceMembers;
@property (nonatomic, readonly) NSInteger presenceSubscribers;

- (instancetype)initWithConnections:(NSInteger)connections
publishers:(NSInteger)publishers
subscribers:(NSInteger)subscribers
presenceConnections:(NSInteger)presenceConnections
presenceMembers:(NSInteger)presenceMembers
presenceSubscribers:(NSInteger)presenceSubscribers;

@end

#pragma mark - ARTChannelOccupancy

@interface ARTChannelOccupancy : NSObject

@property (nonatomic, strong, readonly) ARTChannelMetrics *metrics;

- (instancetype)initWithMetrics:(ARTChannelMetrics *)metrics;

@end

#pragma mark - ARTChannelStatus

@interface ARTChannelStatus : NSObject

@property (nonatomic, readonly) BOOL active;

@property (nonatomic, strong, readonly) ARTChannelOccupancy *occupancy;

- (instancetype)initWithOccupancy:(ARTChannelOccupancy *)occupancy active:(BOOL)active;

@end

#pragma mark - ARTChannelDetails

@interface ARTChannelDetails : NSObject

@property (nonatomic, strong, readonly) NSString *channelId;

@property (nonatomic, strong, readonly) ARTChannelStatus *status;
lawrence-forooghian marked this conversation as resolved.
Show resolved Hide resolved

- (instancetype)initWithChannelId:(NSString *)channelId status:(ARTChannelStatus *)status;

@end

#pragma mark - ARTJsonCompatible

@protocol ARTJsonCompatible <NSObject>
Expand Down Expand Up @@ -264,6 +318,7 @@ typedef void (^ARTChannelStateCallback)(ARTChannelStateChange *stateChange);
typedef void (^ARTConnectionStateCallback)(ARTConnectionStateChange *stateChange);
typedef void (^ARTPresenceMessageCallback)(ARTPresenceMessage *message);
typedef void (^ARTPresenceMessagesCallback)(NSArray<ARTPresenceMessage *> *_Nullable result, ARTErrorInfo *_Nullable error);
typedef void (^ARTChannelDetailsCallback)(ARTChannelDetails *_Nullable details, ARTErrorInfo *_Nullable error);

typedef void (^ARTStatusCallback)(ARTStatus *status);
typedef void (^ARTURLRequestCallback)(NSHTTPURLResponse *_Nullable result, NSData *_Nullable data, NSError *_Nullable error);
Expand Down
65 changes: 65 additions & 0 deletions Source/ARTTypes.m
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,71 @@ - (NSString *)description {

@end

#pragma mark - ARTChannelMetrics

@implementation ARTChannelMetrics

- (instancetype)initWithConnections:(NSInteger)connections
publishers:(NSInteger)publishers
subscribers:(NSInteger)subscribers
presenceConnections:(NSInteger)presenceConnections
presenceMembers:(NSInteger)presenceMembers
presenceSubscribers:(NSInteger)presenceSubscribers {

if (self = [super init]) {
_connections = connections;
_publishers = publishers;
_subscribers = subscribers;
_presenceConnections = presenceConnections;
_presenceMembers = presenceMembers;
_presenceSubscribers = presenceSubscribers;
}
return self;
}

@end

#pragma mark - ARTChannelOccupancy

@implementation ARTChannelOccupancy

- (instancetype)initWithMetrics:(ARTChannelMetrics *)metrics {
if (self = [super init]) {
_metrics = metrics;
}
return self;
}

@end

#pragma mark - ARTChannelStatus

@implementation ARTChannelStatus

- (instancetype)initWithOccupancy:(ARTChannelOccupancy *)occupancy active:(BOOL)active {
if (self = [super init]) {
_occupancy = occupancy;
_active = active;
}
return self;
}

@end

#pragma mark - ARTChannelDetails

@implementation ARTChannelDetails

- (instancetype)initWithChannelId:(NSString *)channelId status:(ARTChannelStatus *)status {
if (self = [super init]) {
_channelId = channelId;
_status = status;
}
return self;
}

@end

#pragma mark - ARTEventIdentification

@implementation NSString (ARTEventIdentification)
Expand Down
1 change: 1 addition & 0 deletions Source/Private/ARTNSDictionary+ARTDictionaryUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- (NSArray *)artArray:(id)key;
- (NSDictionary *)artDictionary:(id)key;
- (NSInteger)artInteger:(id)key;
- (BOOL)artBoolean:(id)key;

- (id)artTyped:(Class)cls key:(id)key;

Expand Down
4 changes: 4 additions & 0 deletions Source/Private/ARTNSDictionary+ARTDictionaryUtil.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,8 @@ - (NSInteger)artInteger:(id)key {
return 0;
}

- (BOOL)artBoolean:(id)key {
return [self artInteger:key] != 0;
}

@end
34 changes: 34 additions & 0 deletions Spec/Tests/RestClientChannelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1488,4 +1488,38 @@ class RestClientChannelTests: XCTestCase {
}
}
}

// RSL8a, CHD2b, CHS2b, CHO2a
func test__047__status__with_subscribers__returns_a_channel_details_object_populated_with_channel_metrics() {
let options = AblyTests.commonAppSetup()
options.clientId = "Client 1"
let rest = ARTRest(options: options)
let realtime = ARTRealtime(options: options)
let channelName = uniqueChannelName()
let realtimeChannel = realtime.channels.get(channelName)
lawrence-forooghian marked this conversation as resolved.
Show resolved Hide resolved
let restChannel = rest.channels.get(channelName)

waitUntil(timeout: testTimeout) { done in
lawrence-forooghian marked this conversation as resolved.
Show resolved Hide resolved
realtimeChannel.presence.enter(nil) { error in
expect(error).to(beNil())
done()
}
}
waitUntil(timeout: testTimeout) { done in
restChannel.status { details, error in
lawrence-forooghian marked this conversation as resolved.
Show resolved Hide resolved
expect(error).to(beNil())
guard let details = details else {
fail("Channel details are empty"); done()
return
}
expect(details.status.occupancy.metrics.connections) == 1 // CHM2a
expect(details.status.occupancy.metrics.publishers) == 1 // CHM2e
expect(details.status.occupancy.metrics.subscribers) == 1 // CHM2f
expect(details.status.occupancy.metrics.presenceMembers) == 1 // CHM2c
expect(details.status.occupancy.metrics.presenceConnections) == 1 // CHM2b
expect(details.status.occupancy.metrics.presenceSubscribers) == 1 // CHM2d
done()
}
}
}
}