FBDiagnostic conforms to FBJSONDeserializable

Summary: This will allow wiring of diagnostics to a remote machine. By using the read-into-memory method on the builder it makes it possible to distinguish between json encodings of a local file, or a payload that can be transmitted to a remote machine.

Reviewed By: marekcirkos

Differential Revision: D3047137

fb-gh-sync-id: f4b8e559291b88dd266a8157c0fce4039f4d8d6e
shipit-source-id: f4b8e559291b88dd266a8157c0fce4039f4d8d6e
This commit is contained in:
Lawrence Lomax 2016-03-14 07:52:07 -07:00 коммит произвёл Facebook Github Bot 9
Родитель 927669d664
Коммит 5f126df855
3 изменённых файлов: 148 добавлений и 19 удалений

Просмотреть файл

@ -16,7 +16,7 @@
Defines the content & metadata of a log.
Lazily converts between the backing store data formats.
*/
@interface FBDiagnostic : NSObject <NSCopying, NSCoding, FBJSONSerializable, FBDebugDescribeable>
@interface FBDiagnostic : NSObject <NSCopying, NSCoding, FBJSONSerializable, FBJSONDeserializable, FBDebugDescribeable>
/**
The name of the Log for uniquely identifying the log.
@ -182,14 +182,13 @@
- (instancetype)updatePath:(NSString *)path;
/**
Updates the underlying `FBDiagnostic` with JSON Encoded String.
Updates the underlying `FBDiagnostic` with the provided Native JSON Object.
Will replace any data, string or path associated with the log.
@param jsonSerializable Can be either an FBJSONSerializable
or an object that meets the requirements of NSJSONSerialization.
@param json Can be either an FBJSONSerializable or an object that meets the requirements of NSJSONSerialization.
@return the reciever, for chaining.
*/
- (instancetype)updateJSONSerializable:(id)jsonSerializable;
- (instancetype)updateJSON:(id)json;
/**
Returns a File Path suitable for writing data into.
@ -217,6 +216,13 @@
Updates the underlying `FBDiagnostic` by reading it into memory, if it is backed by a file.
This means that the created FBDiagnostic is effectively immutable since the content cannot change.
File-backed Diagnostics are beneficial as they allow for the work of reading, transforming or writing a diagnostic
to be deferred to a later date. Once a Diagnostic is read into memory the backing file can change and will not
affect the diagnostic object.
If you wish to transfer a diagnostic to a remote machine, reading it into memory will also mean that it can be encoded
into a JSON representation and reconstructed as a file on the remote machine.
@return the reciever, for chaining.
*/
- (instancetype)readIntoMemory;

Просмотреть файл

