public void TestForceInsertEmptyHistory()
        {
            var rev = new RevisionInternal("FakeDocId", "1-abcd", false);
            var revProperties = new Dictionary<string, object>();
            revProperties.Put("_id", rev.GetDocId());
            revProperties.Put("_rev", rev.GetRevId());
            revProperties["message"] = "hi";
            rev.SetProperties(revProperties);

            IList<string> revHistory = null;
            database.ForceInsert(rev, revHistory, null);
        }
示例#2
0
		/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
		public virtual void TestForceInsertEmptyHistory()
		{
			IList<string> revHistory = null;
			RevisionInternal rev = new RevisionInternal("FakeDocId", "1-tango", false, database
				);
			IDictionary<string, object> revProperties = new Dictionary<string, object>();
			revProperties.Put("_id", rev.GetDocId());
			revProperties.Put("_rev", rev.GetRevId());
			revProperties.Put("message", "hi");
			rev.SetProperties(revProperties);
			database.ForceInsert(rev, revHistory, null);
		}
        internal RevisionInternal Winner(Int64 docNumericID, String oldWinningRevID, Boolean oldWinnerWasDeletion, RevisionInternal newRev)
        {
            if (oldWinningRevID == null)
            {
                return newRev;
            }
            var newRevID = newRev.GetRevId();
            if (!newRev.IsDeleted())
            {
                if (oldWinnerWasDeletion || RevisionInternal.CBLCompareRevIDs(newRevID, oldWinningRevID) > 0)
                {
                    return newRev;
                }
            }
            else
            {
                // this is now the winning live revision
                if (oldWinnerWasDeletion)
                {
                    if (RevisionInternal.CBLCompareRevIDs(newRevID, oldWinningRevID) > 0)
                    {
                        return newRev;
                    }
                }
                else
                {
                    // doc still deleted, but this beats previous deletion rev
                    // Doc was alive. How does this deletion affect the winning rev ID?
                    var outIsDeleted = new AList<bool>();
                    var outIsConflict = new AList<bool>();
                    var winningRevID = WinningRevIDOfDoc(docNumericID, outIsDeleted, outIsConflict);

                    if (!winningRevID.Equals(oldWinningRevID))
                    {
                        if (winningRevID.Equals(newRev.GetRevId()))
                        {
                            return newRev;
                        }
                        else
                        {
                            var deleted = false;
                            var winningRev = new RevisionInternal(newRev.GetDocId(), winningRevID, deleted, this);
                            return winningRev;
                        }
                    }
                }
            }
            return null;
        }
        private DocumentChange ChangeWithNewRevision(RevisionInternal inRev, bool isWinningRev, C4Document *doc, Uri source)
        {
            var winningRevId = default(string);
            if(isWinningRev) {
                winningRevId = inRev.GetRevId();
            } else {
                winningRevId = (string)doc->revID;
            }

            return new DocumentChange(inRev, winningRevId, doc->IsConflicted, source);
        }
        /// <summary>Returns an array of TDRevs in reverse chronological order, starting with the given revision.
        ///     </summary>
        /// <remarks>Returns an array of TDRevs in reverse chronological order, starting with the given revision.
        ///     </remarks>
        internal IList<RevisionInternal> GetRevisionHistory(RevisionInternal rev)
        {
            string docId = rev.GetDocId();
            string revId = rev.GetRevId();

            Debug.Assert(((docId != null) && (revId != null)));

            long docNumericId = GetDocNumericID(docId);
            if (docNumericId < 0)
            {
                return null;
            }
            else
            {
                if (docNumericId == 0)
                {
                    return new AList<RevisionInternal>();
                }
            }

            Cursor cursor = null;
            IList<RevisionInternal> result;
            var args = new [] { Convert.ToString(docNumericId) };
            var sql = "SELECT sequence, parent, revid, deleted, json isnull  FROM revs WHERE doc_id=? ORDER BY sequence DESC";

            try
            {
                cursor = StorageEngine.RawQuery(sql, args);
                cursor.MoveToNext();

                long lastSequence = 0;
                result = new AList<RevisionInternal>();

                while (!cursor.IsAfterLast())
                {
                    var sequence = cursor.GetLong(0);
                    var parent = cursor.GetLong(1);

                    bool matches = false;
                    if (lastSequence == 0)
                    {
                        matches = revId.Equals(cursor.GetString(2));
                    }
                    else
                    {
                        matches = (sequence == lastSequence);
                    }
                    if (matches)
                    {
                        revId = cursor.GetString(2);
                        var deleted = (cursor.GetInt(3) > 0);
                        var missing = (cursor.GetInt(4) > 0);

                        var aRev = new RevisionInternal(docId, revId, deleted, this);
                        aRev.SetSequence(sequence);
                        aRev.SetMissing(missing);
                        result.AddItem(aRev);

                        if (parent > -1)
                            lastSequence = parent;

                        if (lastSequence == 0)
                        {
                            break;
                        }
                    }
                    cursor.MoveToNext();
                }
            }
            catch (SQLException e)
            {
                Log.E(Tag, "Error getting revision history", e);
                return null;
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
            }
            return result;
        }
