986 lines
38 KiB
Objective-C
986 lines
38 KiB
Objective-C
#import <Foundation/Foundation.h>
|
|
|
|
#import "ARTJsonLikeEncoder.h"
|
|
|
|
#import "ARTMessage.h"
|
|
#import "ARTPresence.h"
|
|
#import "ARTPresenceMessage.h"
|
|
#import "ARTProtocolMessage.h"
|
|
#import "ARTProtocolMessage+Private.h"
|
|
#import "ARTNSDictionary+ARTDictionaryUtil.h"
|
|
#import "ARTNSDate+ARTUtil.h"
|
|
#import "ARTLog.h"
|
|
#import "ARTHttp.h"
|
|
#import "ARTStatus.h"
|
|
#import "ARTTokenDetails.h"
|
|
#import "ARTTokenRequest.h"
|
|
#import "ARTAuthDetails.h"
|
|
#import "ARTPush.h"
|
|
#import "ARTDeviceDetails.h"
|
|
#import "ARTDeviceIdentityTokenDetails.h"
|
|
#import "ARTDevicePushDetails.h"
|
|
#import "ARTConnectionDetails.h"
|
|
#import "ARTRest+Private.h"
|
|
#import "ARTJsonEncoder.h"
|
|
#import "ARTPushChannelSubscription.h"
|
|
|
|
@implementation ARTJsonLikeEncoder {
|
|
__weak ARTRestInternal *_rest; // weak because rest owns self
|
|
ARTLog *_logger;
|
|
}
|
|
|
|
- (instancetype)init {
|
|
return [self initWithDelegate:[[ARTJsonEncoder alloc] init]];
|
|
}
|
|
|
|
- (instancetype)initWithDelegate:(id<ARTJsonLikeEncoderDelegate>)delegate {
|
|
if (self = [super init]) {
|
|
_rest = nil;
|
|
_logger = nil;
|
|
_delegate = delegate;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithLogger:(ARTLog *)logger delegate:(id<ARTJsonLikeEncoderDelegate>)delegate {
|
|
if (self = [super init]) {
|
|
_rest = nil;
|
|
_logger = logger;
|
|
_delegate = delegate;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithRest:(ARTRestInternal *)rest delegate:(id<ARTJsonLikeEncoderDelegate>)delegate {
|
|
if (self = [super init]) {
|
|
_rest = rest;
|
|
_logger = rest.logger;
|
|
_delegate = delegate;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (NSString *)mimeType {
|
|
return [_delegate mimeType];
|
|
}
|
|
|
|
- (ARTEncoderFormat)format {
|
|
return [_delegate format];
|
|
}
|
|
|
|
- (NSString *)formatAsString {
|
|
return [_delegate formatAsString];
|
|
}
|
|
|
|
- (ARTMessage *)decodeMessage:(NSData *)data error:(NSError **)error {
|
|
return [self messageFromDictionary:[self decodeDictionary:data error:error]];
|
|
}
|
|
|
|
- (NSArray *)decodeMessages:(NSData *)data error:(NSError **)error {
|
|
return [self messagesFromArray:[self decodeArray:data error:error]];
|
|
}
|
|
|
|
- (NSData *)encodeMessage:(ARTMessage *)message error:(NSError **)error {
|
|
return [self encode:[self messageToDictionary:message] error:error];
|
|
}
|
|
|
|
- (NSData *)encodeMessages:(NSArray *)messages error:(NSError **)error {
|
|
return [self encode:[self messagesToArray:messages] error:error];
|
|
}
|
|
|
|
- (ARTPresenceMessage *)decodePresenceMessage:(NSData *)data error:(NSError **)error {
|
|
return [self presenceMessageFromDictionary:[self decodeDictionary:data error:error]];
|
|
}
|
|
|
|
- (NSArray *)decodePresenceMessages:(NSData *)data error:(NSError **)error {
|
|
return [self presenceMessagesFromArray:[self decodeArray:data error:error]];
|
|
}
|
|
|
|
- (NSData *)encodePresenceMessage:(ARTPresenceMessage *)message error:(NSError **)error {
|
|
return [self encode:[self presenceMessageToDictionary:message] error:error];
|
|
}
|
|
|
|
- (NSData *)encodePresenceMessages:(NSArray *)messages error:(NSError **)error {
|
|
return [self encode:[self presenceMessagesToArray:messages] error:error];
|
|
}
|
|
|
|
- (NSData *)encodeProtocolMessage:(ARTProtocolMessage *)message error:(NSError **)error {
|
|
return [self encode:[self protocolMessageToDictionary:message] error:error];
|
|
}
|
|
|
|
- (ARTProtocolMessage *)decodeProtocolMessage:(NSData *)data error:(NSError **)error {
|
|
return [self protocolMessageFromDictionary:[self decodeDictionary:data error:error]];
|
|
}
|
|
|
|
- (ARTTokenDetails *)decodeTokenDetails:(NSData *)data error:(NSError **)error {
|
|
return [self tokenFromDictionary:[self decodeDictionary:data error:nil] error:error];
|
|
}
|
|
|
|
- (ARTTokenRequest *)decodeTokenRequest:(NSData *)data error:(NSError **)error {
|
|
return [self tokenRequestFromDictionary:[self decodeDictionary:data error:nil] error:error];
|
|
}
|
|
|
|
- (NSData *)encodeTokenRequest:(ARTTokenRequest *)request error:(NSError **)error {
|
|
return [self encode:[self tokenRequestToDictionary:request] error:error];
|
|
}
|
|
|
|
- (NSData *)encodeTokenDetails:(ARTTokenDetails *)tokenDetails error:(NSError **)error {
|
|
return [self encode:[self tokenDetailsToDictionary:tokenDetails] error:error];
|
|
}
|
|
|
|
- (NSData *)encodeDeviceDetails:(ARTDeviceDetails *)deviceDetails error:(NSError **)error {
|
|
return [self encode:[self deviceDetailsToDictionary:deviceDetails] error:error];
|
|
}
|
|
|
|
- (ARTDeviceDetails *)decodeDeviceDetails:(NSData *)data error:(NSError **)error {
|
|
return [self deviceDetailsFromDictionary:[self decodeDictionary:data error:nil] error:error];
|
|
}
|
|
|
|
- (NSArray<ARTDeviceDetails *> *)decodeDevicesDetails:(NSData *)data error:(NSError * __autoreleasing *)error {
|
|
return [self devicesDetailsFromArray:[self decodeArray:data error:nil] error:error];
|
|
}
|
|
|
|
- (NSArray<ARTDeviceDetails *> *)devicesDetailsFromArray:(NSArray *)input error:(NSError * __autoreleasing *)error {
|
|
NSMutableArray<ARTDeviceDetails *> *output = [NSMutableArray array];
|
|
for (NSDictionary *item in input) {
|
|
ARTDeviceDetails *deviceDetails = [self deviceDetailsFromDictionary:item error:error];
|
|
if (!deviceDetails) {
|
|
return nil;
|
|
}
|
|
[output addObject:deviceDetails];
|
|
}
|
|
return output;
|
|
}
|
|
|
|
- (ARTDeviceIdentityTokenDetails *)decodeDeviceIdentityTokenDetails:(NSData *)data error:(NSError * __autoreleasing *)error {
|
|
return [self deviceIdentityTokenDetailsFromDictionary:[self decodeDictionary:data error:nil] error:error];
|
|
}
|
|
|
|
- (NSData *)encodeDevicePushDetails:(ARTDevicePushDetails *)devicePushDetails error:(NSError **)error {
|
|
return [self encode:[self devicePushDetailsToDictionary:devicePushDetails] error:error];
|
|
}
|
|
|
|
- (ARTDevicePushDetails *)decodeDevicePushDetails:(NSData *)data error:(NSError * __autoreleasing *)error {
|
|
return [self devicePushDetailsFromDictionary:[self decode:data error:nil] error:error];
|
|
}
|
|
|
|
- (NSData *)encodePushChannelSubscription:(ARTPushChannelSubscription *)channelSubscription error:(NSError * __autoreleasing *)error {
|
|
return [self encode:[self pushChannelSubscriptionToDictionary:channelSubscription] error:error];
|
|
}
|
|
|
|
- (ARTPushChannelSubscription *)decodePushChannelSubscription:(NSData *)data error:(NSError * __autoreleasing *)error {
|
|
return [self pushChannelSubscriptionFromDictionary:[self decodeDictionary:data error:nil] error:error];
|
|
}
|
|
|
|
- (NSArray<ARTPushChannelSubscription *> *)decodePushChannelSubscriptions:(NSData *)data error:(NSError * __autoreleasing *)error {
|
|
return [self pushChannelSubscriptionsFromArray:[self decodeArray:data error:nil] error:error];
|
|
}
|
|
|
|
- (NSArray<ARTPushChannelSubscription *> *)pushChannelSubscriptionsFromArray:(NSArray *)input error:(NSError * __autoreleasing *)error {
|
|
NSMutableArray<ARTPushChannelSubscription *> *output = [NSMutableArray array];
|
|
for (NSDictionary *item in input) {
|
|
ARTPushChannelSubscription *subscription = [self pushChannelSubscriptionFromDictionary:item error:error];
|
|
if (!subscription) {
|
|
return nil;
|
|
}
|
|
[output addObject:subscription];
|
|
}
|
|
return output;
|
|
}
|
|
|
|
- (NSDictionary *)pushChannelSubscriptionToDictionary:(ARTPushChannelSubscription *)channelSubscription {
|
|
NSMutableDictionary *output = [NSMutableDictionary dictionary];
|
|
|
|
if (channelSubscription.channel) {
|
|
[output setObject:channelSubscription.channel forKey:@"channel"];
|
|
}
|
|
|
|
if (channelSubscription.clientId) {
|
|
[output setObject:channelSubscription.clientId forKey:@"clientId"];
|
|
}
|
|
|
|
if (channelSubscription.deviceId) {
|
|
[output setObject:channelSubscription.deviceId forKey:@"deviceId"];
|
|
}
|
|
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: pushChannelSubscriptionToDictionary %@", _rest, [_delegate formatAsString], output];
|
|
return output;
|
|
}
|
|
|
|
- (ARTPushChannelSubscription *)pushChannelSubscriptionFromDictionary:(NSDictionary *)input error:(NSError * __autoreleasing *)error {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: pushChannelSubscriptionFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
|
|
NSString *clientId = [input artString:@"clientId"];
|
|
NSString *deviceId = [input artString:@"deviceId"];
|
|
|
|
if ((clientId && deviceId) || (!clientId && !deviceId)) {
|
|
[_logger error:@"ARTJsonLikeEncoder<%@>: clientId and deviceId are both present or both nil", [_delegate formatAsString]];
|
|
if (error) {
|
|
*error = [NSError errorWithDomain:ARTAblyErrorDomain
|
|
code:ARTErrorIncompatibleCredentials
|
|
userInfo:@{ NSLocalizedDescriptionKey: @"clientId and deviceId are both present or both nil"}];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
NSString *channelName = [input artString:@"channel"];
|
|
|
|
ARTPushChannelSubscription *channelSubscription;
|
|
if (clientId) {
|
|
channelSubscription = [[ARTPushChannelSubscription alloc] initWithClientId:clientId channel:channelName];
|
|
}
|
|
else {
|
|
channelSubscription = [[ARTPushChannelSubscription alloc] initWithDeviceId:deviceId channel:channelName];
|
|
}
|
|
|
|
return channelSubscription;
|
|
}
|
|
|
|
- (NSDate *)decodeTime:(NSData *)data error:(NSError **)error {
|
|
NSArray *resp = [self decodeArray:data error:error];
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: decodeTime %@", _rest, [_delegate formatAsString], resp];
|
|
if (resp && resp.count == 1) {
|
|
NSNumber *num = resp[0];
|
|
if ([num isKindOfClass:[NSNumber class]]) {
|
|
return [NSDate dateWithTimeIntervalSince1970:millisecondsToTimeInterval(num.doubleValue)];
|
|
}
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (NSArray *)decodeStats:(NSData *)data error:(NSError **)error {
|
|
return [self statsFromArray:[self decodeArray:data error:error]];
|
|
}
|
|
|
|
- (ARTMessage *)messageFromDictionary:(NSDictionary *)input {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: messageFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
|
|
ARTMessage *message = [[ARTMessage alloc] init];
|
|
message.id = [input artString:@"id"];
|
|
message.name = [input artString:@"name"];
|
|
message.clientId = [input artString:@"clientId"];
|
|
message.data = [input objectForKey:@"data"];
|
|
message.encoding = [input artString:@"encoding"];;
|
|
message.timestamp = [input artTimestamp:@"timestamp"];
|
|
message.connectionId = [input artString:@"connectionId"];
|
|
message.extras = [input objectForKey:@"extras"];
|
|
|
|
return message;
|
|
}
|
|
|
|
- (NSArray *)messagesFromArray:(NSArray *)input {
|
|
if (![input isKindOfClass:[NSArray class]]) {
|
|
return nil;
|
|
}
|
|
|
|
NSMutableArray *output = [NSMutableArray array];
|
|
for (NSDictionary *item in input) {
|
|
ARTMessage *message = [self messageFromDictionary:item];
|
|
if (!message) {
|
|
return nil;
|
|
}
|
|
[output addObject:message];
|
|
}
|
|
return output;
|
|
}
|
|
|
|
- (ARTPresenceAction)presenceActionFromInt:(int) action
|
|
{
|
|
switch (action) {
|
|
case 0:
|
|
return ARTPresenceAbsent;
|
|
case 1:
|
|
return ARTPresencePresent;
|
|
case 2:
|
|
return ARTPresenceEnter;
|
|
case 3:
|
|
return ARTPresenceLeave;
|
|
case 4:
|
|
return ARTPresenceUpdate;
|
|
}
|
|
[_logger error:@"RS:%p ARTJsonEncoder invalid ARTPresenceAction %d", _rest, action];
|
|
return ARTPresenceAbsent;
|
|
|
|
}
|
|
|
|
- (int)intFromPresenceMessageAction:(ARTPresenceAction) action
|
|
{
|
|
switch (action) {
|
|
case ARTPresenceAbsent:
|
|
return 0;
|
|
case ARTPresencePresent:
|
|
return 1;
|
|
case ARTPresenceEnter:
|
|
return 2;
|
|
case ARTPresenceLeave:
|
|
return 3;
|
|
case ARTPresenceUpdate:
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
- (ARTPresenceMessage *)presenceMessageFromDictionary:(NSDictionary *)input {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: presenceMessageFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
ARTPresenceMessage *message = [[ARTPresenceMessage alloc] init];
|
|
message.id = [input artString:@"id"];
|
|
message.data = [input objectForKey:@"data"];
|
|
message.encoding = [input artString:@"encoding"];
|
|
message.clientId = [input artString:@"clientId"];
|
|
message.timestamp = [input artTimestamp:@"timestamp"];
|
|
|
|
int action = [[input artNumber:@"action"] intValue];
|
|
|
|
message.action = [self presenceActionFromInt:action];
|
|
|
|
message.connectionId = [input artString:@"connectionId"];
|
|
|
|
return message;
|
|
}
|
|
|
|
- (NSArray *)presenceMessagesFromArray:(NSArray *)input {
|
|
if (![input isKindOfClass:[NSArray class]]) {
|
|
return nil;
|
|
}
|
|
|
|
NSMutableArray *output = [NSMutableArray array];
|
|
for (NSDictionary *item in input) {
|
|
ARTPresenceMessage *message = [self presenceMessageFromDictionary:item];
|
|
if (!message) {
|
|
return nil;
|
|
}
|
|
[output addObject:message];
|
|
}
|
|
return output;
|
|
}
|
|
|
|
- (NSDictionary *)messageToDictionary:(ARTMessage *)message {
|
|
NSMutableDictionary *output = [NSMutableDictionary dictionary];
|
|
if (message.id) {
|
|
[output setObject:message.id forKey:@"id"];
|
|
}
|
|
|
|
if (message.timestamp) {
|
|
[output setObject:[message.timestamp artToNumberMs] forKey:@"timestamp"];
|
|
}
|
|
|
|
if (message.clientId) {
|
|
[output setObject:message.clientId forKey:@"clientId"];
|
|
}
|
|
|
|
if (message.data) {
|
|
[self writeData:message.data encoding:message.encoding toDictionary:output];
|
|
}
|
|
|
|
if (message.name) {
|
|
[output setObject:message.name forKey:@"name"];
|
|
}
|
|
|
|
if (message.extras) {
|
|
[output setObject:message.extras forKey:@"extras"];
|
|
}
|
|
|
|
if (message.connectionId) {
|
|
[output setObject:message.connectionId forKey:@"connectionId"];
|
|
}
|
|
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: messageToDictionary %@", _rest, [_delegate formatAsString], output];
|
|
return output;
|
|
}
|
|
|
|
- (NSDictionary *)authDetailsToDictionary:(ARTAuthDetails *)authDetails {
|
|
NSMutableDictionary *output = [NSMutableDictionary dictionary];
|
|
|
|
[output setObject:authDetails.accessToken forKey:@"accessToken"];
|
|
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: authDetailsToDictionary %@", _rest, [_delegate formatAsString], output];
|
|
return output;
|
|
}
|
|
|
|
- (ARTAuthDetails *)authDetailsFromDictionary:(NSDictionary *)input {
|
|
if (!input) {
|
|
return nil;
|
|
}
|
|
return [[ARTAuthDetails alloc] initWithToken:[input artString:@"accessToken"]];
|
|
}
|
|
|
|
- (NSArray *)messagesToArray:(NSArray *)messages {
|
|
NSMutableArray *output = [NSMutableArray array];
|
|
|
|
for (ARTMessage *message in messages) {
|
|
NSDictionary *item = [self messageToDictionary:message];
|
|
[output addObject:item];
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
- (NSDictionary *)presenceMessageToDictionary:(ARTPresenceMessage *)message {
|
|
NSMutableDictionary *output = [NSMutableDictionary dictionary];
|
|
|
|
if (message.timestamp) {
|
|
[output setObject:[message.timestamp artToNumberMs] forKey:@"timestamp"];
|
|
}
|
|
|
|
if (message.clientId) {
|
|
[output setObject:message.clientId forKey:@"clientId"];
|
|
}
|
|
|
|
if (message.data) {
|
|
[self writeData:message.data encoding:message.encoding toDictionary:output];
|
|
}
|
|
if(message.connectionId) {
|
|
[output setObject:message.connectionId forKey:@"connectionId"];
|
|
}
|
|
|
|
int action = [self intFromPresenceMessageAction:message.action];
|
|
|
|
[output setObject:[NSNumber numberWithInt:action] forKey:@"action"];
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: presenceMessageToDictionary %@", _rest, [_delegate formatAsString], output];
|
|
return output;
|
|
}
|
|
|
|
- (NSArray *)presenceMessagesToArray:(NSArray *)messages {
|
|
NSMutableArray *output = [NSMutableArray array];
|
|
|
|
for (ARTPresenceMessage *message in messages) {
|
|
NSDictionary *item = [self presenceMessageToDictionary:message];
|
|
[output addObject:item];
|
|
}
|
|
return output;
|
|
}
|
|
|
|
- (NSDictionary *)protocolMessageToDictionary:(ARTProtocolMessage *)message {
|
|
NSMutableDictionary *output = [NSMutableDictionary dictionary];
|
|
output[@"action"] = [NSNumber numberWithLongLong:message.action];
|
|
|
|
if (message.channel) {
|
|
output[@"channel"] = message.channel;
|
|
}
|
|
|
|
if (message.channelSerial) {
|
|
output[@"channelSerial"] = message.channelSerial;
|
|
}
|
|
|
|
if (message.msgSerial) {
|
|
output[@"msgSerial"] = message.msgSerial;
|
|
}
|
|
|
|
if (message.messages) {
|
|
output[@"messages"] = [self messagesToArray:message.messages];
|
|
}
|
|
|
|
if (message.presence) {
|
|
output[@"presence"] = [self presenceMessagesToArray:message.presence];
|
|
}
|
|
|
|
if (message.auth) {
|
|
output[@"auth"] = [self authDetailsToDictionary:message.auth];
|
|
}
|
|
|
|
if (message.flags) {
|
|
output[@"flags"] = [NSNumber numberWithLongLong:message.flags];
|
|
}
|
|
|
|
if (message.params) {
|
|
output[@"params"] = message.params;
|
|
}
|
|
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: protocolMessageToDictionary %@", _rest, [_delegate formatAsString], output];
|
|
return output;
|
|
}
|
|
|
|
- (ARTTokenDetails *)tokenFromDictionary:(NSDictionary *)input error:(NSError * __autoreleasing *)error {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: tokenFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
|
|
NSDictionary *jsonError = [input artDictionary:@"error"];
|
|
if (jsonError) {
|
|
[_logger error:@"RS:%p ARTJsonLikeEncoder<%@>: tokenFromDictionary error %@", _rest, [_delegate formatAsString], jsonError];
|
|
if (error) {
|
|
NSMutableDictionary* details = [NSMutableDictionary dictionary];
|
|
[details setValue:[jsonError artString:@"message"] forKey:NSLocalizedDescriptionKey];
|
|
*error = [NSError errorWithDomain:ARTAblyErrorDomain
|
|
code:[jsonError artNumber:@"code"].integerValue
|
|
userInfo:details];
|
|
}
|
|
return nil;
|
|
}
|
|
error = nil;
|
|
|
|
NSString *token = [input artString:@"token"];
|
|
NSNumber *expiresTimeInterval = [input objectForKey:@"expires"];
|
|
NSDate *expires = expiresTimeInterval ? [NSDate dateWithTimeIntervalSince1970:millisecondsToTimeInterval(expiresTimeInterval.doubleValue)] : nil;
|
|
NSNumber *issuedInterval = [input objectForKey:@"issued"];
|
|
NSDate *issued = issuedInterval ? [NSDate dateWithTimeIntervalSince1970:millisecondsToTimeInterval(issuedInterval.doubleValue)] : nil;
|
|
|
|
return [[ARTTokenDetails alloc] initWithToken:token
|
|
expires:expires
|
|
issued:issued
|
|
capability:[input artString:@"capability"]
|
|
clientId:[input artString:@"clientId"]];
|
|
|
|
}
|
|
|
|
- (NSDictionary *)tokenRequestToDictionary:(ARTTokenRequest *)tokenRequest {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: tokenRequestToDictionary %@", _rest, [_delegate formatAsString], tokenRequest];
|
|
|
|
NSNumber *timestamp;
|
|
if (tokenRequest.timestamp)
|
|
timestamp = [NSNumber numberWithUnsignedLongLong:dateToMilliseconds(tokenRequest.timestamp)];
|
|
else
|
|
timestamp = [NSNumber numberWithUnsignedLongLong:dateToMilliseconds([NSDate date])];
|
|
|
|
NSMutableDictionary *dictionary = [@{
|
|
@"keyName":tokenRequest.keyName ? tokenRequest.keyName : @"",
|
|
@"timestamp":timestamp,
|
|
@"nonce":tokenRequest.nonce ? tokenRequest.nonce : @"",
|
|
@"mac":tokenRequest.mac ? tokenRequest.mac : @""
|
|
} mutableCopy];
|
|
|
|
if (tokenRequest.capability) {
|
|
dictionary[@"capability"] = tokenRequest.capability;
|
|
}
|
|
if (tokenRequest.clientId) {
|
|
dictionary[@"clientId"] = tokenRequest.clientId;
|
|
}
|
|
if (tokenRequest.ttl) {
|
|
dictionary[@"ttl"] = [NSNumber numberWithUnsignedLongLong:timeIntervalToMilliseconds(tokenRequest.ttl.doubleValue)];
|
|
}
|
|
|
|
return dictionary;
|
|
}
|
|
|
|
- (ARTTokenRequest *)tokenRequestFromDictionary:(NSDictionary *)input error:(NSError * __autoreleasing *)error {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: tokenRequestFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
|
|
NSDictionary *jsonError = [input artDictionary:@"error"];
|
|
if (jsonError) {
|
|
[_logger error:@"RS:%p ARTJsonLikeEncoder<%@>: tokenRequestFromDictionary error %@", _rest, [_delegate formatAsString], jsonError];
|
|
if (error) {
|
|
NSMutableDictionary* details = [NSMutableDictionary dictionary];
|
|
[details setValue:[jsonError artString:@"message"] forKey:NSLocalizedDescriptionKey];
|
|
*error = [NSError errorWithDomain:ARTAblyErrorDomain
|
|
code:[jsonError artNumber:@"code"].integerValue
|
|
userInfo:details];
|
|
}
|
|
return nil;
|
|
}
|
|
error = nil;
|
|
|
|
ARTTokenParams *params = [[ARTTokenParams alloc] initWithClientId:[input artString:@"clientId"]
|
|
nonce:[input artString:@"nonce"]];
|
|
NSInteger millisecondsTtl = [input artInteger:@"ttl"];
|
|
if (millisecondsTtl) {
|
|
params.ttl = [NSNumber numberWithDouble:millisecondsToTimeInterval(millisecondsTtl)];
|
|
}
|
|
params.capability = [input artString:@"capability"];
|
|
params.timestamp = [input artTimestamp:@"timestamp"];
|
|
|
|
return [[ARTTokenRequest alloc] initWithTokenParams:params
|
|
keyName:[input artString:@"keyName"]
|
|
nonce:[input artString:@"nonce"]
|
|
mac:[input artString:@"mac"]];
|
|
}
|
|
|
|
- (NSDictionary *)tokenDetailsToDictionary:(ARTTokenDetails *)tokenDetails {
|
|
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
|
|
|
dictionary[@"token"] = tokenDetails.token;
|
|
|
|
if (tokenDetails.issued) {
|
|
dictionary[@"issued"] = [NSString stringWithFormat:@"%llu", dateToMilliseconds(tokenDetails.issued)];
|
|
}
|
|
|
|
if (tokenDetails.expires) {
|
|
dictionary[@"expires"] = [NSString stringWithFormat:@"%llu", dateToMilliseconds(tokenDetails.expires)];
|
|
}
|
|
|
|
if (tokenDetails.capability) {
|
|
dictionary[@"capability"] = tokenDetails.capability;
|
|
}
|
|
|
|
if (tokenDetails.clientId) {
|
|
dictionary[@"clientId"] = tokenDetails.clientId;
|
|
}
|
|
|
|
return dictionary;
|
|
}
|
|
|
|
- (NSDictionary *)deviceDetailsToDictionary:(ARTDeviceDetails *)deviceDetails {
|
|
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
|
|
|
dictionary[@"id"] = deviceDetails.id;
|
|
dictionary[@"platform"] = deviceDetails.platform;
|
|
dictionary[@"formFactor"] = deviceDetails.formFactor;
|
|
|
|
if (deviceDetails.clientId) {
|
|
dictionary[@"clientId"] = deviceDetails.clientId;
|
|
}
|
|
|
|
dictionary[@"push"] = [self devicePushDetailsToDictionary:deviceDetails.push];
|
|
|
|
return dictionary;
|
|
}
|
|
|
|
- (ARTDeviceDetails *)deviceDetailsFromDictionary:(NSDictionary *)input error:(NSError * __autoreleasing *)error {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: deviceDetailsFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
|
|
ARTDeviceDetails *deviceDetails = [[ARTDeviceDetails alloc] initWithId:[input artString:@"id"]];
|
|
deviceDetails.clientId = [input artString:@"clientId"];
|
|
deviceDetails.platform = [input artString:@"platform"];
|
|
deviceDetails.formFactor = [input artString:@"formFactor"];
|
|
deviceDetails.metadata = [input valueForKey:@"metadata"];
|
|
deviceDetails.push = [self devicePushDetailsFromDictionary:input[@"push"] error:error];
|
|
|
|
return deviceDetails;
|
|
}
|
|
|
|
- (ARTDeviceIdentityTokenDetails *)deviceIdentityTokenDetailsFromDictionary:(NSDictionary *)input error:(NSError * __autoreleasing *)error {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: deviceIdentityTokenDetailsFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
|
|
NSDictionary *deviceIdentityTokenInput = input[@"deviceIdentityToken"];
|
|
NSString *token = [deviceIdentityTokenInput artString:@"token"];
|
|
NSNumber *issuedMsecs = [deviceIdentityTokenInput artNumber:@"issued"];
|
|
NSDate *issued = [NSDate art_dateWithMillisecondsSince1970:issuedMsecs.doubleValue];
|
|
NSNumber *expiresMsecs = [deviceIdentityTokenInput artNumber:@"expires"];
|
|
NSDate *expires = [NSDate art_dateWithMillisecondsSince1970:expiresMsecs.doubleValue];
|
|
NSString *capability = [deviceIdentityTokenInput artString:@"capability"];
|
|
NSString *clientId = [deviceIdentityTokenInput artString:@"clientId"];
|
|
|
|
ARTDeviceIdentityTokenDetails *deviceIdentityTokenDetails = [[ARTDeviceIdentityTokenDetails alloc] initWithToken:token issued:issued expires:expires capability:capability clientId:clientId];
|
|
|
|
return deviceIdentityTokenDetails;
|
|
}
|
|
|
|
- (NSDictionary *)devicePushDetailsToDictionary:(ARTDevicePushDetails *)devicePushDetails {
|
|
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
|
|
|
dictionary[@"recipient"] = devicePushDetails.recipient;
|
|
|
|
return dictionary;
|
|
}
|
|
|
|
- (ARTDevicePushDetails *)devicePushDetailsFromDictionary:(NSDictionary *)input error:(NSError * __autoreleasing *)error {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: devicePushDetailsFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
|
|
ARTDevicePushDetails *devicePushDetails = [[ARTDevicePushDetails alloc] init];
|
|
devicePushDetails.state = [input artString:@"state"];
|
|
NSDictionary *errorReason = [input valueForKey:@"errorReason"];
|
|
if (errorReason) {
|
|
devicePushDetails.errorReason = [ARTErrorInfo createWithCode:[[errorReason artNumber:@"code"] intValue] status:[[errorReason artNumber:@"statusCode"] intValue] message:[errorReason artString:@"message"]];
|
|
}
|
|
devicePushDetails.recipient = [input valueForKey:@"recipient"];
|
|
|
|
return devicePushDetails;
|
|
}
|
|
|
|
- (ARTProtocolMessage *)protocolMessageFromDictionary:(NSDictionary *)input {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: protocolMessageFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
|
|
ARTProtocolMessage *message = [[ARTProtocolMessage alloc] init];
|
|
message.action = (ARTProtocolMessageAction)[[input artNumber:@"action"] intValue];
|
|
message.count = [[input artNumber:@"count"] intValue];
|
|
message.channel = [input artString:@"channel"];
|
|
message.channelSerial = [input artString:@"channelSerial"];
|
|
message.connectionId = [input artString:@"connectionId"];
|
|
NSNumber * serial = [input artNumber:@"connectionSerial"];
|
|
if (serial) {
|
|
message.connectionSerial = [serial longLongValue];
|
|
}
|
|
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"]];
|
|
message.auth = [self authDetailsFromDictionary:[input valueForKey:@"auth"]];
|
|
message.params = [input valueForKey:@"params"];
|
|
|
|
NSDictionary *error = [input valueForKey:@"error"];
|
|
if (error) {
|
|
message.error = [ARTErrorInfo createWithCode:[[error artNumber:@"code"] intValue] status:[[error artNumber:@"statusCode"] intValue] message:[error artString:@"message"]];
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
- (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"])];
|
|
}
|
|
|
|
- (NSArray *)statsFromArray:(NSArray *)input {
|
|
if (![input isKindOfClass:[NSArray class]]) {
|
|
return nil;
|
|
}
|
|
|
|
NSMutableArray *output = [NSMutableArray array];
|
|
|
|
for (NSDictionary *item in input) {
|
|
if (![item isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
ARTStats *statsItem = [self statsFromDictionary:item];
|
|
if (!statsItem) {
|
|
return nil;
|
|
}
|
|
[output addObject:statsItem];
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
- (ARTStats *)statsFromDictionary:(NSDictionary *)input {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: statsFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
|
|
return [[ARTStats alloc] initWithAll:[self statsMessageTypesFromDictionary:[input objectForKey:@"all"]]
|
|
inbound:[self statsMessageTrafficFromDictionary:[input objectForKey:@"inbound"]]
|
|
outbound:[self statsMessageTrafficFromDictionary:[input objectForKey:@"outbound"]]
|
|
persisted:[self statsMessageTypesFromDictionary:[input objectForKey:@"persisted"]]
|
|
connections:[self statsConnectionTypesFromDictionary:[input objectForKey:@"connections"]]
|
|
channels:[self statsResourceCountFromDictionary:[input objectForKey:@"channels"]]
|
|
apiRequests:[self statsRequestCountFromDictionary:[input objectForKey:@"apiRequests"]]
|
|
tokenRequests:[self statsRequestCountFromDictionary:[input objectForKey:@"tokenRequests"]]
|
|
pushes:[self statsPushCountFromDictionary:[input objectForKey:@"push"]]
|
|
inProgress:[input artString:@"inProgress"]
|
|
count:[[input artNumber:@"count"] unsignedIntegerValue]
|
|
intervalId:[input artString:@"intervalId"]];
|
|
}
|
|
|
|
- (ARTStatsMessageTypes *)statsMessageTypesFromDictionary:(NSDictionary *)input {
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return [ARTStatsMessageTypes empty];
|
|
}
|
|
|
|
ARTStatsMessageCount *all = [self statsMessageCountFromDictionary:[input objectForKey:@"all"]];
|
|
ARTStatsMessageCount *messages = [self statsMessageCountFromDictionary:[input objectForKey:@"messages"]];
|
|
ARTStatsMessageCount *presence = [self statsMessageCountFromDictionary:[input objectForKey:@"presence"]];
|
|
|
|
if (all || messages || presence) {
|
|
return [[ARTStatsMessageTypes alloc] initWithAll:all messages:messages presence:presence];
|
|
}
|
|
|
|
return [ARTStatsMessageTypes empty];
|
|
}
|
|
|
|
- (ARTStatsMessageCount *)statsMessageCountFromDictionary:(NSDictionary *)input {
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return [ARTStatsMessageCount empty];
|
|
}
|
|
|
|
NSNumber *count = [input artTyped:[NSNumber class] key:@"count"];
|
|
NSNumber *data = [input artTyped:[NSNumber class] key:@"data"];
|
|
|
|
return [[ARTStatsMessageCount alloc] initWithCount:count.doubleValue data:data.doubleValue];
|
|
}
|
|
|
|
- (ARTStatsMessageTraffic *)statsMessageTrafficFromDictionary:(NSDictionary *)input {
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return [ARTStatsMessageTraffic empty];
|
|
}
|
|
|
|
ARTStatsMessageTypes *all = [self statsMessageTypesFromDictionary:[input objectForKey:@"all"]];
|
|
ARTStatsMessageTypes *realtime = [self statsMessageTypesFromDictionary:[input objectForKey:@"realtime"]];
|
|
ARTStatsMessageTypes *rest = [self statsMessageTypesFromDictionary:[input objectForKey:@"rest"]];
|
|
ARTStatsMessageTypes *webhook = [self statsMessageTypesFromDictionary:[input objectForKey:@"webhook"]];
|
|
|
|
if (all || realtime || rest || webhook) {
|
|
return [[ARTStatsMessageTraffic alloc] initWithAll:all
|
|
realtime:realtime
|
|
rest:rest
|
|
webhook:webhook];
|
|
}
|
|
|
|
return [ARTStatsMessageTraffic empty];
|
|
}
|
|
|
|
- (ARTStatsConnectionTypes *)statsConnectionTypesFromDictionary:(NSDictionary *)input {
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return [ARTStatsConnectionTypes empty];
|
|
}
|
|
|
|
ARTStatsResourceCount *all = [self statsResourceCountFromDictionary:[input objectForKey:@"all"]];
|
|
ARTStatsResourceCount *plain = [self statsResourceCountFromDictionary:[input objectForKey:@"plain"]];
|
|
ARTStatsResourceCount *tls = [self statsResourceCountFromDictionary:[input objectForKey:@"tls"]];
|
|
|
|
if (all || plain || tls) {
|
|
return [[ARTStatsConnectionTypes alloc] initWithAll:all plain:plain tls:tls];
|
|
}
|
|
|
|
return [ARTStatsConnectionTypes empty];
|
|
}
|
|
|
|
- (ARTStatsResourceCount *)statsResourceCountFromDictionary:(NSDictionary *)input {
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return [ARTStatsResourceCount empty];
|
|
}
|
|
|
|
NSNumber *opened = [input artTyped:[NSNumber class] key:@"opened"];
|
|
NSNumber *peak = [input artTyped:[NSNumber class] key:@"peak"];
|
|
NSNumber *mean = [input artTyped:[NSNumber class] key:@"mean"];
|
|
NSNumber *min = [input artTyped:[NSNumber class] key:@"min"];
|
|
NSNumber *refused = [input artTyped:[NSNumber class] key:@"refused"];
|
|
|
|
return [[ARTStatsResourceCount alloc] initWithOpened:opened.doubleValue
|
|
peak:peak.doubleValue
|
|
mean:mean.doubleValue
|
|
min:min.doubleValue
|
|
refused:refused.doubleValue];
|
|
}
|
|
|
|
- (ARTErrorInfo *)decodeErrorInfo:(NSData *)artError error:(NSError **)error {
|
|
NSDictionary *decodedError = [[self decodeDictionary:artError error:error] valueForKey:@"error"];
|
|
if (!decodedError) {
|
|
return nil;
|
|
}
|
|
return [ARTErrorInfo createWithCode:[decodedError[@"code"] intValue] status:[decodedError[@"statusCode"] intValue] message:decodedError[@"message"]];
|
|
}
|
|
|
|
- (ARTStatsRequestCount *)statsRequestCountFromDictionary:(NSDictionary *)input {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: statsRequestCountFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return [ARTStatsRequestCount empty];
|
|
}
|
|
|
|
NSNumber *succeeded = [input artTyped:[NSNumber class] key:@"succeeded"];
|
|
NSNumber *failed = [input artTyped:[NSNumber class] key:@"failed"];
|
|
NSNumber *refused = [input artTyped:[NSNumber class] key:@"refused"];
|
|
|
|
return [[ARTStatsRequestCount alloc] initWithSucceeded:succeeded.doubleValue
|
|
failed:failed.doubleValue
|
|
refused:refused.doubleValue];
|
|
}
|
|
|
|
- (ARTStatsPushCount *)statsPushCountFromDictionary:(NSDictionary *)input {
|
|
[_logger verbose:@"RS:%p ARTJsonLikeEncoder<%@>: statsPushCountFromDictionary %@", _rest, [_delegate formatAsString], input];
|
|
if (![input isKindOfClass:[NSDictionary class]]) {
|
|
return [ARTStatsPushCount empty];
|
|
}
|
|
|
|
NSNumber *messages = [input artNumber:@"messages"];
|
|
NSNumber *direct = [input artNumber:@"directPublishes"];
|
|
|
|
NSDictionary *notifications = input[@"notifications"];
|
|
NSNumber *succeeded = [notifications artNumber:@"successful"];
|
|
NSNumber *invalid = [notifications artNumber:@"invalid"];
|
|
NSNumber *attempted = [notifications artNumber:@"attempted"];
|
|
NSNumber *failed = [notifications artNumber:@"failed"];
|
|
|
|
return [[ARTStatsPushCount alloc] initWithSucceeded:succeeded.integerValue
|
|
invalid:invalid.integerValue
|
|
attempted:attempted.integerValue
|
|
failed:failed.integerValue
|
|
messages:messages.integerValue
|
|
direct:direct.integerValue];
|
|
}
|
|
|
|
- (void)writeData:(id)data encoding:(NSString *)encoding toDictionary:(NSMutableDictionary *)output {
|
|
if (encoding.length) {
|
|
output[@"encoding"] = encoding;
|
|
}
|
|
output[@"data"] = data;
|
|
}
|
|
|
|
- (NSDictionary *)decodeDictionary:(NSData *)data error:(NSError **)error {
|
|
id obj = [self decode:data error:error];
|
|
if (![obj isKindOfClass:[NSDictionary class]]) {
|
|
return nil;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
- (NSArray *)decodeArray:(NSData *)data error:(NSError **)error {
|
|
id obj = [self decode:data error:error];
|
|
if (![obj isKindOfClass:[NSArray class]]) {
|
|
return nil;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
- (NSArray<NSDictionary *> *)decodeToArray:(NSData *)data error:(NSError **)error {
|
|
id obj = [self decode:data error:error];
|
|
if ([obj isKindOfClass:[NSDictionary class]]) {
|
|
return @[obj];
|
|
}
|
|
if (![obj isKindOfClass:[NSArray class]]) {
|
|
return nil;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
- (id)decode:(NSData *)data error:(NSError **)error {
|
|
NSError *e = nil;
|
|
id decoded = [_delegate decode:data error:&e];
|
|
if (e) {
|
|
[_logger error:@"failed decoding data %@ with error: %@ (%@)", data, e.localizedDescription, e.localizedFailureReason];
|
|
}
|
|
if (error) {
|
|
*error = e;
|
|
}
|
|
[_logger debug:@"RS:%p ARTJsonLikeEncoder<%@> decoding '%@'; got: %@", _rest, [_delegate formatAsString], data, decoded];
|
|
return decoded;
|
|
}
|
|
|
|
- (NSData *)encode:(id)obj error:(NSError **)error {
|
|
NSError *e = nil;
|
|
NSData *encoded = [_delegate encode:obj error:&e];
|
|
if (e) {
|
|
[_logger error:@"failed encoding object %@ with error: %@ (%@)", obj, e.localizedDescription, e.localizedFailureReason];
|
|
}
|
|
if (error) {
|
|
*error = e;
|
|
}
|
|
[_logger debug:@"RS:%p ARTJsonLikeEncoder<%@> encoding '%@'; got: %@", _rest, [_delegate formatAsString], obj, encoded];
|
|
return encoded;
|
|
}
|
|
|
|
@end
|
|
|