@ -11,6 +11,9 @@
#import <objc/runtime.h>
#import "FBCollectionInformation.h"
#import "FBControlCoreError.h"
@interface FBDiagnostic ()
@property (nonatomic, copy, readwrite) NSString *shortName;
@ -31,6 +34,8 @@
*/
@interface FBDiagnostic_Data : FBDiagnostic
+ (NSData *)dataFromJSON:(NSDictionary *)json error:(NSError **)error;
@end
/**
@ -38,6 +43,8 @@
*/
@interface FBDiagnostic_String : FBDiagnostic
+ (NSString *)stringFromJSON:(NSDictionary *)json error:(NSError **)error;
@end
/**
@ -45,6 +52,8 @@
*/
@interface FBDiagnostic_Path : FBDiagnostic
+ (NSString *)pathFromJSON:(NSDictionary *)json error:(NSError **)error;
@end
/**
@ -52,6 +61,8 @@
*/
@interface FBDiagnostic_JSON : FBDiagnostic
+ (id)objectFromJSON:(NSDictionary *)json error:(NSError **)error;
@end
/**
@ -172,7 +183,42 @@
return [storageDirectory stringByAppendingPathComponent:filename];
}
#pragma mark FBJSONSerializable
#pragma mark JSON
+ (instancetype)inflateFromJSON:(NSDictionary *)json error:(NSError **)error
{
if (![FBCollectionInformation isArrayHeterogeneous:json.allKeys withClass:NSString.class]) {
return [[FBControlCoreError describeFormat:@"%@ does not have string keys", json] fail:error];
}
NSString *shortName = json[@"short_name"];
if (!shortName) {
return [[FBControlCoreError describeFormat:@"%@ should exist for for 'short_name'", shortName] fail:error];
}
NSString *humanReadableName = json[@"human_name"];
NSString *fileType = json[@"file_type"];
FBDiagnosticBuilder *builder = [[[[FBDiagnosticBuilder builder]
updateShortName:shortName]
updateHumanReadableName:humanReadableName]
updateFileType:fileType];
NSData *data = [FBDiagnostic_Data dataFromJSON:json error:nil];
if (data) {
return [[builder updateData:data] build];
}
NSString *string = [FBDiagnostic_String stringFromJSON:json error:nil];
if (string) {
return [[builder updateString:string] build];
}
NSString *path = [FBDiagnostic_Path pathFromJSON:json error:nil];
if (path) {
return [[builder updatePath:path] build];
}
id object = [FBDiagnostic_JSON objectFromJSON:json error:nil];
if (object) {
return [[builder updateJSON:object] build];
}
return [builder build];
}
- (NSDictionary *)jsonSerializableRepresentation
{
@ -311,7 +357,20 @@
return [self.backingData writeToFile:path options:0 error:error];
}
#pragma mark FBJSONSerializable
#pragma mark JSON
+ (NSData *)dataFromJSON:(NSDictionary *)json error:(NSError **)error
{
NSString *base64String = json[@"data"];
if (![base64String isKindOfClass:NSString.class]) {
return [[FBControlCoreError describeFormat:@"%@ is not a string for 'data'", base64String] fail:error];
}
NSData *data = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
if (!data) {
return [[FBControlCoreError describe:@"base64 encoded string could not be decoded"] fail:error];
}
return data;
}
- (NSDictionary *)jsonSerializableRepresentation
{
@ -441,7 +500,16 @@
return [self.backingString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:error];
}
#pragma mark FBJSONSerializable
#pragma mark JSON
+ (NSString *)stringFromJSON:(NSDictionary *)json error:(NSError **)error
{
NSString *string = json[@"contents"];
if (![string isKindOfClass:NSString.class]) {
return [[FBControlCoreError describeFormat:@"%@ is not a string for 'contents'", string] fail:error];
}
return string;
}
- (NSDictionary *)jsonSerializableRepresentation
{
@ -553,7 +621,16 @@
return [NSFileManager.defaultManager copyItemAtPath:self.backingFilePath toPath:path error:error];
}
#pragma mark FBJSONSerializable
#pragma mark JSON
+ (NSString *)pathFromJSON:(NSDictionary *)json error:(NSError **)error
{
NSString *path = json[@"location"];
if (![path isKindOfClass:NSString.class]) {
return [[FBControlCoreError describeFormat:@"%@ is not a string for 'path'", path] fail:error];
}
return path;
}
- (NSDictionary *)jsonSerializableRepresentation
{
@ -683,7 +760,16 @@
return bytesWritten > 0;
}
#pragma mark FBJSONSerializable
#pragma mark JSON
+ (id)objectFromJSON:(NSDictionary *)json error:(NSError **)error
{
id object = json[@"object"];
if (!object) {
return [[FBControlCoreError describe:@"'object' does not exist"] fail:error];
}
return object;
}
- (NSDictionary *)jsonSerializableRepresentation
{
@ -758,16 +844,37 @@
@implementation FBDiagnosticBuilder : NSObject
#pragma mark Initializers
+ (instancetype)builder
{
return [self builderWithDiagnostic:nil];
return [[FBDiagnosticBuilder alloc] initWithDiagnostic:nil];
}
+ (instancetype)builderWithDiagnostic:(FBDiagnostic *)diagnostic
{
return [[FBDiagnosticBuilder new] updateDiagnostic:[diagnostic copy] ?: [FBDiagnostic_Empty new]];
return [[FBDiagnosticBuilder alloc] initWithDiagnostic:diagnostic];
}
- (instancetype)init
{
return [self initWithDiagnostic:nil];
}
- (instancetype)initWithDiagnostic:(FBDiagnostic *)diagnostic
{
self = [super init];
if (!self) {
return nil;
}
_diagnostic = [diagnostic copy] ?: FBDiagnostic_Empty.new;
return self;
}
#pragma mark Updates
- (instancetype)updateDiagnostic:(FBDiagnostic *)diagnostic
{
if (!diagnostic) {
@ -851,11 +958,9 @@
return self;
}
- (instancetype)updateJSONSerializable:(id)jsonSerializable
- (instancetype)updateJSON:(id)json
{
id json = [jsonSerializable conformsToProtocol:@protocol(FBJSONSerializable)]
? [jsonSerializable jsonSerializableRepresentation]
: jsonSerializable;
json = [json conformsToProtocol:@protocol(FBJSONSerializable)] ? [json jsonSerializableRepresentation] : json;
if (!json) {
return self;
}

Просмотреть файл

@ -29,8 +29,9 @@
{
NSArray *values = @[self.photoDiagnostic, self.simulatorSystemLog, self.treeJSONDiagnostic];
[self assertEqualityOfCopy:values];
[self assertJSONSerialization:values];
[self assertUnarchiving:values];
[self assertJSONSerialization:values];
[self assertJSONDeserialization:values];
}
- (void)assertWritesOutToFile:(FBDiagnostic *)diagnostic
@ -224,7 +225,7 @@
};
FBDiagnostic *diagnostic = [[[[FBDiagnosticBuilder builder]
updateShortName:@"somelog"]
updateJSONSerializable:json]
updateJSON:json]
build];
XCTAssertNotNil(diagnostic.asPath);
@ -278,7 +279,7 @@
{
FBDiagnostic *diagnostic = [[[[FBDiagnosticBuilder builder]
updateShortName:@"process_info"]
updateJSONSerializable:self.launchCtlProcess]
updateJSON:self.launchCtlProcess]
build];
XCTAssertNotNil(diagnostic.asPath);
@ -290,4 +291,21 @@
[self assertWritesOutToFile:diagnostic];
}
- (void)testJSONFileWiring
{
FBDiagnostic *localFile = self.treeJSONDiagnostic;
FBDiagnostic *wireDiagnostic = [[[FBDiagnosticBuilder
builderWithDiagnostic:localFile]
readIntoMemory]
build];
id json = [wireDiagnostic jsonSerializableRepresentation];
NSError *error = nil;
FBDiagnostic *remoteDiagnostic = [FBDiagnostic inflateFromJSON:json error:&error];
XCTAssertNil(error);
XCTAssertNotNil(remoteDiagnostic);
XCTAssertEqualObjects(localFile.shortName , remoteDiagnostic.shortName);
XCTAssertEqualObjects(localFile.asString, remoteDiagnostic.asString);
}
@end