Skip to content

Commit

Permalink
TE6, TD7: Token{Request, Details}::fromJSON
Browse files Browse the repository at this point in the history
Implements ably/docs#193.

Fixes #529.
  • Loading branch information
tcard committed Nov 14, 2016
1 parent 1570a4e commit c6713bb
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 28 deletions.
31 changes: 7 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,18 +308,11 @@ let clientOptions = ARTClientOptions()
clientOptions.authCallback = { params, callback in
getTokenRequestJSONFromYourServer(params) { json, error in
//handle error
let tokenParams = ARTTokenParams(clientId:json["clientId"])
let tokenRequest = ARTTokenRequest(tokenParams:tokenParams,
keyName:json["keyName"],
nonce:json["nonce"],
mac:json["mac"])
tokenRequest.clientId = json["clientId"]
tokenRequest.ttl = json["ttl"]
tokenRequest.capability = json["capability"]
tokenRequest.timestamp = NSDate(timeIntervalSince1970:(json["timestamp"] / 1000))
callback(tokenRequest, nil)
do {
callback(try ARTTokenRequest.fromJSON(json), nil)
} catch let error as NSError {
callback(nil, error)
}
}
}
Expand All @@ -333,18 +326,8 @@ ARTClientOptions *clientOptions = [[ARTClientOptions alloc] init];
clientOptions.authCallback = ^(ARTTokenParams *params, void(^callback)(id<ARTTokenDetailsCompatible>, NSError*)) {
[self getTokenRequestJSONFromYourServer:params completion:^(NSDictionary *json, NSError *error) {
//handle error
ARTTokenParams *tokenParams = [[ARTTokenParams alloc] initWithClientId:json[@"clientId"]];

ARTTokenRequest *tokenRequest = [[ARTTokenRequest alloc] initWithTokenParams:tokenParams
keyName:json[@"keyName"]
nonce:json[@"nonce"]
mac:json[@"mac"]];
tokenRequest.clientId = json[@"clientId"];
tokenRequest.ttl = [json[@"ttl"] doubleValue];
tokenRequest.capability = json[@"capability"];
tokenRequest.timestamp = [NSDate dateWithTimeIntervalSince1970:[json[@"timestamp"] doubleValue] / 1000];

callback(tokenRequest, nil);
ARTTokenRequest *tokenRequest = [ARTTokenRequest fromJSON:json error:&error];
callback(tokenRequest, error);
}];
};