示例#6
0
        //Doesn't handle CouchbaseLiteException
        internal RevisionInternal LoadRevisionBody(RevisionInternal rev)
        {
            if (rev.GetSequence() > 0) {
                var props = rev.GetProperties();
                if (props != null && props.GetCast<string>("_rev") != null && props.GetCast<string>("_id") != null) {
                    return rev;
                }
            }

            Debug.Assert(rev.GetDocId() != null && rev.GetRevId() != null);
            Storage.LoadRevisionBody(rev);
            return rev;
        }
        internal RevisionInternal TransformRevision(RevisionInternal rev)
        {
            if (RevisionBodyTransformationFunction != null) {
                try {
                    var generation = rev.GetGeneration();
                    var xformed = RevisionBodyTransformationFunction(rev);
                    if (xformed == null) {
                        return null;
                    }

                    if (xformed != rev) {
                        Debug.Assert((xformed.GetDocId().Equals(rev.GetDocId())));
                        Debug.Assert((xformed.GetRevId().Equals(rev.GetRevId())));
                        Debug.Assert((xformed.GetProperties().Get("_revisions").Equals(rev.GetProperties().Get("_revisions"))));

                        if (xformed.GetProperties().ContainsKey("_attachments")) {
                            // Insert 'revpos' properties into any attachments added by the callback:
                            var mx = new RevisionInternal(xformed.GetProperties());
                            xformed = mx;
                            mx.MutateAttachments((name, info) =>
                            {
                                if (info.Get("revpos") != null) {
                                    return info;
                                }

                                if (info.Get("data") == null) {
                                    throw new InvalidOperationException("Transformer added attachment without adding data");
                                }

                                var newInfo = new Dictionary<string, object>(info);
                                newInfo["revpos"] = generation;
                                return newInfo;
                            });
                        }
                    }
                } catch (Exception e) {
                    Log.W(TAG, String.Format("Exception transforming a revision of doc '{0}'", rev.GetDocId()), e);
                }
            }

            return rev;
        }
 /// <summary>
 /// Creates a dictionary of metadata for one specific revision
 /// </summary>
 /// <returns>The metadata dictionary</returns>
 /// <param name="rev">The revision to examine</param>
 /// <param name="responseState">The current response state</param>
 public static IDictionary<string, object> ChangesDictForRev(RevisionInternal rev, DBMonitorCouchbaseResponseState responseState)
 {
     if (responseState.ChangesIncludeDocs) {
         var status = new Status();
         var rev2 = DocumentMethods.ApplyOptions(responseState.ContentOptions, rev, responseState.Context, responseState.Db, status);
         if (rev2 != null) {
             rev2.SetSequence(rev.GetSequence());
             rev = rev2;
         }
     }
     return new NonNullDictionary<string, object> {
         { "seq", rev.GetSequence() },
         { "id", rev.GetDocId() },
         { "changes", new List<object> { 
                 new Dictionary<string, object> { 
                     { "rev", rev.GetRevId() } 
                 } 
             } 
         },
         { "deleted", rev.IsDeleted() ? (object)true : null },
         { "doc", responseState.ChangesIncludeDocs ? rev.GetProperties() : null }
     };
 }
        public long GetRevisionSequence(RevisionInternal rev)
        {
            var retVal = 0L;
            WithC4Document(rev.GetDocId(), rev.GetRevId(), false, false, doc => retVal = (long)doc->selectedRev.sequence);

            return retVal;
        }
        public void TestLocalDocs()
        {
            //create a document
            var documentProperties = new Dictionary<string, object>();
            documentProperties["_id"] = "_local/doc1";
            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;
            var body = new Body(documentProperties);
            var rev1 = new RevisionInternal(body);
            rev1 = database.Storage.PutLocalRevision(rev1, null, true);
            Log.V(Tag, "Created " + rev1);
            Assert.AreEqual("_local/doc1", rev1.GetDocId());
            Assert.IsTrue(rev1.GetRevId().StartsWith("1-"));

            //read it back
            var readRev = database.Storage.GetLocalDocument(rev1.GetDocId(), null);
            Assert.IsNotNull(readRev);
            var readRevProps = readRev.GetProperties();
            Assert.AreEqual(rev1.GetDocId(), readRevProps.Get("_id"));
            Assert.AreEqual(rev1.GetRevId(), readRevProps.Get("_rev"));
            AssertPropertiesAreEqual(UserProperties(readRevProps), 
                UserProperties(body.GetProperties()));

            //now update it
            documentProperties = (Dictionary<string, object>)readRev.GetProperties();
            documentProperties["status"] = "updated!";
            body = new Body(documentProperties);
            var rev2 = new RevisionInternal(body);
            var rev2input = rev2;
            rev2 = database.Storage.PutLocalRevision(rev2, rev1.GetRevId(), true);
            Log.V(Tag, "Updated " + rev1);
            Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId());
            Assert.IsTrue(rev2.GetRevId().StartsWith("2-"));
            
            //read it back
            readRev = database.Storage.GetLocalDocument(rev2.GetDocId(), null);
            Assert.IsNotNull(readRev);
            AssertPropertiesAreEqual(UserProperties(readRev.GetProperties()), 
                UserProperties(body.GetProperties()));

            // Try to update the first rev, which should fail:
            var gotException = false;
            try
            {
                database.Storage.PutLocalRevision(rev2input, rev1.GetRevId(), true);
            }
            catch (CouchbaseLiteException e)
            {
                Assert.AreEqual(StatusCode.Conflict, e.CBLStatus.Code);
                gotException = true;
            }
            Assert.IsTrue(gotException);
            
            // Delete it:
            var revD = new RevisionInternal(rev2.GetDocId(), null, true);
            gotException = false;
            try
            {
                var revResult = database.Storage.PutLocalRevision(revD, null, true);
                Assert.IsNull(revResult);
            }
            catch (CouchbaseLiteException e)
            {
                Assert.AreEqual(StatusCode.Conflict, e.CBLStatus.Code);
                gotException = true;
            }
            Assert.IsTrue(gotException);
            revD = database.Storage.PutLocalRevision(revD, rev2.GetRevId(), true);
            
            // Delete nonexistent doc:
            gotException = false;
            var revFake = new RevisionInternal("_local/fake", null, true);
            try
            {
                database.Storage.PutLocalRevision(revFake, null, true);
            }
            catch (CouchbaseLiteException e)
            {
                Assert.AreEqual(StatusCode.NotFound, e.CBLStatus.Code);
                gotException = true;
            }
            Assert.IsTrue(gotException);
            
            // Read it back (should fail):
            readRev = database.Storage.GetLocalDocument(revD.GetDocId(), null);
            Assert.IsNull(readRev);
        }
        public void TestRevTreeChangeNotification()
        {
            const string DOCUMENT_ID = "MyDocId";

            var rev = new RevisionInternal(DOCUMENT_ID, "1-abcd", false);
            var revProperties = new Dictionary<string, object>();
            revProperties["_id"] = rev.GetDocId();
            revProperties["_rev"] = rev.GetRevId();
            revProperties["message"] = "hi";
            rev.SetProperties(revProperties);

            var revHistory = new List<string>();
            revHistory.Add(rev.GetRevId());

            EventHandler<DatabaseChangeEventArgs> handler = (sender, e) =>
            {
                var changes = e.Changes.ToList();
                Assert.AreEqual(1, changes.Count);
                var change = changes[0];
                Assert.AreEqual(DOCUMENT_ID, change.DocumentId);
                Assert.AreEqual(rev.GetRevId(), change.RevisionId);
                Assert.IsTrue(change.IsCurrentRevision);
                Assert.IsFalse(change.IsConflict);

                var current = database.GetDocument(change.DocumentId).CurrentRevision;
                Assert.AreEqual(rev.GetRevId(), current.Id);
            };

            database.Changed += handler;
            database.ForceInsert(rev, revHistory, null);
            database.Changed -= handler;

            // add two more revisions to the document
            var rev3 = new RevisionInternal(DOCUMENT_ID, "3-abcd", false);
            var rev3Properties = new Dictionary<string, object>();
            rev3Properties["_id"] = rev3.GetDocId();
            rev3Properties["_rev"] = rev3.GetRevId();
            rev3Properties["message"] = "hi again";
            rev3.SetProperties(rev3Properties);

            var rev3History = new List<string>();
            rev3History.Add(rev3.GetRevId());
            rev3History.Add("2-abcd");
            rev3History.Add(rev.GetRevId());

            handler = (sender, e) =>
            {
                var changes = e.Changes.ToList();
                Assert.AreEqual(1, changes.Count);
                var change = changes[0];
                Assert.AreEqual(DOCUMENT_ID, change.DocumentId);
                Assert.AreEqual(rev3.GetRevId(), change.RevisionId);
                Assert.IsTrue(change.IsCurrentRevision);
                Assert.IsFalse(change.IsConflict);

                var doc = database.GetDocument(change.DocumentId);
                Assert.AreEqual(rev3.GetRevId(), doc.CurrentRevisionId);
                try
                {
                    Assert.AreEqual(3, doc.RevisionHistory.ToList().Count);
                }
                catch (CouchbaseLiteException)
                {
                    Assert.Fail();
                }
            };

            database.Changed += handler;
            database.ForceInsert(rev3, rev3History, null);
            database.Changed -= handler;

            // add a conflicting revision, with the same history length as the last revision we
            // inserted. Since this new revision's revID has a higher ASCII sort, it should become the
            // new winning revision.
            var conflictRev = new RevisionInternal(DOCUMENT_ID, "3-bcde", false);
            var conflictProperties = new Dictionary<string, object>();
            conflictProperties["_id"] = conflictRev.GetDocId();
            conflictProperties["_rev"] = conflictRev.GetRevId();
            conflictProperties["message"] = "winner";
            conflictRev.SetProperties(conflictProperties);

            var conflictRevHistory = new List<string>();
            conflictRevHistory.Add(conflictRev.GetRevId());
            conflictRevHistory.Add("2-abcd");
            conflictRevHistory.Add(rev.GetRevId());

            handler = (sender, e) =>
            {
                var changes = e.Changes.ToList();
                Assert.AreEqual(1, changes.Count);
                var change = changes[0];
                Assert.AreEqual(DOCUMENT_ID, change.DocumentId);
                Assert.AreEqual(conflictRev.GetRevId(), change.RevisionId);
                Assert.IsTrue(change.IsCurrentRevision);
                Assert.IsFalse(change.IsConflict);

                var doc = database.GetDocument(change.DocumentId);
                Assert.AreEqual(rev3.GetRevId(), doc.CurrentRevisionId);
                try
                {
                    Assert.AreEqual(2, doc.ConflictingRevisions.ToList().Count);
                    Assert.AreEqual(3, doc.RevisionHistory.ToList().Count);
                }
                catch (CouchbaseLiteException)
                {
                    Assert.Fail();
                }
            };

            database.Changed += handler;
            database.ForceInsert(conflictRev, conflictRevHistory, null);
            database.Changed -= handler;
        }
        public IList<RevisionInternal> GetRevisionHistory(RevisionInternal rev, ICollection<string> ancestorRevIds)
        {
            var history = new List<RevisionInternal>();
            WithC4Document(rev.GetDocId(), rev.GetRevId(), false, false, doc =>
            {
                var enumerator = new CBForestHistoryEnumerator(doc, false);
                foreach(var next in enumerator) {
                    if(ancestorRevIds != null && ancestorRevIds.Contains((string)next.Document->selectedRev.revID)) {
                        break;
                    }

                    var newRev = new RevisionInternal(next.Document, false);
                    newRev.SetMissing(!Native.c4doc_hasRevisionBody(next.Document));
                    history.Add(newRev);
                }
            });

            return history;
        }
 /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
 public virtual void TestLocalDocs()
 {
     //create a document
     IDictionary<string, object> documentProperties = new Dictionary<string, object>();
     documentProperties.Put("_id", "_local/doc1");
     documentProperties.Put("foo", 1);
     documentProperties.Put("bar", false);
     Body body = new Body(documentProperties);
     RevisionInternal rev1 = new RevisionInternal(body, database);
     Status status = new Status();
     rev1 = database.PutLocalRevision(rev1, null);
     Log.V(Tag, "Created " + rev1);
     NUnit.Framework.Assert.AreEqual("_local/doc1", rev1.GetDocId());
     NUnit.Framework.Assert.IsTrue(rev1.GetRevId().StartsWith("1-"));
     //read it back
     RevisionInternal readRev = database.GetLocalDocument(rev1.GetDocId(), null);
     NUnit.Framework.Assert.IsNotNull(readRev);
     IDictionary<string, object> readRevProps = readRev.GetProperties();
     NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), readRev.GetProperties().Get("_id"
         ));
     NUnit.Framework.Assert.AreEqual(rev1.GetRevId(), readRev.GetProperties().Get("_rev"
         ));
     NUnit.Framework.Assert.AreEqual(UserProperties(readRevProps), UserProperties(body
         .GetProperties()));
     //now update it
     documentProperties = readRev.GetProperties();
     documentProperties.Put("status", "updated!");
     body = new Body(documentProperties);
     RevisionInternal rev2 = new RevisionInternal(body, database);
     RevisionInternal rev2input = rev2;
     rev2 = database.PutLocalRevision(rev2, rev1.GetRevId());
     Log.V(Tag, "Updated " + rev1);
     NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId());
     NUnit.Framework.Assert.IsTrue(rev2.GetRevId().StartsWith("2-"));
     //read it back
     readRev = database.GetLocalDocument(rev2.GetDocId(), null);
     NUnit.Framework.Assert.IsNotNull(readRev);
     NUnit.Framework.Assert.AreEqual(UserProperties(readRev.GetProperties()), UserProperties
         (body.GetProperties()));
     // Try to update the first rev, which should fail:
     bool gotException = false;
     try
     {
         database.PutLocalRevision(rev2input, rev1.GetRevId());
     }
     catch (CouchbaseLiteException e)
     {
         NUnit.Framework.Assert.AreEqual(Status.Conflict, e.GetCBLStatus().GetCode());
         gotException = true;
     }
     NUnit.Framework.Assert.IsTrue(gotException);
     // Delete it:
     RevisionInternal revD = new RevisionInternal(rev2.GetDocId(), null, true, database
         );
     gotException = false;
     try
     {
         RevisionInternal revResult = database.PutLocalRevision(revD, null);
         NUnit.Framework.Assert.IsNull(revResult);
     }
     catch (CouchbaseLiteException e)
     {
         NUnit.Framework.Assert.AreEqual(Status.Conflict, e.GetCBLStatus().GetCode());
         gotException = true;
     }
     NUnit.Framework.Assert.IsTrue(gotException);
     revD = database.PutLocalRevision(revD, rev2.GetRevId());
     // Delete nonexistent doc:
     gotException = false;
     RevisionInternal revFake = new RevisionInternal("_local/fake", null, true, database
         );
     try
     {
         database.PutLocalRevision(revFake, null);
     }
     catch (CouchbaseLiteException e)
     {
         NUnit.Framework.Assert.AreEqual(Status.NotFound, e.GetCBLStatus().GetCode());
         gotException = true;
     }
     NUnit.Framework.Assert.IsTrue(gotException);
     // Read it back (should fail):
     readRev = database.GetLocalDocument(revD.GetDocId(), null);
     NUnit.Framework.Assert.IsNull(readRev);
 }
        public string FindCommonAncestor(RevisionInternal rev, IEnumerable<string> revIds)
        {
            var generation = RevisionInternal.GenerationFromRevID(rev.GetRevId());
            var revIdArray = revIds == null ? null : revIds.ToList();
            if (generation <= 1 || revIdArray == null || revIdArray.Count == 0) {
                return null;
            }
             
            revIdArray.Sort(RevisionInternal.CBLCompareRevIDs);
            var commonAncestor = default(string);
            WithC4Document(rev.GetDocId(), null, false, false, doc =>
            {
                foreach(var possibleRevId in revIds) {
                    if(RevisionInternal.GenerationFromRevID(possibleRevId) <= generation &&
                        Native.c4doc_selectRevision(doc, possibleRevId, false, null)) {
                        commonAncestor = possibleRevId;
                        return;
                    }
                }
            });

            return commonAncestor;
        }
        public IEnumerable<string> GetPossibleAncestors(RevisionInternal rev, int limit, bool onlyAttachments)
        {
            var returnedCount = 0;
            var generation = RevisionInternal.GenerationFromRevID(rev.GetRevId());
            var enumerator = GetHistoryEnumerator(rev, generation);
            if(enumerator == null) {
                yield break;
            }

            foreach (var next in enumerator) {
                if(returnedCount >= limit) {
                    break;
                }

                var revId = next.CurrentRevID;
                if(RevisionInternal.GenerationFromRevID(revId) < generation &&
                    !next.SelectedRev.IsDeleted && next.HasRevisionBody &&
                    !(onlyAttachments && !next.SelectedRev.HasAttachments)) {
                    returnedCount++;
                    yield return revId;
                }
            }
        }
        public RevisionInternal GetParentRevision(RevisionInternal rev)
        {
            var retVal = default(RevisionInternal);
            WithC4Document(rev.GetDocId(), rev.GetRevId(), false, false, doc =>
            {
                if (!Native.c4doc_selectParentRevision(doc)) {
                    return;
                }
                    
                ForestDBBridge.Check(err => Native.c4doc_loadRevisionBody(doc, err));
                retVal = new RevisionInternal((string)doc->docID, (string)doc->selectedRev.revID, doc->selectedRev.IsDeleted);
                retVal.SetSequence((long)doc->selectedRev.sequence);
                retVal.SetBody(new Body(doc->selectedRev.body));
            });

            return retVal;
        }
