Ben Copsey

Added ability to put to cloud files from a local file

Authenticate method now returns an NSError if something goes wrong
Change lock mediating access to credentials to recursive
@@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
21 #import "ASIInputStream.h" 21 #import "ASIInputStream.h"
22 22
23 // Automatically set on build 23 // Automatically set on build
24 -NSString *ASIHTTPRequestVersion = @"v1.5-26 2010-01-20"; 24 +NSString *ASIHTTPRequestVersion = @"v1.5-32 2010-01-20";
25 25
26 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; 26 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
27 27
@@ -58,6 +58,7 @@ @@ -58,6 +58,7 @@
58 // The Object can be created with custom metadata via HTTP headers identified with the “X-Object-Meta-” prefix. 58 // The Object can be created with custom metadata via HTTP headers identified with the “X-Object-Meta-” prefix.
59 + (id)putObjectRequestWithContainer:(NSString *)containerName object:(ASICloudFilesObject *)object; 59 + (id)putObjectRequestWithContainer:(NSString *)containerName object:(ASICloudFilesObject *)object;
60 + (id)putObjectRequestWithContainer:(NSString *)containerName objectPath:(NSString *)objectPath contentType:(NSString *)contentType objectData:(NSData *)objectData metadata:(NSDictionary *)metadata etag:(NSString *)etag; 60 + (id)putObjectRequestWithContainer:(NSString *)containerName objectPath:(NSString *)objectPath contentType:(NSString *)contentType objectData:(NSData *)objectData metadata:(NSDictionary *)metadata etag:(NSString *)etag;
  61 ++ (id)putObjectRequestWithContainer:(NSString *)containerName objectPath:(NSString *)objectPath contentType:(NSString *)contentType file:(NSString *)filePath metadata:(NSDictionary *)metadata etag:(NSString *)etag;
61 62
62 // POST /<api version>/<account>/<container>/<object> 63 // POST /<api version>/<account>/<container>/<object>
63 // POST operations against an Object name are used to set and overwrite arbitrary key/value metadata. You cannot use the POST operation to change any of the Object's other headers such as Content-Type, ETag, etc. It is not used to upload storage Objects (see PUT). 64 // POST operations against an Object name are used to set and overwrite arbitrary key/value metadata. You cannot use the POST operation to change any of the Object's other headers such as Content-Type, ETag, etc. It is not used to upload storage Objects (see PUT).
@@ -155,7 +155,6 @@ @@ -155,7 +155,6 @@
155 155
156 ASICloudFilesObjectRequest *request = [ASICloudFilesObjectRequest storageRequestWithMethod:@"PUT" containerName:containerName objectPath:objectPath]; 156 ASICloudFilesObjectRequest *request = [ASICloudFilesObjectRequest storageRequestWithMethod:@"PUT" containerName:containerName objectPath:objectPath];
157 [request addRequestHeader:@"Content-Type" value:contentType]; 157 [request addRequestHeader:@"Content-Type" value:contentType];
158 - [request addRequestHeader:@"Content-Length" value:[NSString stringWithFormat:@"%i", objectData.length]];  
159 158
160 // add metadata to headers 159 // add metadata to headers
161 if (metadata) { 160 if (metadata) {
@@ -168,6 +167,23 @@ @@ -168,6 +167,23 @@
168 return request; 167 return request;
169 } 168 }
170 169
  170 ++ (id)putObjectRequestWithContainer:(NSString *)containerName objectPath:(NSString *)objectPath contentType:(NSString *)contentType file:(NSString *)filePath metadata:(NSDictionary *)metadata etag:(NSString *)etag
  171 +{
  172 + ASICloudFilesObjectRequest *request = [ASICloudFilesObjectRequest storageRequestWithMethod:@"PUT" containerName:containerName objectPath:objectPath];
  173 + [request addRequestHeader:@"Content-Type" value:contentType];
  174 +
  175 + // add metadata to headers
  176 + if (metadata) {
  177 + for (NSString *key in [metadata keyEnumerator]) {
  178 + [request addRequestHeader:[NSString stringWithFormat:@"X-Object-Meta-%@", key] value:[metadata objectForKey:key]];
  179 + }
  180 + }
  181 +
  182 + [request setShouldStreamPostDataFromDisk:YES];
  183 + [request setPostBodyFilePath:filePath];
  184 + return request;
  185 +}
  186 +