Expand Down
7 changes: 3 additions & 4 deletions Source/ARTJsonLikeEncoder.m
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,7 @@ - (NSDate *)decodeTime:(NSData *)data {
if (resp && resp.count == 1) {
NSNumber *num = resp[0];
if ([num isKindOfClass:[NSNumber class]]) {
long long msSince1970 = [num longLongValue];
return [NSDate dateWithTimeIntervalSince1970:(msSince1970 / 1000.0)];
return [NSDate dateWithTimeIntervalSince1970:([num doubleValue] / 1000.0)];
}
}
return nil;
Expand Down Expand Up @@ -386,9 +385,9 @@ - (ARTTokenDetails *)tokenFromDictionary:(NSDictionary *)input error:(NSError *

NSString *token = [input artString:@"token"];
NSNumber *expiresTimeInterval = [input objectForKey:@"expires"];
NSDate *expires = expiresTimeInterval ? [NSDate dateWithTimeIntervalSince1970:expiresTimeInterval.longLongValue / 1000] : nil;
NSDate *expires = expiresTimeInterval ? [NSDate dateWithTimeIntervalSince1970:expiresTimeInterval.doubleValue / 1000] : nil;
NSNumber *issuedInterval = [input objectForKey:@"issued"];
NSDate *issued = issuedInterval ? [NSDate dateWithTimeIntervalSince1970:issuedInterval.longLongValue / 1000] : nil;
NSDate *issued = issuedInterval ? [NSDate dateWithTimeIntervalSince1970:issuedInterval.doubleValue / 1000] : nil;

return [[ARTTokenDetails alloc] initWithToken:token
expires:expires
Expand Down
2 changes: 2 additions & 0 deletions Source/ARTTokenDetails.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ ART_ASSUME_NONNULL_BEGIN
- (instancetype)initWithToken:(NSString *)token;
- (instancetype)initWithToken:(NSString *)token expires:(art_nullable NSDate *)expires issued:(art_nullable NSDate *)issued capability:(art_nullable NSString *)capability clientId:(art_nullable NSString *)clientId;

+ (ARTTokenDetails *__art_nullable)fromJSON:(id<ARTJsonCompatible>)json error:(NSError *__art_nullable *__art_nullable)error;

@end

@interface ARTTokenDetails (ARTTokenDetailsCompatible) <ARTTokenDetailsCompatible>
Expand Down
22 changes: 22 additions & 0 deletions Source/ARTTokenDetails.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@ - (id)copyWithZone:(NSZone *)zone {
return tokenDetails;
}

+ (ARTTokenDetails *__art_nullable)fromJSON:(id<ARTJsonCompatible>)json error:(NSError *__art_nullable *__art_nullable)error {
NSError *e;
NSDictionary *dict = [json toJSON:&e];
if (e) {
if (error) {
*error = e;
}
return nil;
}

NSNumber *expiresInterval = [dict objectForKey:@"expires"];
NSDate *expires = expiresInterval ? [NSDate dateWithTimeIntervalSince1970:(expiresInterval.doubleValue) / 1000] : nil;
NSNumber *issuedInterval = [dict objectForKey:@"issued"];
NSDate *issued = issuedInterval ? [NSDate dateWithTimeIntervalSince1970:(issuedInterval.doubleValue) / 1000] : nil;

return [[ARTTokenDetails alloc] initWithToken:dict[@"token"]
expires:expires
issued:issued
capability:dict[@"capability"]
clientId:dict[@"clientId"]];
}

@end

@class ARTAuth;
Expand Down
3 changes: 3 additions & 0 deletions Source/ARTTokenRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

#import <Foundation/Foundation.h>
#import "ARTTypes.h"
#import "ARTTokenParams.h"
#import "ARTAuthOptions.h"

Expand Down Expand Up @@ -56,6 +57,8 @@ ART_ASSUME_NONNULL_BEGIN
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
- (instancetype)initWithTokenParams:(ARTTokenParams *)tokenParams keyName:(NSString *)keyName nonce:(NSString *)nonce mac:(NSString *)mac;

+ (ARTTokenRequest *__art_nullable)fromJSON:(id<ARTJsonCompatible>)json error:(NSError *__art_nullable *__art_nullable)error;

@end

@interface ARTTokenRequest (ARTTokenDetailsCompatible) <ARTTokenDetailsCompatible>
Expand Down
24 changes: 24 additions & 0 deletions Source/ARTTokenRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,30 @@ - (NSString *)description {
self.keyName, self.clientId, self.nonce, self.mac, self.ttl, self.capability, self.timestamp];
}

+ (ARTTokenRequest *__art_nullable)fromJSON:(id<ARTJsonCompatible>)json error:(NSError *__art_nullable *__art_nullable)error {
NSError *e;
NSDictionary *dict = [json toJSON:&e];
if (e) {
if (error) {
*error = e;
}
return nil;
}

ARTTokenParams *tokenParams = [[ARTTokenParams alloc] initWithClientId:dict[@"clientId"]];

ARTTokenRequest *tokenRequest = [[ARTTokenRequest alloc] initWithTokenParams:tokenParams
keyName:dict[@"keyName"]
nonce:dict[@"nonce"]
mac:dict[@"mac"]];
tokenRequest.clientId = dict[@"clientId"];
tokenRequest.ttl = millisecondsToTimeInterval([dict[@"ttl"] doubleValue]);
tokenRequest.capability = dict[@"capability"];
tokenRequest.timestamp = [NSDate dateWithTimeIntervalSince1970:[dict[@"timestamp"] doubleValue] / 1000];

return tokenRequest;
}

@end

@implementation ARTTokenRequest (ARTTokenDetailsCompatible)
Expand Down
11 changes: 11 additions & 0 deletions Source/ARTTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#import <Foundation/Foundation.h>
#import "CompatibilityMacros.h"
#import "ARTStatus.h"

@class ARTStatus;
@class ARTHttpResponse;
Expand Down Expand Up @@ -113,4 +114,14 @@ NSString *generateNonce();

@end

@protocol ARTJsonCompatible <NSObject>
- (NSDictionary *__art_nullable)toJSON:(NSError *__art_nullable *__art_nullable)error;
@end

@interface NSString (ARTJsonCompatible) <ARTJsonCompatible>
@end

@interface NSDictionary (ARTJsonCompatible) <ARTJsonCompatible>
@end

ART_ASSUME_NONNULL_END
34 changes: 34 additions & 0 deletions Source/ARTTypes.m
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,37 @@ - (void)setRetryIn:(NSTimeInterval)retryIn {
}

@end

@implementation NSString (ARTJsonCompatible)

- (NSDictionary *)toJSON:(NSError *__art_nullable *__art_nullable)error {
NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
NSError *jsonError;
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError) {
if (error) {
*error = jsonError;
}
return nil;
}
if (![json isKindOfClass:[NSDictionary class]]) {
if (error) {
*error = [NSError errorWithDomain:ARTAblyErrorDomain code:0 userInfo:@{NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"expected JSON object, got %@", [json class]]}];
}
return nil;
}
return (NSDictionary *)json;
}