示例#17
0
        internal Int64 InsertRevision(RevisionInternal rev, long docNumericID, long parentSequence, bool current, bool hasAttachments, IEnumerable<byte> data)
        {
            var rowId = 0L;
            try
            {
                var args = new ContentValues();
                args["doc_id"] = docNumericID;
                args.Put("revid", rev.GetRevId());
                if (parentSequence != 0)
                {
                    args["parent"] = parentSequence;
                }

                args["current"] = current;
                args["deleted"] = rev.IsDeleted();
                args["no_attachments"] = !hasAttachments;
                if (data != null)
                {
                    args["json"] = data.ToArray();
                }

                rowId = StorageEngine.Insert("revs", null, args);
                rev.SetSequence(rowId);
            }
            catch (Exception e)
            {
                Log.E(Tag, "Error inserting revision", e);
            }
            return rowId;
        }
        public void TestPusher()
        {
            if (!Boolean.Parse((string)Runtime.Properties["replicationTestsEnabled"]))
            {
                Assert.Inconclusive("Replication tests disabled.");
                return;
            }

            using (var remoteDb = _sg.CreateDatabase(TempDbName())) {
                var remote = remoteDb.RemoteUri;
                var docIdTimestamp = Convert.ToString(Runtime.CurrentTimeMillis());

                // Create some documents:
                var documentProperties = new Dictionary<string, object>();
                var doc1Id = string.Format("doc1-{0}", docIdTimestamp);
                documentProperties["_id"] = doc1Id;
                documentProperties["foo"] = 1;
                documentProperties["bar"] = false;

                var body = new Body(documentProperties);
                var rev1 = new RevisionInternal(body);
                rev1 = database.PutRevision(rev1, null, false);

                documentProperties.Put("_rev", rev1.GetRevId());
                documentProperties["UPDATED"] = true;
                database.PutRevision(new RevisionInternal(documentProperties), rev1.GetRevId(), false);

                documentProperties = new Dictionary<string, object>();
                var doc2Id = string.Format("doc2-{0}", docIdTimestamp);
                documentProperties["_id"] = doc2Id;
                documentProperties["baz"] = 666;
                documentProperties["fnord"] = true;

                database.PutRevision(new RevisionInternal(documentProperties), null, false);

                var doc2 = database.GetDocument(doc2Id);
                var doc2UnsavedRev = doc2.CreateRevision();
                var attachmentStream = GetAsset("attachment.png");
                doc2UnsavedRev.SetAttachment("attachment_testPusher.png", "image/png", attachmentStream);
                var doc2Rev = doc2UnsavedRev.Save();
                doc2UnsavedRev.Dispose();
                attachmentStream.Dispose();

                Assert.IsNotNull(doc2Rev);

                const bool continuous = false;
                var repl = database.CreatePushReplication(remote);
                repl.Continuous = continuous;
                if (!IsSyncGateway(remote)) {
                    repl.CreateTarget = true;
                }

                // Check the replication's properties:
                Assert.AreEqual(database, repl.LocalDatabase);
                Assert.AreEqual(remote, repl.RemoteUrl);
                Assert.IsFalse(repl.IsPull);
                Assert.IsFalse(repl.Continuous);
                Assert.IsNull(repl.Filter);
                Assert.IsNull(repl.FilterParams);
                Assert.IsNull(repl.DocIds);
                // TODO: CAssertNil(r1.headers); still not null!
                // Check that the replication hasn't started running:
                Assert.IsFalse(repl.IsRunning);
                Assert.AreEqual(ReplicationStatus.Stopped, repl.Status);
                Assert.AreEqual(0, repl.CompletedChangesCount);
                Assert.AreEqual(0, repl.ChangesCount);
                Assert.IsNull(repl.LastError);

                RunReplication(repl);

                // TODO: Verify the foloowing 2 asserts. ChangesCount and CompletedChangesCount
                // should already be reset when the replicator stopped.
                Assert.IsNull(repl.LastError);
                Assert.IsTrue(repl.ChangesCount >= 2);
                Assert.IsTrue(repl.CompletedChangesCount >= 2);


                remoteDb.VerifyDocumentExists(doc1Id);

                // Add doc3
                documentProperties = new Dictionary<string, object>();
                var doc3Id = string.Format("doc3-{0}", docIdTimestamp);
                var doc3 = database.GetDocument(doc3Id);
                documentProperties["bat"] = 677;
                doc3.PutProperties(documentProperties);

                // re-run push replication
                var repl2 = database.CreatePushReplication(remote);
                repl2.Continuous = continuous;
                if (!IsSyncGateway(remote)) {
                    repl2.CreateTarget = true;
                }

                var repl2CheckedpointId = repl2.RemoteCheckpointDocID();

                RunReplication(repl2);

                Assert.IsNull(repl2.LastError);

                Sleep(1000);

                // make sure trhe doc has been added
                remoteDb.VerifyDocumentExists(doc3Id);

                Assert.AreEqual(repl2.LastSequence, database.LastSequenceWithCheckpointId(repl2CheckedpointId));

                Sleep(2000);
                var json = GetRemoteDoc(remote, repl2CheckedpointId);
                var remoteLastSequence = (string)json["lastSequence"];
                Assert.AreEqual(repl2.LastSequence, remoteLastSequence);
            }
        }
