diff --git a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Commands/DatabaseGetTableData.h b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Commands/DatabaseGetTableData.h index 2cc3f850b..e8417befd 100644 --- a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Commands/DatabaseGetTableData.h +++ b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Commands/DatabaseGetTableData.h @@ -39,4 +39,7 @@ start:(NSInteger)start count:(NSInteger)count; ++ (DatabaseGetTableDataRequest*)getTableDataRequestFromDictionary: + (NSDictionary*)dictionary; + @end diff --git a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Commands/DatabaseGetTableData.m b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Commands/DatabaseGetTableData.m index 632142a01..d5666614f 100644 --- a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Commands/DatabaseGetTableData.m +++ b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Commands/DatabaseGetTableData.m @@ -47,4 +47,23 @@ return self; } ++ (DatabaseGetTableDataRequest*)getTableDataRequestFromDictionary: + (NSDictionary*)dictionary { + NSInteger databaseId = [dictionary[@"databaseId"] integerValue]; + NSString* table = dictionary[@"table"]; + NSString* order = dictionary[@"order"]; + BOOL reverse = [dictionary[@"reverse"] boolValue]; + NSInteger start = [dictionary[@"start"] integerValue]; + NSInteger count = [dictionary[@"count"] integerValue]; + if (databaseId <= 0 || table.length == 0) { + return nil; + } + return [[DatabaseGetTableDataRequest alloc] initWithDatabaseId:databaseId + table:table + order:order + reverse:reverse + start:start + count:count]; +} + @end diff --git a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/DatabaseDriver.h b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/DatabaseDriver.h index a275162d0..0d227761e 100644 --- a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/DatabaseDriver.h +++ b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/DatabaseDriver.h @@ -8,8 +8,9 @@ #import @protocol DatabaseDescriptor; -@class DatabaseGetTableInfoResponse; @class DatabaseGetTableStructureResponse; +@class DatabaseGetTableInfoResponse; +@class DatabaseGetTableDataResponse; @protocol DatabaseDriver - (NSArray>*)getDatabases; @@ -22,4 +23,13 @@ getTableInfoWithDatabaseDescriptor: (id)databaseDescriptor forTable:(NSString*)tableName; + +- (DatabaseGetTableDataResponse*) + getTableDataWithDatabaseDescriptor: + (id)databaseDescriptor + forTable:(NSString*)tableName + order:(NSString*)order + reverse:(BOOL)reverse + start:(NSInteger)start + count:(NSInteger)count; @end diff --git a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/DatabasesManager.m b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/DatabasesManager.m index 9cfd0f1c6..0fbc0f4ff 100644 --- a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/DatabasesManager.m +++ b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/DatabasesManager.m @@ -14,6 +14,7 @@ #import "DatabaseDescriptorHolder.h" #import "DatabaseDriver.h" #import "DatabaseErrorCodes.h" +#import "DatabaseGetTableData.h" #import "DatabaseGetTableInfo.h" #import "DatabaseGetTableStructure.h" #import "ObjectMapper.h" @@ -85,8 +86,46 @@ [self.connection receive:@"getTableData" - withBlock:^(NSDictionary* params, id responder){ + withBlock:^(NSDictionary* params, id responder) { + DatabaseGetTableDataRequest* request = [DatabaseGetTableDataRequest + getTableDataRequestFromDictionary:params]; + if (!request) { + NSDictionary* errorResponse = [ObjectMapper + errorWithCode:DatabasesErrorCodesInvalidRequest + message:kDatabasesErrorCodesInvalidRequestMessage]; + [responder error:errorResponse]; + return; + } + DatabaseDescriptorHolder* descriptorHolder = + self.databaseDescriptorHolders[@(request.databaseId)]; + if (!descriptorHolder) { + NSDictionary* errorResponse = [ObjectMapper + errorWithCode:DatabasesErrorCodesDatabaseInvalid + message:kDatabasesErrorCodesDatabaseInvalidMessage]; + [responder error:errorResponse]; + return; + } + @try { + DatabaseGetTableDataResponse* tableDataResponse = + [descriptorHolder.databaseDriver + getTableDataWithDatabaseDescriptor:descriptorHolder + .databaseDescriptor + forTable:request.table + order:request.order + reverse:request.reverse + start:request.start + count:request.count]; + NSDictionary* response = [ObjectMapper + databaseGetTableDataResponseToDictionary:tableDataResponse]; + [responder success:response]; + } @catch (NSException* exception) { + NSDictionary* errorResponse = [ObjectMapper + errorWithCode:DatabasesErrorCodesSqlExecutionException + message:[kDatabasesErrorCodesSqlExecutionExceptionMessage + stringByAppendingString:exception.reason]]; + [responder error:errorResponse]; + } }]; [self.connection diff --git a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Mock/MockDatabaseDriver.m b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Mock/MockDatabaseDriver.m index 712409888..0f7563b8e 100644 --- a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Mock/MockDatabaseDriver.m +++ b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/Mock/MockDatabaseDriver.m @@ -6,6 +6,7 @@ */ #import "MockDatabaseDriver.h" +#import "DatabaseGetTableData.h" #import "DatabaseGetTableInfo.h" #import "DatabaseGetTableStructure.h" #import "MockDatabaseDescriptor.h" @@ -55,4 +56,28 @@ initWithDefinition:@"This is mocked table definition"]; } +- (DatabaseGetTableDataResponse*) + getTableDataWithDatabaseDescriptor: + (id)databaseDescriptor + forTable:(NSString*)tableName + order:(NSString*)order + reverse:(BOOL)reverse + start:(NSInteger)start + count:(NSInteger)count { + NSMutableArray* columns = [NSMutableArray array]; + NSMutableArray* values = [NSMutableArray array]; + for (int i = 0; i < 100; i++) { + NSString* columnName = [NSString stringWithFormat:@"column%d", i + 1]; + [columns addObject:columnName]; + NSArray* valueRow = @[ @"value1", @"value2", @"value3" ]; + [values addObject:valueRow]; + } + + return [[DatabaseGetTableDataResponse alloc] initWithColumns:[columns copy] + values:[values copy] + start:0 + count:100 + total:100]; +} + @end diff --git a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/ObjectMapper.m b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/ObjectMapper.m index faf30e24b..16e007f4e 100644 --- a/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/ObjectMapper.m +++ b/iOS/Plugins/FlipperKitDatabasesPlugin/FlipperKitDatabasesPlugin/ObjectMapper.m @@ -15,6 +15,9 @@ @implementation ObjectMapper +static const int MAX_BLOB_LENGTH = 100 * 1024; +static NSString* const UNKNOWN_BLOB_LABEL_FORMAT = @"{%d-byte %@ blob}"; + + (NSMutableArray*)databaseListToFlipperArray: (NSMutableSet*)databaseDescriptorHolderSet { NSMutableArray* result = [NSMutableArray new]; @@ -39,7 +42,22 @@ + (NSDictionary*)databaseGetTableDataResponseToDictionary: (DatabaseGetTableDataResponse*)response { - return @{}; + NSMutableArray* rows = [NSMutableArray array]; + for (NSArray* row in response.values) { + NSMutableArray* rowValues = [NSMutableArray array]; + for (id item in row) { + [rowValues addObject:[self objectAndTypeToFlipperObject:item]]; + } + [rows addObject:rowValues]; + } + + return @{ + @"columns" : response.columns, + @"values" : rows, + @"start" : @(response.start), + @"count" : @(response.count), + @"total" : @(response.total) + }; } + (NSDictionary*)errorWithCode:(NSInteger)code message:(NSString*)message { @@ -68,4 +86,72 @@ return @{}; } ++ (NSDictionary*)objectAndTypeToFlipperObject:(id)object { + if (!object) { + return @{@"type" : @"null"}; + } else if ([object isKindOfClass:[NSNumber class]]) { + NSNumber* number = (NSNumber*)object; + NSString* type = [NSString stringWithCString:[number objCType]]; + + if ([type isEqualToString:@"i"]) { + return @{@"type" : @"integer", @"value" : number}; + } else if ([type isEqualToString:@"f"] || [type isEqualToString:@"d"]) { + return @{@"type" : @"float", @"value" : number}; + } else if ([type isEqualToString:@"B"]) { + return @{@"type" : @"boolean", @"value" : number}; + } else { + return @{@"type" : @"integer", @"value" : @([number integerValue])}; + } + + return @{@"type" : @"integer", @"value" : object}; + } else if ([object isKindOfClass:[NSDecimalNumber class]]) { + return @{@"type" : @"float", @"value" : object}; + } else if ([object isKindOfClass:[NSString class]]) { + return @{@"type" : @"string", @"value" : object}; + } else if ([object isKindOfClass:[NSData class]]) { + NSString* blobString = [self blobToString:(NSData*)object]; + return @{@"type" : @"blob", @"value" : blobString}; + } else if ([object isKindOfClass:[NSValue class]]) { + return @{@"type" : @"boolean", @"value" : object}; + } else { + @throw [NSException exceptionWithName:@"InvalidArgumentException" + reason:@"type of Object is invalid" + userInfo:nil]; + } +} + ++ (NSString*)blobToString:(NSData*)data { + const uint8_t* bytes = data.bytes; + uint length = data.length; + + if (length <= MAX_BLOB_LENGTH) { + if ([self fastIsAscii:bytes length:length]) { + NSStringEncoding encoding = NSASCIIStringEncoding; + return [[NSString alloc] initWithBytesNoCopy:(void*)bytes + length:length + encoding:encoding + freeWhenDone:NO]; + } else { + // try UTF-8 + NSStringEncoding encoding = NSUTF8StringEncoding; + return [[NSString alloc] initWithBytesNoCopy:(void*)bytes + length:length + encoding:encoding + freeWhenDone:NO]; + } + } + return + [NSString stringWithFormat:UNKNOWN_BLOB_LABEL_FORMAT, length, @"binary"]; +} + ++ (BOOL)fastIsAscii:(const uint8_t*)bytes length:(NSUInteger)length { + for (int i = 0; i < length; i++) { + uint8_t b = bytes[i]; + if ((b & ~0x7f) != 0) { + return NO; + } + } + return YES; +} + @end