171 #pragma mark - 187 #pragma mark -
172 #pragma mark POST - Set Object Metadata 188 #pragma mark POST - Set Object Metadata
173 189
@@ -27,7 +27,7 @@ @@ -27,7 +27,7 @@
27 #pragma mark Rackspace Cloud Authentication 27 #pragma mark Rackspace Cloud Authentication
28 28
29 + (id)authenticationRequest; 29 + (id)authenticationRequest;
30 -+ (void)authenticate; 30 ++ (NSError *)authenticate;
31 + (NSString *)username; 31 + (NSString *)username;
32 + (void)setUsername:(NSString *)username; 32 + (void)setUsername:(NSString *)username;
33 + (NSString *)apiKey; 33 + (NSString *)apiKey;
@@ -20,14 +20,14 @@ static NSString *storageURL = nil; @@ -20,14 +20,14 @@ static NSString *storageURL = nil;
20 static NSString *cdnManagementURL = nil; 20 static NSString *cdnManagementURL = nil;
21 static NSString *rackspaceCloudAuthURL = @"https://auth.api.rackspacecloud.com/v1.0"; 21 static NSString *rackspaceCloudAuthURL = @"https://auth.api.rackspacecloud.com/v1.0";
22 22
23 -static NSLock *accessDetailsLock = nil; 23 +static NSRecursiveLock *accessDetailsLock = nil;
24 24
25 @implementation ASICloudFilesRequest 25 @implementation ASICloudFilesRequest
26 26
27 + (void)initialize 27 + (void)initialize
28 { 28 {
29 if (self == [ASICloudFilesRequest class]) { 29 if (self == [ASICloudFilesRequest class]) {
30 - accessDetailsLock = [[NSLock alloc] init]; 30 + accessDetailsLock = [[NSRecursiveLock alloc] init];
31 } 31 }
32 } 32 }
33 33
@@ -59,7 +59,7 @@ static NSLock *accessDetailsLock = nil; @@ -59,7 +59,7 @@ static NSLock *accessDetailsLock = nil;
59 return request; 59 return request;
60 } 60 }
61 61
62 -+ (void)authenticate 62 ++ (NSError *)authenticate
63 { 63 {
64 [accessDetailsLock lock]; 64 [accessDetailsLock lock];
65 ASIHTTPRequest *request = [ASICloudFilesRequest authenticationRequest]; 65 ASIHTTPRequest *request = [ASICloudFilesRequest authenticationRequest];
@@ -72,6 +72,7 @@ static NSLock *accessDetailsLock = nil; @@ -72,6 +72,7 @@ static NSLock *accessDetailsLock = nil;
72 cdnManagementURL = [responseHeaders objectForKey:@"X-Cdn-Management-Url"]; 72 cdnManagementURL = [responseHeaders objectForKey:@"X-Cdn-Management-Url"];
73 } 73 }
74 [accessDetailsLock unlock]; 74 [accessDetailsLock unlock];
  75 + return [request error];
75 } 76 }
76 77
77 + (NSString *)username 78 + (NSString *)username
@@ -211,12 +211,40 @@ static NSString *apiKey = @""; @@ -211,12 +211,40 @@ static NSString *apiKey = @"";
211 GHAssertEqualStrings(object.name, @"puttestfile.txt", @"Failed to parse object name", @"Failed to parse object name"); 211 GHAssertEqualStrings(object.name, @"puttestfile.txt", @"Failed to parse object name", @"Failed to parse object name");
212 GHAssertNotNil(object.data, @"Failed to parse object data"); 212 GHAssertNotNil(object.data, @"Failed to parse object data");
213 GHAssertEqualStrings(string, @"this is a test", @"Failed to parse object data", @"Failed to parse object data"); 213 GHAssertEqualStrings(string, @"this is a test", @"Failed to parse object data", @"Failed to parse object data");
214 - 214 +
215 - [string release];  
216 215
217 ASICloudFilesContainerRequest *deleteContainerRequest = [ASICloudFilesContainerRequest deleteContainerRequest:@"ASICloudFilesTest"]; 216 ASICloudFilesContainerRequest *deleteContainerRequest = [ASICloudFilesContainerRequest deleteContainerRequest:@"ASICloudFilesTest"];
218 [deleteContainerRequest startSynchronous]; 217 [deleteContainerRequest startSynchronous];
219 218
  219 + // Now put the object from a file
  220 +
  221 + createContainerRequest = [ASICloudFilesContainerRequest createContainerRequest:@"ASICloudFilesTest"];
  222 + [createContainerRequest startSynchronous];
  223 +
  224 + NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"cloudfile"];
  225 + [data writeToFile:filePath atomically:NO];
  226 +
  227 + putRequest = [ASICloudFilesObjectRequest putObjectRequestWithContainer:@"ASICloudFilesTest" objectPath:@"puttestfile.txt" contentType:@"text/plain" file:filePath metadata:nil etag:nil];
  228 +
  229 + [putRequest startSynchronous];
  230 +
  231 + GHAssertNil([putRequest error], @"Failed to PUT object");
  232 +
  233 + getRequest = [ASICloudFilesObjectRequest getObjectRequestWithContainer:@"ASICloudFilesTest" objectPath:@"puttestfile.txt"];
  234 + [getRequest startSynchronous];
  235 +
  236 + object = [getRequest object];
  237 +
  238 + GHAssertNotNil(object, @"Failed to retrieve new object");
  239 + GHAssertNotNil(object.name, @"Failed to parse object name");
  240 + GHAssertEqualStrings(object.name, @"puttestfile.txt", @"Failed to parse object name", @"Failed to parse object name");
  241 + GHAssertNotNil(object.data, @"Failed to parse object data");
  242 + GHAssertEqualStrings(string, @"this is a test", @"Failed to parse object data", @"Failed to parse object data");
  243 +
  244 + [string release];
  245 +
  246 + deleteContainerRequest = [ASICloudFilesContainerRequest deleteContainerRequest:@"ASICloudFilesTest"];
  247 + [deleteContainerRequest startSynchronous];
220 } 248 }
221 249
222 - (void)testPostObject { 250 - (void)testPostObject {