示例#19
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        internal RevisionInternal LoadRevisionBody(RevisionInternal rev, DocumentContentOptions contentOptions)
        {
            if (rev.GetBody() != null && contentOptions == DocumentContentOptions.None && rev.GetSequence() != 0)
            {
                return rev;
            }

            if ((rev.GetDocId() == null) || (rev.GetRevId() == null))
            {
                Log.E(Database.Tag, "Error loading revision body");
                throw new CouchbaseLiteException(StatusCode.PreconditionFailed);
            }
            Cursor cursor = null;
            var result = new Status(StatusCode.NotFound);
            try
            {
                // TODO: on ios this query is:
                // TODO: "SELECT sequence, json FROM revs WHERE doc_id=@ AND revid=@ LIMIT 1"
                var sql = "SELECT sequence, json FROM revs, docs WHERE revid=? AND docs.docid=? AND revs.doc_id=docs.doc_id LIMIT 1";
                var args = new [] { rev.GetRevId(), rev.GetDocId() };

                cursor = StorageEngine.RawQuery(sql, CommandBehavior.SequentialAccess, args);
                if (cursor.MoveToNext())
                {
                    result.SetCode(StatusCode.Ok);
                    rev.SetSequence(cursor.GetLong(0));
                    ExpandStoredJSONIntoRevisionWithAttachments(cursor.GetBlob(1), rev, contentOptions);
                }
            }
            catch (SQLException e)
            {
                Log.E(Tag, "Error loading revision body", e);
                throw new CouchbaseLiteException(StatusCode.InternalServerError);
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
            }
            if (result.GetCode() == StatusCode.NotFound)
            {
                throw new CouchbaseLiteException(result.GetCode());
            }
            return rev;
        }
