Skip to content
Browse files

More replicator improvements

* Created a TD_DatabaseManager method to create replicators

Conflicts:
	Source/TDReplicatorManager.m
	Source/TD_DatabaseManager.m
  • Loading branch information...
1 parent 84b7003 commit 6fbb4f2974b0379988cc5af6cdcbeefcd6faa081 @snej snej committed Jan 11, 2013
View
9 Source/TDReplicatorManager.h
@@ -31,13 +31,4 @@ extern NSString* const kTDReplicatorDatabaseName;
- (void) start;
- (void) stop;
-/** Examines the JSON object describing a replication and determines the local database and remote URL, and some of the other parameters. */
-- (TDStatus) parseReplicatorProperties: (NSDictionary*)body
- toDatabase: (TD_Database**)outDatabase
- remote: (NSURL**)outRemote
- isPush: (BOOL*)outIsPush
- createTarget: (BOOL*)outCreateTarget
- headers: (NSDictionary**)outHeaders
- authorizer: (id<TDAuthorizer>*)outAuthorizer;
-
@end
View
137 Source/TDReplicatorManager.m
@@ -24,8 +24,6 @@
#import "TDPusher.h"
#import "TDPuller.h"
#import "TD_View.h"
-#import "TDOAuth1Authorizer.h"
-#import "TDBrowserIDAuthorizer.h"
#import "TDInternal.h"
#import "TDMisc.h"
#import "MYBlockUtils.h"
@@ -102,102 +100,6 @@ - (NSString*) docIDForReplicator: (TDReplicator*)repl {
}
-// Replication 'source' or 'target' property may be a string or a dictionary. Normalize to dict form
-static NSDictionary* parseSourceOrTarget(NSDictionary* properties, NSString* key) {
- id value = properties[key];
- if ([value isKindOfClass: [NSDictionary class]])
- return value;
- else if ([value isKindOfClass: [NSString class]])
- return $dict({@"url", value});
- else
- return nil;
-}
-
-
-- (TDStatus) parseReplicatorProperties: (NSDictionary*)properties
- toDatabase: (TD_Database**)outDatabase // may be NULL
- remote: (NSURL**)outRemote // may be NULL
- isPush: (BOOL*)outIsPush
- createTarget: (BOOL*)outCreateTarget
- headers: (NSDictionary**)outHeaders
- authorizer: (id<TDAuthorizer>*)outAuthorizer
-{
- // http://wiki.apache.org/couchdb/Replication
- NSDictionary* sourceDict = parseSourceOrTarget(properties, @"source");
- NSDictionary* targetDict = parseSourceOrTarget(properties, @"target");
- NSString* source = sourceDict[@"url"];
- NSString* target = targetDict[@"url"];
- if (!source || !target)
- return kTDStatusBadRequest;
-
- *outCreateTarget = [$castIf(NSNumber, properties[@"create_target"]) boolValue];
- *outIsPush = NO;
- TD_Database* db = nil;
- NSDictionary* remoteDict = nil;
- if ([TD_DatabaseManager isValidDatabaseName: source]) {
- if (outDatabase)
- db = [_dbManager existingDatabaseNamed: source];
- remoteDict = targetDict;
- *outIsPush = YES;
- } else {
- if (![TD_DatabaseManager isValidDatabaseName: target])
- return kTDStatusBadID;
- remoteDict = sourceDict;
- if (outDatabase) {
- if (*outCreateTarget) {
- db = [_dbManager databaseNamed: target];
- if (![db open])
- return kTDStatusDBError;
- } else {
- db = [_dbManager existingDatabaseNamed: target];
- }
- }
- }
- NSURL* remote = [NSURL URLWithString: remoteDict[@"url"]];
- if (![@[@"http", @"https", @"touchdb"] containsObject: remote.scheme.lowercaseString])
- return kTDStatusBadRequest;
- if (outDatabase) {
- *outDatabase = db;
- if (!db)
- return kTDStatusNotFound;
- }
- if (outRemote)
- *outRemote = remote;
- if (outHeaders)
- *outHeaders = $castIf(NSDictionary, remoteDict[@"headers"]);
-
- if (outAuthorizer) {
- *outAuthorizer = nil;
- NSDictionary* auth = $castIf(NSDictionary, remoteDict[@"auth"]);
- if (auth) {
- NSDictionary* oauth = $castIf(NSDictionary, auth[@"oauth"]);
- if (oauth) {
- NSString* consumerKey = $castIf(NSString, oauth[@"consumer_key"]);
- NSString* consumerSec = $castIf(NSString, oauth[@"consumer_secret"]);
- NSString* token = $castIf(NSString, oauth[@"token"]);
- NSString* tokenSec = $castIf(NSString, oauth[@"token_secret"]);
- NSString* sigMethod = $castIf(NSString, oauth[@"signature_method"]);
- *outAuthorizer = [[TDOAuth1Authorizer alloc] initWithConsumerKey: consumerKey
- consumerSecret: consumerSec
- token: token
- tokenSecret: tokenSec
- signatureMethod: sigMethod];
- } else {
- NSDictionary* browserid = $castIf(NSDictionary, auth[@"browserid"]);
- if (browserid) {
- NSString* assertion = $castIf(NSString, browserid[@"assertion"]);
- *outAuthorizer = [[TDBrowserIDAuthorizer alloc] initWithAssertion: assertion];
- }
- }
- if (!*outAuthorizer)
- return kTDStatusBadRequest;
- }
- }
-
- return kTDStatusOK;
-}
-
-
#pragma mark - CRUD:
@@ -210,11 +112,7 @@ - (BOOL) validateRevision: (TD_Revision*)newRev context: (id<TD_ValidationContex
// First make sure the basic properties are valid:
NSDictionary* newProperties = newRev.properties;
LogTo(Sync, @"ReplicatorManager: Validating %@: %@", newRev, newProperties);
- BOOL push, createTarget;
- if ([self parseReplicatorProperties: newProperties toDatabase: NULL
- remote: NULL isPush: &push createTarget: &createTarget
- headers: NULL
- authorizer: NULL] >= 300) {
+ if ([_dbManager validateReplicatorProperties: newProperties] >= 300) {
context.errorMessage = @"Invalid replication parameters";
return NO;
}
@@ -330,40 +228,13 @@ - (void) processInsertion: (TD_Revision*)rev {
return;
LogTo(Sync, @"ReplicatorManager: %@ was created", rev);
NSDictionary* properties = rev.properties;
- TD_Database* localDb;
- NSURL* remote;
- BOOL push, createTarget;
- NSDictionary* headers;
- id<TDAuthorizer> authorizer;
- TDStatus status = [self parseReplicatorProperties: properties
- toDatabase: &localDb remote: &remote
- isPush: &push
- createTarget: &createTarget
- headers: &headers
- authorizer: &authorizer];
- if (TDStatusIsError(status)) {
- Warn(@"TDReplicatorManager: Can't find replication endpoints for %@", properties);
+ TDReplicator* repl = [_dbManager replicatorWithProperties: properties status: NULL];
+ if (!repl) {
+ Warn(@"TDReplicatorManager: Can't create replicator for %@", properties);
return;
}
-
- BOOL continuous = [$castIf(NSNumber, properties[@"continuous"]) boolValue];
- LogTo(Sync, @"TDReplicatorManager creating (remote=%@, push=%d, create=%d, continuous=%d)",
- remote, push, createTarget, continuous);
- TDReplicator* repl = [[TDReplicator alloc] initWithDB: localDb
- remote: remote
- push: push
- continuous: continuous];
- if (!repl)
- return;
NSString* replicationID = properties[@"_replication_id"] ?: TDCreateUUID();
repl.sessionID = replicationID;
- repl.filterName = $castIf(NSString, properties[@"filter"]);;
- repl.filterParameters = $castIf(NSDictionary, properties[@"query_params"]);
- repl.options = properties;
- repl.requestHeaders = headers;
- repl.authorizer = authorizer;
- if (push)
- ((TDPusher*)repl).createTarget = createTarget;
if (!_replicatorsByDocID)
_replicatorsByDocID = [[NSMutableDictionary alloc] init];
View
54 Source/TDReplicator_Tests.m
@@ -294,9 +294,19 @@ static void deleteRemoteDB(void) {
}
+@interface TD_DatabaseManager (Internal)
+- (TDStatus) parseReplicatorProperties: (NSDictionary*)properties
+ toDatabase: (TD_Database**)outDatabase // may be NULL
+ remote: (NSURL**)outRemote // may be NULL
+ isPush: (BOOL*)outIsPush
+ createTarget: (BOOL*)outCreateTarget
+ headers: (NSDictionary**)outHeaders
+ authorizer: (id<TDAuthorizer>*)outAuthorizer;
+@end
+
+
TestCase(ParseReplicatorProperties) {
TD_DatabaseManager* dbManager = [TD_DatabaseManager createEmptyAtTemporaryPath: @"TDReplicatorManagerTest"];
- TDReplicatorManager* replManager = [dbManager replicatorManager];
TD_Database* localDB = [dbManager databaseNamed: @"foo"];
TD_Database* db = nil;
@@ -308,13 +318,13 @@ static void deleteRemoteDB(void) {
props = $dict({@"source", @"foo"},
{@"target", @"http://example.com"},
{@"create_target", $true});
- CAssertEq(200, [replManager parseReplicatorProperties: props
- toDatabase: &db
- remote: &remote
- isPush: &isPush
- createTarget: &createTarget
- headers: &headers
- authorizer: NULL]);
+ CAssertEq(200, [dbManager parseReplicatorProperties: props
+ toDatabase: &db
+ remote: &remote
+ isPush: &isPush
+ createTarget: &createTarget
+ headers: &headers
+ authorizer: NULL]);
CAssertEq(db, localDB);
CAssertEqual(remote, $url(@"http://example.com"));
CAssertEq(isPush, YES);
@@ -323,13 +333,13 @@ static void deleteRemoteDB(void) {
props = $dict({@"source", @"touchdb:///foo"},
{@"target", @"foo"});
- CAssertEq(200, [replManager parseReplicatorProperties: props
- toDatabase: &db
- remote: &remote
- isPush: &isPush
- createTarget: &createTarget
- headers: &headers
- authorizer: NULL]);
+ CAssertEq(200, [dbManager parseReplicatorProperties: props
+ toDatabase: &db
+ remote: &remote
+ isPush: &isPush
+ createTarget: &createTarget
+ headers: &headers
+ authorizer: NULL]);
CAssertEq(db, localDB);
CAssertEqual(remote, $url(@"touchdb:///foo"));
CAssertEq(isPush, NO);
@@ -345,13 +355,13 @@ static void deleteRemoteDB(void) {
{@"auth", $dict({@"oauth", oauthDict})})},
{@"target", @"foo"});
id<TDAuthorizer> authorizer = nil;
- CAssertEq(200, [replManager parseReplicatorProperties: props
- toDatabase: &db
- remote: &remote
- isPush: &isPush
- createTarget: &createTarget
- headers: &headers
- authorizer: &authorizer]);
+ CAssertEq(200, [dbManager parseReplicatorProperties: props
+ toDatabase: &db
+ remote: &remote
+ isPush: &isPush
+ createTarget: &createTarget
+ headers: &headers
+ authorizer: &authorizer]);
CAssertEq(db, localDB);
CAssertEqual(remote, $url(@"http://example.com"));
CAssertEq(isPush, NO);
View
35 Source/TDRouter+Handlers.m
@@ -58,42 +58,15 @@ - (TDStatus) do_GET_all_dbs {
}
- (TDStatus) do_POST_replicate {
- // Extract the parameters from the JSON request body:
- // http://wiki.apache.org/couchdb/Replication
- TD_Database* db;
- NSURL* remote;
- BOOL push, createTarget;
- NSDictionary* headers;
- id<TDAuthorizer> authorizer;
NSDictionary* body = self.bodyAsDictionary;
- TDStatus status = [_dbManager.replicatorManager parseReplicatorProperties: body
- toDatabase: &db remote: &remote
- isPush: &push
- createTarget: &createTarget
- headers: &headers
- authorizer: &authorizer];
- if (TDStatusIsError(status))
- return status;
-
- BOOL continuous = [$castIf(NSNumber, body[@"continuous"]) boolValue];
-
- TDReplicator* repl = [[TDReplicator alloc] initWithDB: db
- remote: remote
- push: push
- continuous: continuous];
+ TDStatus status;
+ TDReplicator* repl = [_dbManager replicatorWithProperties: body status: &status];
if (!repl)
- return kTDStatusServerError;
- repl.filterName = $castIf(NSString, body[@"filter"]);
- repl.filterParameters = $castIf(NSDictionary, body[@"query_params"]);
- repl.options = body;
- repl.requestHeaders = headers;
- repl.authorizer = authorizer;
- if (push)
- ((TDPusher*)repl).createTarget = createTarget;
+ return status;
if ([$castIf(NSNumber, body[@"cancel"]) boolValue]) {
// Cancel replication:
- TDReplicator* activeRepl = [db activeReplicatorLike: repl];
+ TDReplicator* activeRepl = [repl.db activeReplicatorLike: repl];
if (!activeRepl)
return kTDStatusNotFound;
[activeRepl stop];
View
7 Source/TD_DatabaseManager.h
@@ -7,7 +7,8 @@
//
#import <Foundation/Foundation.h>
-@class TD_Database, TDReplicatorManager;
+#import "TDStatus.h"
+@class TD_Database, TDReplicator, TDReplicatorManager;
typedef struct TD_DatabaseManagerOptions {
@@ -46,4 +47,8 @@ extern const TD_DatabaseManagerOptions kTD_DatabaseManagerDefaultOptions;
- (void) close;
+- (TDStatus) validateReplicatorProperties: (NSDictionary*)properties;
+- (TDReplicator*) replicatorWithProperties: (NSDictionary*)body
+ status: (TDStatus*)outStatus;
+
@end
View
162 Source/TD_DatabaseManager.m
@@ -14,7 +14,10 @@
// and limitations under the License.
#import "TD_DatabaseManager.h"
-#import <TouchDB/TD_Database.h>
+#import "TD_Database.h"
+#import "TDOAuth1Authorizer.h"
+#import "TDBrowserIDAuthorizer.h"
+#import "TDPusher.h"
#import "TDReplicatorManager.h"
#import "TDInternal.h"
#import "TDMisc.h"
@@ -183,6 +186,163 @@ - (void) close {
}
+#pragma mark - REPLICATION:
+
+
+// Replication 'source' or 'target' property may be a string or a dictionary. Normalize to dict form
+static NSDictionary* parseSourceOrTarget(NSDictionary* properties, NSString* key) {
+ id value = properties[key];
+ if ([value isKindOfClass: [NSDictionary class]])
+ return value;
+ else if ([value isKindOfClass: [NSString class]])
+ return $dict({@"url", value});
+ else
+ return nil;
+}
+
+
+- (TDStatus) parseReplicatorProperties: (NSDictionary*)properties
+ toDatabase: (TD_Database**)outDatabase // may be NULL
+ remote: (NSURL**)outRemote // may be NULL
+ isPush: (BOOL*)outIsPush
+ createTarget: (BOOL*)outCreateTarget
+ headers: (NSDictionary**)outHeaders
+ authorizer: (id<TDAuthorizer>*)outAuthorizer
+{
+ // http://wiki.apache.org/couchdb/Replication
+ NSDictionary* sourceDict = parseSourceOrTarget(properties, @"source");
+ NSDictionary* targetDict = parseSourceOrTarget(properties, @"target");
+ NSString* source = sourceDict[@"url"];
+ NSString* target = targetDict[@"url"];
+ if (!source || !target)
+ return kTDStatusBadRequest;
+
+ *outCreateTarget = [$castIf(NSNumber, properties[@"create_target"]) boolValue];
+ *outIsPush = NO;
+ TD_Database* db = nil;
+ NSDictionary* remoteDict = nil;
+ if ([TD_DatabaseManager isValidDatabaseName: source]) {
+ if (outDatabase)
+ db = [self existingDatabaseNamed: source];
+ remoteDict = targetDict;
+ *outIsPush = YES;
+ } else {
+ if (![TD_DatabaseManager isValidDatabaseName: target])
+ return kTDStatusBadID;
+ remoteDict = sourceDict;
+ if (outDatabase) {
+ if (*outCreateTarget) {
+ db = [self databaseNamed: target];
+ if (![db open])
+ return kTDStatusDBError;
+ } else {
+ db = [self existingDatabaseNamed: target];
+ }
+ }
+ }
+ NSURL* remote = [NSURL URLWithString: remoteDict[@"url"]];
+ if (![@[@"http", @"https", @"touchdb"] containsObject: remote.scheme.lowercaseString])
+ return kTDStatusBadRequest;
+ if (outDatabase) {
+ *outDatabase = db;
+ if (!db)
+ return kTDStatusNotFound;
+ }
+ if (outRemote)
+ *outRemote = remote;
+ if (outHeaders)
+ *outHeaders = $castIf(NSDictionary, remoteDict[@"headers"]);
+
+ if (outAuthorizer) {
+ *outAuthorizer = nil;
+ NSDictionary* auth = $castIf(NSDictionary, remoteDict[@"auth"]);
+ if (auth) {
+ NSDictionary* oauth = $castIf(NSDictionary, auth[@"oauth"]);
+ if (oauth) {
+ NSString* consumerKey = $castIf(NSString, oauth[@"consumer_key"]);
+ NSString* consumerSec = $castIf(NSString, oauth[@"consumer_secret"]);
+ NSString* token = $castIf(NSString, oauth[@"token"]);
+ NSString* tokenSec = $castIf(NSString, oauth[@"token_secret"]);
+ NSString* sigMethod = $castIf(NSString, oauth[@"signature_method"]);
+ *outAuthorizer = [[TDOAuth1Authorizer alloc] initWithConsumerKey: consumerKey
+ consumerSecret: consumerSec
+ token: token
+ tokenSecret: tokenSec
+ signatureMethod: sigMethod];
+ } else {
+ NSDictionary* browserid = $castIf(NSDictionary, auth[@"browserid"]);
+ if (browserid) {
+ NSString* assertion = $castIf(NSString, browserid[@"assertion"]);
+ *outAuthorizer = [[TDBrowserIDAuthorizer alloc] initWithAssertion: assertion];
+ }
+ }
+ if (!*outAuthorizer)
+ return kTDStatusBadRequest;
+ }
+ }
+
+ return kTDStatusOK;
+}
+
+
+- (TDStatus) validateReplicatorProperties: (NSDictionary*)properties {
+ BOOL push, createTarget;
+ return [self parseReplicatorProperties: properties toDatabase: NULL
+ remote: NULL isPush: &push createTarget: &createTarget
+ headers: NULL
+ authorizer: NULL];
+}
+
+
+- (TDReplicator*) replicatorWithProperties: (NSDictionary*)properties
+ status: (TDStatus*)outStatus
+{
+ // Extract the parameters from the JSON request body:
+ // http://wiki.apache.org/couchdb/Replication
+ TD_Database* db;
+ NSURL* remote;
+ BOOL push, createTarget;
+ NSDictionary* headers;
+ id<TDAuthorizer> authorizer;
+
+ TDStatus status = [self parseReplicatorProperties: properties
+ toDatabase: &db remote: &remote
+ isPush: &push
+ createTarget: &createTarget
+ headers: &headers
+ authorizer: &authorizer];
+ if (TDStatusIsError(status)) {
+ if (outStatus)
+ *outStatus = status;
+ return nil;
+ }
+
+ BOOL continuous = [$castIf(NSNumber, properties[@"continuous"]) boolValue];
+
+ TDReplicator* repl = [[TDReplicator alloc] initWithDB: db
+ remote: remote
+ push: push
+ continuous: continuous];
+ if (!repl) {
+ if (outStatus)
+ *outStatus = kTDStatusServerError;
+ return nil;
+ }
+
+ repl.filterName = $castIf(NSString, properties[@"filter"]);
+ repl.filterParameters = $castIf(NSDictionary, properties[@"query_params"]);
+ repl.options = properties;
+ repl.requestHeaders = headers;
+ repl.authorizer = authorizer;
+ if (push)
+ ((TDPusher*)repl).createTarget = createTarget;
+
+ if (outStatus)
+ *outStatus = kTDStatusOK;
+ return repl;
+}
+
+
- (TDReplicatorManager*) replicatorManager {
if (!_replicatorManager && !_options.noReplicator) {
LogTo(TD_Server, @"Starting replicator manager for %@", self);

0 comments on commit 6fbb4f2

Please sign in to comment.
Something went wrong with that request. Please try again.