diff --git a/mobile/android/manifest b/mobile/android/manifest index 93aed04..bd362ef 100644 --- a/mobile/android/manifest +++ b/mobile/android/manifest @@ -2,7 +2,7 @@ # this is your module manifest and used by Titanium # during compilation, packaging, distribution, etc. # -version: 1.0 +version: 1.0.1 apiversion: 2 description: TouchDB for Titanium author: Paul Mietz Egli diff --git a/mobile/android/src/com/obscure/titouchdb/ReplicationProxy.java b/mobile/android/src/com/obscure/titouchdb/ReplicationProxy.java index 1a57679..482d8c0 100644 --- a/mobile/android/src/com/obscure/titouchdb/ReplicationProxy.java +++ b/mobile/android/src/com/obscure/titouchdb/ReplicationProxy.java @@ -8,6 +8,8 @@ import org.appcelerator.kroll.KrollProxy; import org.appcelerator.kroll.annotations.Kroll; +import com.couchbase.lite.auth.Authenticator; +import com.couchbase.lite.auth.AuthenticatorFactory; import com.couchbase.lite.replicator.Replication; import com.couchbase.lite.replicator.Replication.ChangeEvent; import com.couchbase.lite.replicator.Replication.ChangeListener; @@ -15,13 +17,13 @@ @Kroll.proxy(parentModule = TitouchdbModule.class) public class ReplicationProxy extends KrollProxy implements ChangeListener { - private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private static final String[] EMPTY_STRING_ARRAY = new String[0]; - private static final String LCAT = "ReplicationProxy"; + private static final String LCAT = "ReplicationProxy"; private DatabaseProxy databaseProxy; - private KrollDict lastError = null; + private KrollDict lastError = null; private Replication replicator; @@ -30,7 +32,7 @@ public ReplicationProxy(DatabaseProxy databaseProxy, Replication replicator) { assert replicator != null; this.databaseProxy = databaseProxy; this.replicator = replicator; - + replicator.addChangeListener(this); } @@ -67,8 +69,7 @@ public KrollDict getFilterParams() { @Kroll.getProperty(name = "headers") public KrollDict getHeaders() { - // TODO - return null; + return TypePreprocessor.toKrollDict(replicator.getHeaders()); } @Kroll.getProperty(name = "localDatabase") @@ -107,7 +108,7 @@ public boolean isRunning() { return replicator.isRunning(); } - @Kroll.method(runOnUiThread=true) + @Kroll.method(runOnUiThread = true) public void restart() { replicator.restart(); } @@ -123,8 +124,17 @@ public void setCreateTarget(boolean createTarget) { } @Kroll.method - public void setCredential(KrollDict credential) { - // TODO + public void setCredential(@Kroll.argument(optional=true) KrollDict credential) { + if (credential == null) { + replicator.setAuthenticator(null); + } + else { + // oddly enough, KrollDict.getString() crashes... + String user = (String) credential.get("user"); + String pass = (String) credential.get("pass"); + Authenticator authenticator = AuthenticatorFactory.createBasicAuthenticator(user, pass); + replicator.setAuthenticator(authenticator); + } } @Kroll.setProperty(name = "docIds") @@ -138,21 +148,21 @@ public void setFilter(String filter) { } @Kroll.setProperty(name = "filterParams") - public void setFilterParams(String filterParams) { - // TODO + public void setFilterParams(KrollDict filterParams) { + replicator.setFilterParams(filterParams); } @Kroll.setProperty(name = "headers") - public void setHeaders(String headers) { - // TODO + public void setHeaders(KrollDict headers) { + replicator.setHeaders(headers); } - @Kroll.method(runOnUiThread=true) + @Kroll.method(runOnUiThread = true) public void start() { replicator.start(); } - @Kroll.method(runOnUiThread= true) + @Kroll.method(runOnUiThread = true) public void stop() { replicator.stop(); } @@ -162,7 +172,7 @@ public void changed(ChangeEvent e) { KrollDict params = new KrollDict(); params.put("source", this); params.put("status", replicator.getStatus().ordinal()); - + fireEvent("status", params); } diff --git a/mobile/ios/manifest b/mobile/ios/manifest index 0a3587d..1e9c413 100644 --- a/mobile/ios/manifest +++ b/mobile/ios/manifest @@ -2,7 +2,7 @@ # this is your module manifest and used by Titanium # during compilation, packaging, distribution, etc. # -version: 1.0 +version: 1.0.1 apiversion: 2 description: Titanium wrapper for Couchbase Mobile author: Paul Mietz Egli diff --git a/mobile/noarch/alloy/sync/titouchdb.js b/mobile/noarch/alloy/sync/titouchdb.js index e1fb630..c4fa773 100755 --- a/mobile/noarch/alloy/sync/titouchdb.js +++ b/mobile/noarch/alloy/sync/titouchdb.js @@ -5,8 +5,7 @@ var _ = require('alloy/underscore'), titouchdb = require('com.obscure.titouchdb'), manager = titouchdb.databaseManager, - db, - modelname; + db; /** @@ -78,64 +77,84 @@ function InitAdapter(config) { function Sync(method, model, options) { var opts = options || {}; + var resp = null, err = null; switch (method) { case 'create': var props = model.toJSON(); - props.modelname = model.config.adapter.modelname; + _.extend(props, model.config.adapter.static_properties || {}); var doc = model.id ? db.getDocument(model.id) : db.createDocument(); doc.putProperties(props); - model.id = doc.documentID; - model.trigger('create'); + err = doc.error; + if (!err) { + model.set(doc.properties, { silent: true }); + model.id = doc.documentID; + resp = model.toJSON(); + !opts.silent && model.trigger('change', { fromAdapter: true }); + } + break; case 'read': - if (opts.parse) { + if (model.idAttribute) { + // fetch a single model + var obj = opts.id ? db.getDocument(opts.id) : db.createDocument(); + if (obj) { + model.set(obj.properties); + model.id = obj.documentID; + !opts.silent && model.trigger('fetch', { fromAdapter: true }); + resp = model.toJSON(); + } + err = db.error; + } + else { var collection = model; // just to clear things up // collection var view = opts.view || collection.config.adapter.views[0]["name"]; - + collection.view = view; + // add default view options from model opts = _.defaults(opts, collection.config.adapter.view_options); var query = query_view(db, view, opts); if (!query) { + err = { error: 'missing view' }; break; } var rows = query.run(); - - // do not use Collection methods! - var len = 0; - if (!opts.add) { - collection.models = []; + if (rows.error) { + err = rows.error; + break; } + + var len = 0, values = []; while (row = rows.next()) { var m = collection.map_row(collection.model, row); if (m) { - collection.models.push(m); + values.push(m); ++len; } } - collection.view = view; - collection.length = len; - collection.trigger('fetch'); - } - else { - // object - var obj = db.getDocument(model.id) - model.set(obj.properties); - model.id = obj.documentID; - model.trigger('fetch'); + + resp = 1 === len ? values[0] : values; + !opts.silent && collection.trigger('fetch', { fromAdapter: true }); } break; case 'update': var props = model.toJSON(); - props.modelname = model.config.adapter.modelname; + _.extend(props, model.config.adapter.static_properties || {}); var doc = db.getDocument(model.id); - doc.putProperties(model.toJSON()); - model.trigger('update'); + doc.putProperties(props); + err = doc.error; + if (!err) { + model.set(doc.properties, { silent: true }); + model.id = doc.documentID; + resp = model.toJSON(); + !opts.silent && model.trigger('change', { fromAdapter: true }); + } + break; case 'delete': @@ -143,10 +162,20 @@ function Sync(method, model, options) { var doc = db.getDocument(model.id); doc.deleteDocument(); model.id = null; - model.trigger('destroy'); + err = doc.error; + if (!err) { + resp = model.toJSON(); + !opts.silent && model.trigger('destroy', { fromAdapter: true }); + } } break; - } + } + + if (resp) { + _.isFunction(opts.success) && opts.success(resp); + } else { + _.isFunction(opts.error) && opts.error(resp); + } } module.exports.sync = Sync; @@ -164,6 +193,7 @@ module.exports.afterModelCreate = function(Model) { Model.prototype.idAttribute = '_id'; // true for all TouchDB documents Model.prototype.config.Model = Model; // needed for fetch operations to initialize the collection from persistent store + Model.prototype.database = db; Model.prototype.attachmentNamed = function(name) { var doc = db.getDocument(this.id); diff --git a/mobile/noarch/example/013_replication.js b/mobile/noarch/example/013_replication.js index efcd48c..bee46e9 100644 --- a/mobile/noarch/example/013_replication.js +++ b/mobile/noarch/example/013_replication.js @@ -165,4 +165,31 @@ module.exports = function() { repl.start(); }); }); + + describe('pull replication with auth credentials', function() { + var conf, repl; + + before(function(done) { + utils.delete_nonsystem_databases(manager); + conf = utils.verify_couchdb_server('replication_config.json', done); + }); + + // currently returning a 400 error due to a request for /elements/_session + it.skip('must replicate with credentials', function(done) { + this.timeout(10000); + var db = manager.getDatabase('repl3'); + repl = db.createPullReplication('http://'+conf.host+':'+conf.port+'/'+conf.dbname); + repl.setCredential({ user: 'scott', pass: 'tiger' }); + repl.addEventListener('status', function(e) { + if (e.status == titouchdb.REPLICATION_MODE_STOPPED) { + should.not.exist(repl.lastError); + db.documentCount.should.eql(118); + repl.isRunning.should.eql(false); + done(); + } + }); + repl.start(); + }); + }); + }; \ No newline at end of file diff --git a/mobile/noarch/example/LiteServ/run_liteserv.sh b/mobile/noarch/example/LiteServ/run_liteserv.sh index 17c8b28..5193aaa 100755 --- a/mobile/noarch/example/LiteServ/run_liteserv.sh +++ b/mobile/noarch/example/LiteServ/run_liteserv.sh @@ -1,5 +1,5 @@ #!/bin/bash DATA=$(mktemp -d /tmp/liteserv.XXXXX) cp -R ../assets/CouchbaseLite/elements* ${DATA} -./bin/LiteServ --dir ${DATA} -LogSync -LogSyncVerbose -rm -Rf ${DATA} \ No newline at end of file +./bin/LiteServ --dir ${DATA} -Log YES -LogSync YES -LogSyncVerbose YES +rm -Rf ${DATA}