示例#20
0
        internal void NotifyChange(RevisionInternal rev, RevisionInternal winningRev, Uri source, bool inConflict)
        {
            var change = new DocumentChange(rev, winningRev.GetRevId(), inConflict, source);
            _changesToNotify.Add(change);

            if (!PostChangeNotifications())
            {
                // The notification wasn't posted yet, probably because a transaction is open.
                // But the Document, if any, needs to know right away so it can update its
                // currentRevision.
                var doc = DocumentCache.Get(change.DocumentId);
                if (doc != null) {
                    doc.RevisionAdded(change, false);
                }
            }
        }
        public static ICouchbaseResponseState RevsDiff(ICouchbaseListenerContext context)
        {
            // Collect all of the input doc/revision IDs as CBL_Revisions:
            var revs = new RevisionList();
            var body = context.BodyAs<Dictionary<string, object>>();
            if (body == null) {
                return context.CreateResponse(StatusCode.BadJson).AsDefaultState();
            }

            foreach (var docPair in body) {
                var revIDs = docPair.Value.AsList<string>();
                if (revIDs == null) {
                    return context.CreateResponse(StatusCode.BadParam).AsDefaultState();
                }

                foreach (var revID in revIDs) {
                    var rev = new RevisionInternal(docPair.Key, revID, false);
                    revs.Add(rev);
                }
            }

            return PerformLogicWithDatabase(context, true, db =>
            {
                var response = context.CreateResponse();
                // Look them up, removing the existing ones from revs:
                db.Storage.FindMissingRevisions(revs);

                // Return the missing revs in a somewhat different format:
                IDictionary<string, object> diffs = new Dictionary<string, object>();
                foreach(var rev in revs) {
                    var docId = rev.GetDocId();
                    IList<string> missingRevs = null;
                    if(!diffs.ContainsKey(docId)) {
                        missingRevs = new List<string>();
                        diffs[docId] = new Dictionary<string, IList<string>> { { "missing", missingRevs } };
                    } else {
                        missingRevs = ((Dictionary<string, IList<string>>)diffs[docId])["missing"];
                    }

                    missingRevs.Add(rev.GetRevId());
                }

                // Add the possible ancestors for each missing revision:
                foreach(var docPair in diffs) {
                    IDictionary<string, IList<string>> docInfo = (IDictionary<string, IList<string>>)docPair.Value;
                    int maxGen = 0;
                    string maxRevID = null;
                    foreach(var revId in docInfo["missing"]) {
                        var parsed = RevisionInternal.ParseRevId(revId);
                        if(parsed.Item1 > maxGen) {
                            maxGen = parsed.Item1;
                            maxRevID = revId;
                        }
                    }

                    var rev = new RevisionInternal(docPair.Key, maxRevID, false);
                    var ancestors = db.Storage.GetPossibleAncestors(rev, 0, false);
                    var ancestorList = ancestors == null ? null : ancestors.ToList();
                    if(ancestorList != null && ancestorList.Count > 0) {
                        docInfo["possible_ancestors"] = ancestorList;
                    }
                }

                response.JsonBody = new Body(diffs);
                return response;
            }).AsDefaultState();

        }
        public void LoadRevisionBody(RevisionInternal rev)
        {
            WithC4Document(rev.GetDocId(), rev.GetRevId(), true, false, doc => 
            {
                if(doc == null) {
                    throw new CouchbaseLiteException(StatusCode.NotFound);
                }

                rev.SetBody(new Body(doc->selectedRev.body));
            });
        }
        public void TestPusherDeletedDoc()
        {
            if (!Boolean.Parse((string)Runtime.Properties["replicationTestsEnabled"]))
            {
                Assert.Inconclusive("Replication tests disabled.");
                return;
            }

            using (var remoteDb = _sg.CreateDatabase(TempDbName())) {
                var remote = remoteDb.RemoteUri;
                var docIdTimestamp = Convert.ToString(Runtime.CurrentTimeMillis());

                // Create some documentsConvert
                var documentProperties = new Dictionary<string, object>();
                var doc1Id = string.Format("doc1-{0}", docIdTimestamp);
                documentProperties["_id"] = doc1Id;
                documentProperties["foo"] = 1;
                documentProperties["bar"] = false;

                var body = new Body(documentProperties);
                var rev1 = new RevisionInternal(body);
                rev1 = database.PutRevision(rev1, null, false);

                documentProperties["_rev"] = rev1.GetRevId();
                documentProperties["UPDATED"] = true;
                documentProperties["_deleted"] = true;
                database.PutRevision(new RevisionInternal(documentProperties), rev1.GetRevId(), false);

                var repl = database.CreatePushReplication(remote);
                if (!IsSyncGateway(remote)) {
                    ((Pusher)repl).CreateTarget = true;
                }

                RunReplication(repl);

                Assert.IsNull(repl.LastError);

                // make sure doc1 is deleted
                var replicationUrlTrailing = new Uri(string.Format("{0}/", remote));
                var pathToDoc = new Uri(replicationUrlTrailing, doc1Id);
                Log.D(Tag, "Send http request to " + pathToDoc);
                var httpRequestDoneSignal = new CountDownLatch(1);
                using (var httpclient = new HttpClient()) {
                    try {
                        var getDocResponse = httpclient.GetAsync(pathToDoc.ToString()).Result;
                        var statusLine = getDocResponse.StatusCode;
                        Log.D(ReplicationTest.Tag, "statusLine " + statusLine);
                        Assert.AreEqual(Couchbase.Lite.StatusCode.NotFound, statusLine.GetStatusCode());                        
                    }
                    catch (ProtocolViolationException e) {
                        Assert.IsNull(e, "Got ClientProtocolException: " + e.Message);
                    }
                    catch (IOException e) {
                        Assert.IsNull(e, "Got IOException: " + e.Message);
                    }
                    finally {
                        httpRequestDoneSignal.CountDown();
                    }
                    Log.D(Tag, "Waiting for http request to finish");
                    try {
                        httpRequestDoneSignal.Await(TimeSpan.FromSeconds(10));
                        Log.D(Tag, "http request finished");
                    }
                    catch (Exception e) {
                        Runtime.PrintStackTrace(e);
                    }
                }
            }
        }
        public void TestRevTree()
        {
            var rev = new RevisionInternal("MyDocId", "4-abcd", false);
            var revProperties = new Dictionary<string, object>();
            revProperties.Put("_id", rev.GetDocId());
            revProperties.Put("_rev", rev.GetRevId());
            revProperties["message"] = "hi";
            rev.SetProperties(revProperties);

            var revHistory = new List<string>();
            revHistory.AddItem(rev.GetRevId());
            revHistory.AddItem("3-abcd");
            revHistory.AddItem("2-abcd");
            revHistory.AddItem("1-abcd");
            database.ForceInsert(rev, revHistory, null);
            Assert.AreEqual(1, database.DocumentCount);

            VerifyHistory(database, rev, revHistory);
            var conflict = new RevisionInternal("MyDocId", "5-abcd", false);
            var conflictProperties = new Dictionary<string, object>();
            conflictProperties.Put("_id", conflict.GetDocId());
            conflictProperties.Put("_rev", conflict.GetRevId());
            conflictProperties["message"] = "yo";
            conflict.SetProperties(conflictProperties);
            
            var conflictHistory = new List<string>();
            conflictHistory.AddItem(conflict.GetRevId());
            conflictHistory.AddItem("4-bcde");
            conflictHistory.AddItem("3-bcde");
            conflictHistory.AddItem("2-abcd");
            conflictHistory.AddItem("1-abcd");
            database.ForceInsert(conflict, conflictHistory, null);
            Assert.AreEqual(1, database.DocumentCount);
            VerifyHistory(database, conflict, conflictHistory);
            
            // Add an unrelated document:
            var other = new RevisionInternal("AnotherDocID", "1-cdef", false);
            var otherProperties = new Dictionary<string, object>();
            otherProperties["language"] = "jp";
            other.SetProperties(otherProperties);
            var otherHistory = new List<string>();
            otherHistory.AddItem(other.GetRevId());
            database.ForceInsert(other, otherHistory, null);
            
            // Fetch one of those phantom revisions with no body:
            var rev2 = database.GetDocument(rev.GetDocId(), "2-abcd", 
                true);
            Assert.IsNull(rev2);

            // Make sure no duplicate rows were inserted for the common revisions:
            Assert.IsTrue(database.LastSequenceNumber <= 8);
            // Make sure the revision with the higher revID wins the conflict:
            var current = database.GetDocument(rev.GetDocId(), null, 
                true);
            Assert.AreEqual(conflict, current);
            
            // Get the _changes feed and verify only the winner is in it:
            var options = new ChangesOptions();
            var changes = database.ChangesSince(0, options, null, null);
            var expectedChanges = new RevisionList();
            expectedChanges.AddItem(conflict);
            expectedChanges.AddItem(other);
            Assert.AreEqual(expectedChanges, changes);
            options.IncludeConflicts = true;
            changes = database.ChangesSince(0, options, null, null);
            expectedChanges = new RevisionList();
            expectedChanges.AddItem(rev);
            expectedChanges.AddItem(conflict);
            expectedChanges.AddItem(other);
            var expectedChangesAlt = new RevisionList();
            expectedChangesAlt.AddItem(conflict);
            expectedChangesAlt.AddItem(rev);
            expectedChangesAlt.AddItem(other);
            Assert.IsTrue(expectedChanges.SequenceEqual(changes) || expectedChangesAlt.SequenceEqual(changes));
        }