@end

@implementation NSDictionary (ARTJsonCompatible)

- (NSDictionary *)toJSON:(NSError *__art_nullable *__art_nullable)error {
if (error) {
*error = nil;
}
return self;
}

@end
82 changes: 82 additions & 0 deletions Spec/Auth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2749,5 +2749,87 @@ class Auth : QuickSpec {
}
}
}

describe("TokenRequest") {
// TE6
describe("fromJSON") {
let json = "{" +
" \"clientId\":\"myClientId\"," +
" \"mac\":\"4rr4J+JzjiCL1DoS8wq7k11Z4oTGCb1PoeN+yGjkaH4=\"," +
" \"capability\":\"{\\\"test\\\":[\\\"publish\\\"]}\"," +
" \"ttl\":42000," +
" \"timestamp\":1479087321934," +
" \"keyName\":\"xxxxxx.yyyyyy\"," +
" \"nonce\":\"7830658976108826\"" +
"}"

func check(request: ARTTokenRequest) {
expect(request.clientId).to(equal("myClientId"))
expect(request.mac).to(equal("4rr4J+JzjiCL1DoS8wq7k11Z4oTGCb1PoeN+yGjkaH4="))
expect(request.capability).to(equal("{\"test\":[\"publish\"]}"))
expect(request.ttl).to(equal(NSTimeInterval(42)))
expect(request.timestamp).to(equal(NSDate(timeIntervalSince1970: 1479087321.934)))
expect(request.keyName).to(equal("xxxxxx.yyyyyy"))
expect(request.nonce).to(equal("7830658976108826"))
}

it("accepts a string, which should be interpreted as JSON") {
check(try! ARTTokenRequest.fromJSON(json))
}

it("accepts a NSDictionary") {
let data = json.dataUsingEncoding(NSUTF8StringEncoding)!
let dict = try! NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves) as! NSDictionary
check(try! ARTTokenRequest.fromJSON(dict))
}

it("rejects invalid JSON") {
expect{try ARTTokenRequest.fromJSON("not JSON")}.to(throwError())
}

it("rejects non-object JSON") {
expect{try ARTTokenRequest.fromJSON("[]")}.to(throwError())
}
}
}

describe("TokenDetails") {
// TD7
describe("fromJSON") {
let json = "{" +
" \"token\": \"xxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\"," +
" \"issued\": 1479087321934," +
" \"expires\": 1479087363934," +
" \"capability\": \"{\\\"test\\\":[\\\"publish\\\"]}\"," +
" \"clientId\": \"myClientId\"" +
"}"

func check(details: ARTTokenDetails) {
expect(details.token).to(equal("xxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"))
expect(details.issued).to(equal(NSDate(timeIntervalSince1970: 1479087321.934)))
expect(details.expires).to(equal(NSDate(timeIntervalSince1970: 1479087363.934)))
expect(details.capability).to(equal("{\"test\":[\"publish\"]}"))
expect(details.clientId).to(equal("myClientId"))
}

it("accepts a string, which should be interpreted as JSON") {
check(try! ARTTokenDetails.fromJSON(json))
}

it("accepts a NSDictionary") {
let data = json.dataUsingEncoding(NSUTF8StringEncoding)!
let dict = try! NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves) as! NSDictionary
check(try! ARTTokenDetails.fromJSON(dict))
}

it("rejects invalid JSON") {
expect{try ARTTokenDetails.fromJSON("not JSON")}.to(throwError())
}

it("rejects non-object JSON") {
expect{try ARTTokenDetails.fromJSON("[]")}.to(throwError())
}
}
}
}
}

0 comments on commit c6713bb

Please sign in to comment.