示例#25
0
        internal RevisionInternal RevisionByLoadingBody(RevisionInternal rev, Status outStatus)
        {
            // First check for no-op -- if we just need the default properties and already have them:
            if (rev.GetSequence() != 0) {
                var props = rev.GetProperties();
                if (props != null && props.ContainsKey("_rev") && props.ContainsKey("_id")) {
                    if (outStatus != null) {
                        outStatus.Code = StatusCode.Ok;
                    }

                    return rev;
                }
            }

            RevisionInternal nuRev = rev.CopyWithDocID(rev.GetDocId(), rev.GetRevId());
            try {
                LoadRevisionBody(nuRev);
            } catch(CouchbaseLiteException e) {
                if (outStatus != null) {
                    outStatus.Code = e.CBLStatus.Code;
                }

                nuRev = null;
            }

            return nuRev;
        }
示例#26
0
        /// <summary>Inserts an already-existing revision replicated from a remote sqliteDb.</summary>
        /// <remarks>
        /// Inserts an already-existing revision replicated from a remote sqliteDb.
        /// It must already have a revision ID. This may create a conflict! The revision's history must be given; ancestor revision IDs that don't already exist locally will create phantom revisions with no content.
        /// </remarks>
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        internal void ForceInsert(RevisionInternal rev, IList<string> revHistory, Uri source)
        {
            var inConflict = false;
            var docId = rev.GetDocId();
            var revId = rev.GetRevId();
            if (!IsValidDocumentId(docId) || (revId == null))
            {
                throw new CouchbaseLiteException(StatusCode.BadRequest);
            }
            int historyCount = 0;
            if (revHistory != null)
            {
                historyCount = revHistory.Count;
            }
            if (historyCount == 0)
            {
                revHistory = new AList<string>();
                revHistory.AddItem(revId);
                historyCount = 1;
            }
            else
            {
                if (!revHistory[0].Equals(rev.GetRevId()))
                {
                    throw new CouchbaseLiteException(StatusCode.BadRequest);
                }
            }
            bool success = false;
            BeginTransaction();
            try
            {
                // First look up all locally-known revisions of this document:
                long docNumericID = GetOrInsertDocNumericID(docId);
                RevisionList localRevs = GetAllRevisionsOfDocumentID(docId, docNumericID, false);
                if (localRevs == null)
                {
                    throw new CouchbaseLiteException(StatusCode.InternalServerError);
                }

                IList<bool> outIsDeleted = new AList<bool>();
                IList<bool> outIsConflict = new AList<bool>();
                bool oldWinnerWasDeletion = false;
                string oldWinningRevID = WinningRevIDOfDoc(docNumericID, outIsDeleted, outIsConflict
                );
                if (outIsDeleted.Count > 0)
                {
                    oldWinnerWasDeletion = true;
                }
                if (outIsConflict.Count > 0)
                {
                    inConflict = true;
                }

                // Walk through the remote history in chronological order, matching each revision ID to
                // a local revision. When the list diverges, start creating blank local revisions to fill
                // in the local history:
                long sequence = 0;
                long localParentSequence = 0;
                for (int i = revHistory.Count - 1; i >= 0; --i)
                {
                    revId = revHistory[i];
                    RevisionInternal localRev = localRevs.RevWithDocIdAndRevId(docId, revId);
                    if (localRev != null)
                    {
                        // This revision is known locally. Remember its sequence as the parent of the next one:
                        sequence = localRev.GetSequence();
                        Debug.Assert((sequence > 0));
                        localParentSequence = sequence;
                    }
                    else
                    {
                        // This revision isn't known, so add it:
                        RevisionInternal newRev;
                        IEnumerable<Byte> data = null;
                        bool current = false;
                        if (i == 0)
                        {
                            // Hey, this is the leaf revision we're inserting:
                            newRev = rev;
                            if (!rev.IsDeleted())
                            {
                                data = EncodeDocumentJSON(rev);
                                if (data == null)
                                {
                                    throw new CouchbaseLiteException(StatusCode.BadRequest);
                                }
                            }
                            current = true;
                        }
                        else
                        {
                            // It's an intermediate parent, so insert a stub:
                            newRev = new RevisionInternal(docId, revId, false, this);
                        }

                        // Insert it:
                        sequence = InsertRevision(newRev, docNumericID, sequence, current, (GetAttachmentsFromRevision(newRev).Count > 0), data);
                        if (sequence <= 0)
                        {
                            throw new CouchbaseLiteException(StatusCode.InternalServerError);
                        }
                        if (i == 0)
                        {
                            // Write any changed attachments for the new revision. As the parent sequence use
                            // the latest local revision (this is to copy attachments from):
                            var attachments = GetAttachmentsFromRevision(rev);
                            if (attachments != null)
                            {
                                ProcessAttachmentsForRevision(attachments, rev, localParentSequence);
                                StubOutAttachmentsInRevision(attachments, rev);
                            }
                        }
                    }
                }
                // Mark the latest local rev as no longer current:
                if (localParentSequence > 0 && localParentSequence != sequence)
                {
                    ContentValues args = new ContentValues();
                    args["current"] = 0;
                    string[] whereArgs = new string[] { Convert.ToString(localParentSequence) };
                    try
                    {
                        var numRowsChanged = StorageEngine.Update("revs", args, "sequence=?", whereArgs);
                        if (numRowsChanged == 0)
                        {
                            inConflict = true;
                        }
                    }
                    catch (Exception)
                    {
                        throw new CouchbaseLiteException(StatusCode.InternalServerError);
                    }
                }

                var winningRev = Winner(docNumericID, oldWinningRevID, oldWinnerWasDeletion, rev);
                success = true;
                NotifyChange(rev, winningRev, source, inConflict);
            }
            catch (SQLException)
            {
                throw new CouchbaseLiteException(StatusCode.InternalServerError);
            }
            finally
            {
                EndTransaction(success);
            }
        }
示例#27
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException">When attempting to add an invalid revision</exception>
        internal void ForceInsert(RevisionInternal inRev, IList<string> revHistory, Uri source)
        {
            if (revHistory == null) {
                revHistory = new List<string>(0);
            }

            var rev = inRev.CopyWithDocID(inRev.GetDocId(), inRev.GetRevId());
            rev.SetSequence(0);
            string revID = rev.GetRevId();
            if (!IsValidDocumentId(rev.GetDocId()) || revID == null) {
                throw new CouchbaseLiteException(StatusCode.BadId);
            }

            if (revHistory.Count == 0) {
                revHistory.Add(revID);
            } else if (revID != revHistory[0]) {
                throw new CouchbaseLiteException(StatusCode.BadId);
            }

            if (inRev.GetAttachments() != null) {
                var updatedRev = inRev.CopyWithDocID(inRev.GetDocId(), inRev.GetRevId());
                string prevRevID = revHistory.Count >= 2 ? revHistory[1] : null;
                Status status = new Status();
                if (!ProcessAttachmentsForRevision(updatedRev, prevRevID, status)) {
                    throw new CouchbaseLiteException(status.Code);
                }

                inRev = updatedRev;
            }

            StoreValidation validationBlock = null;
            if (Shared != null && Shared.HasValues("validation", Name)) {
                validationBlock = ValidateRevision;
            }

            var insertStatus = Storage.ForceInsert(inRev, revHistory, validationBlock, source);
            if(insertStatus.IsError) {
                throw new CouchbaseLiteException(insertStatus.Code);
            }
        }
示例#28
0
        internal RevisionInternal GetParentRevision(RevisionInternal rev)
        {
            // First get the parent's sequence:
            var seq = rev.GetSequence();
            if (seq > 0)
            {
                seq = LongForQuery("SELECT parent FROM revs WHERE sequence=?", new [] { Convert.ToString(seq) });
            }
            else
            {
                var docNumericID = GetDocNumericID(rev.GetDocId());
                if (docNumericID <= 0)
                {
                    return null;
                }
                var args = new [] { Convert.ToString(docNumericID), rev.GetRevId() };
                seq = LongForQuery("SELECT parent FROM revs WHERE doc_id=? and revid=?", args);
            }
            if (seq == 0)
            {
                return null;
            }

            // Now get its revID and deletion status:
            RevisionInternal result = null;
            var queryArgs = new [] { Convert.ToString(seq) };
            var queryString = "SELECT revid, deleted FROM revs WHERE sequence=?";

            Cursor cursor = null;
            try
            {
                cursor = StorageEngine.RawQuery(queryString, queryArgs);
                if (cursor.MoveToNext())
                {
                    string revId = cursor.GetString(0);
                    bool deleted = (cursor.GetInt(1) > 0);
                    result = new RevisionInternal(rev.GetDocId(), revId, deleted, this);
                    result.SetSequence(seq);
                }
            }
            finally
            {
                cursor.Close();
            }
            return result;
        }
示例#29
0
		/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
		public virtual void TestValidations()
		{
            Database.ValidateDelegate validator = (Revision newRevision, ValidationContext context)=>
            {
                NUnit.Framework.Assert.IsNotNull(newRevision);
                NUnit.Framework.Assert.IsNotNull(context);
                NUnit.Framework.Assert.IsTrue(newRevision.Properties != null || newRevision.
                    IsDeletion);
                this._enclosing.validationCalled = true;
                bool hoopy = newRevision.IsDeletion || (newRevision.Properties.Get("towel"
                ) != null);
                Log.V(ValidationsTest.Tag, string.Format("--- Validating %s --> %b", newRevision.
                    Properties, hoopy));
                if (!hoopy)
                {
                    context.Reject("Where's your towel?");
                }
                return hoopy;
            };
			database.SetValidation("hoopy", validator);
			// POST a valid new document:
			IDictionary<string, object> props = new Dictionary<string, object>();
			props["name"] = "Zaphod Beeblebrox";
			props["towel"] = "velvet";
			RevisionInternal rev = new RevisionInternal(props, database);
			Status status = new Status();
			validationCalled = false;
			rev = database.PutRevision(rev, null, false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode());
			// PUT a valid update:
			props["head_count"] = 3;
			rev.SetProperties(props);
			validationCalled = false;
			rev = database.PutRevision(rev, rev.GetRevId(), false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode());
			// PUT an invalid update:
			Sharpen.Collections.Remove(props, "towel");
			rev.SetProperties(props);
			validationCalled = false;
			bool gotExpectedError = false;
			try
			{
				rev = database.PutRevision(rev, rev.GetRevId(), false, status);
			}
			catch (CouchbaseLiteException e)
			{
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
			}
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
			// POST an invalid new document:
			props = new Dictionary<string, object>();
			props["name"] = "Vogon";
			props["poetry"] = true;
			rev = new RevisionInternal(props, database);
			validationCalled = false;
			gotExpectedError = false;
			try
			{
				rev = database.PutRevision(rev, null, false, status);
			}
			catch (CouchbaseLiteException e)
			{
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
			}
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
			// PUT a valid new document with an ID:
			props = new Dictionary<string, object>();
			props["_id"] = "ford";
			props["name"] = "Ford Prefect";
			props["towel"] = "terrycloth";
			rev = new RevisionInternal(props, database);
			validationCalled = false;
			rev = database.PutRevision(rev, null, false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.AreEqual("ford", rev.GetDocId());
			// DELETE a document:
			rev = new RevisionInternal(rev.GetDocId(), rev.GetRevId(), true, database);
			NUnit.Framework.Assert.IsTrue(rev.IsDeleted());
			validationCalled = false;
			rev = database.PutRevision(rev, rev.GetRevId(), false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
			// PUT an invalid new document:
			props = new Dictionary<string, object>();
			props["_id"] = "petunias";
			props["name"] = "Pot of Petunias";
			rev = new RevisionInternal(props, database);
			validationCalled = false;
			gotExpectedError = false;
			try
			{
				rev = database.PutRevision(rev, null, false, status);
			}
			catch (CouchbaseLiteException e)
			{
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
			}
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
		}
示例#30
0
        /// <summary>Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev.
        ///     </summary>
        /// <remarks>
        /// Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev.
        /// Rev must already have its revID and sequence properties set.
        /// </remarks>
        internal IDictionary<String, Object> ExtraPropertiesForRevision(RevisionInternal rev, DocumentContentOptions contentOptions)
        {
            var docId = rev.GetDocId();
            var revId = rev.GetRevId();

            var sequenceNumber = rev.GetSequence();

            Debug.Assert((revId != null));
            Debug.Assert((sequenceNumber > 0));

            // Get attachment metadata, and optionally the contents:
            IDictionary<string, object> attachmentsDict = null;

            if (!contentOptions.HasFlag(DocumentContentOptions.NoAttachments))
            {
                attachmentsDict = GetAttachmentsDictForSequenceWithContent (sequenceNumber, contentOptions);
            }
            // Get more optional stuff to put in the properties:
            //OPT: This probably ends up making redundant SQL queries if multiple options are enabled.
            var localSeq = -1L;
            if (contentOptions.HasFlag(DocumentContentOptions.IncludeLocalSeq))
            {
                localSeq = sequenceNumber;
            }
            IDictionary<string, object> revHistory = null;
            if (contentOptions.HasFlag(DocumentContentOptions.IncludeRevs))
            {
                revHistory = GetRevisionHistoryDict(rev);
            }
            IList<object> revsInfo = null;
            if (contentOptions.HasFlag(DocumentContentOptions.IncludeRevsInfo))
            {
                revsInfo = new AList<object>();
                var revHistoryFull = GetRevisionHistory(rev);
                foreach (RevisionInternal historicalRev in revHistoryFull)
                {
                    var revHistoryItem = new Dictionary<string, object>();
                    var status = "available";
                    if (historicalRev.IsDeleted())
                    {
                        status = "deleted";
                    }

                    if (historicalRev.IsMissing())
                    {
                        status = "missing";
                    }
                    revHistoryItem.Put("rev", historicalRev.GetRevId());
                    revHistoryItem["status"] = status;
                    revsInfo.AddItem(revHistoryItem);
                }
            }
            IList<string> conflicts = null;
            if (contentOptions.HasFlag(DocumentContentOptions.IncludeConflicts))
            {
                var revs = GetAllRevisionsOfDocumentID(docId, true);
                if (revs.Count > 1)
                {
                    conflicts = new AList<string>();
                    foreach (RevisionInternal savedRev in revs)
                    {
                        if (!(savedRev.Equals(rev) || savedRev.IsDeleted()))
                        {
                            conflicts.AddItem(savedRev.GetRevId());
                        }
                    }
                }
            }

            var result = new Dictionary<string, object>();
            result["_id"] = docId;
            result["_rev"] = revId;

            if (rev.IsDeleted())
            {
                result["_deleted"] = true;
            }
            if (attachmentsDict != null)
            {
                result["_attachments"] = attachmentsDict;
            }
            if (localSeq > -1)
            {
                result["_local_seq"] = localSeq;
            }
            if (revHistory != null)
            {
                result["_revisions"] = revHistory;
            }
            if (revsInfo != null)
            {
                result["_revs_info"] = revsInfo;
            }
            if (conflicts != null)
            {
                result["_conflicts"] = conflicts;
            }
            return result;
        }