Exemplo n.º 1
0
        private bool UploadMultipartRevision(RevisionInternal revision)
        {
            MultipartEntity multiPart             = null;
            IDictionary <string, object> revProps = revision.GetProperties();
            // TODO: refactor this to
            IDictionary <string, object> attachments = (IDictionary <string, object>)revProps.Get
                                                           ("_attachments");

            foreach (string attachmentKey in attachments.Keys)
            {
                IDictionary <string, object> attachment = (IDictionary <string, object>)attachments
                                                          .Get(attachmentKey);
                if (attachment.ContainsKey("follows"))
                {
                    if (multiPart == null)
                    {
                        multiPart = new MultipartEntity();
                        try
                        {
                            string   json        = Manager.GetObjectMapper().WriteValueAsString(revProps);
                            Encoding utf8charset = Sharpen.Extensions.GetEncoding("UTF-8");
                            multiPart.AddPart("param1", new StringBody(json, "application/json", utf8charset)
                                              );
                        }
                        catch (IOException e)
                        {
                            throw new ArgumentException(e);
                        }
                    }
                    BlobStore   blobStore    = this.db.GetAttachments();
                    string      base64Digest = (string)attachment.Get("digest");
                    BlobKey     blobKey      = new BlobKey(base64Digest);
                    InputStream inputStream  = blobStore.BlobStreamForKey(blobKey);
                    if (inputStream == null)
                    {
                        Log.W(Log.TagSync, "Unable to find blob file for blobKey: %s - Skipping upload of multipart revision."
                              , blobKey);
                        multiPart = null;
                    }
                    else
                    {
                        string contentType = null;
                        if (attachment.ContainsKey("content_type"))
                        {
                            contentType = (string)attachment.Get("content_type");
                        }
                        else
                        {
                            if (attachment.ContainsKey("content-type"))
                            {
                                Log.W(Log.TagSync, "Found attachment that uses content-type" + " field name instead of content_type (see couchbase-lite-android"
                                      + " issue #80): %s", attachment);
                            }
                        }
                        multiPart.AddPart(attachmentKey, new InputStreamBody(inputStream, contentType, attachmentKey
                                                                             ));
                    }
                }
            }
            if (multiPart == null)
            {
                return(false);
            }
            string path = string.Format("/%s?new_edits=false", revision.GetDocId());

            Log.D(Log.TagSync, "Uploading multipart request.  Revision: %s", revision);
            AddToChangesCount(1);
            Log.V(Log.TagSync, "%s | %s: uploadMultipartRevision() calling asyncTaskStarted()"
                  , this, Sharpen.Thread.CurrentThread());
            AsyncTaskStarted();
            SendAsyncMultipartRequest("PUT", path, multiPart, new _RemoteRequestCompletionBlock_542
                                          (this, revision));
            // Server doesn't like multipart, eh? Fall back to JSON.
            //status 415 = "bad_content_type"
            return(true);
        }
Exemplo n.º 2
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public virtual void TestViewIndex()
        {
            int numTimesMapFunctionInvoked     = 0;
            IDictionary <string, object> dict1 = new Dictionary <string, object>();

            dict1["key"] = "one";
            IDictionary <string, object> dict2 = new Dictionary <string, object>();

            dict2["key"] = "two";
            IDictionary <string, object> dict3 = new Dictionary <string, object>();

            dict3["key"] = "three";
            IDictionary <string, object> dictX = new Dictionary <string, object>();

            dictX["clef"] = "quatre";
            RevisionInternal rev1 = PutDoc(database, dict1);
            RevisionInternal rev2 = PutDoc(database, dict2);
            RevisionInternal rev3 = PutDoc(database, dict3);

            PutDoc(database, dictX);
            View view            = database.GetView("aview");
            var  numTimesInvoked = 0;

            MapDelegate mapBlock = (IDictionary <string, object> document, EmitDelegate emitter) =>
            {
                numTimesInvoked += 1;
                NUnit.Framework.Assert.IsNotNull(document["_id"]);
                NUnit.Framework.Assert.IsNotNull(document["_rev"]);
                if (document["key"] != null)
                {
                    emitter(document["key"], null);
                }
            };

            view.SetMap(mapBlock, "1");
            NUnit.Framework.Assert.AreEqual(1, view.GetViewId());
            NUnit.Framework.Assert.IsTrue(view.IsStale());
            view.UpdateIndex();
            IList <IDictionary <string, object> > dumpResult = view.Dump();

            Log.V(Tag, "View dump: " + dumpResult);
            NUnit.Framework.Assert.AreEqual(3, dumpResult.Count);
            NUnit.Framework.Assert.AreEqual("\"one\"", dumpResult[0]["key"]);
            NUnit.Framework.Assert.AreEqual(1, dumpResult[0]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"two\"", dumpResult[2]["key"]);
            NUnit.Framework.Assert.AreEqual(2, dumpResult[2]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"three\"", dumpResult[1]["key"]);
            NUnit.Framework.Assert.AreEqual(3, dumpResult[1]["seq"]);
            //no-op reindex
            NUnit.Framework.Assert.IsFalse(view.IsStale());
            view.UpdateIndex();
            // Now add a doc and update a doc:
            RevisionInternal threeUpdated = new RevisionInternal(rev3.GetDocId(), rev3.GetRevId
                                                                     (), false, database);

            numTimesMapFunctionInvoked = mapBlock.GetNumTimesInvoked();
            IDictionary <string, object> newdict3 = new Dictionary <string, object>();

            newdict3["key"] = "3hree";
            threeUpdated.SetProperties(newdict3);
            Status status = new Status();

            rev3 = database.PutRevision(threeUpdated, rev3.GetRevId(), false, status);
            NUnit.Framework.Assert.IsTrue(status.IsSuccessful());
            // Reindex again:
            NUnit.Framework.Assert.IsTrue(view.IsStale());
            view.UpdateIndex();
            // Make sure the map function was only invoked one more time (for the document that was added)
            NUnit.Framework.Assert.AreEqual(mapBlock.GetNumTimesInvoked(), numTimesMapFunctionInvoked
                                            + 1);
            IDictionary <string, object> dict4 = new Dictionary <string, object>();

            dict4["key"] = "four";
            RevisionInternal rev4       = PutDoc(database, dict4);
            RevisionInternal twoDeleted = new RevisionInternal(rev2.GetDocId(), rev2.GetRevId
                                                                   (), true, database);

            database.PutRevision(twoDeleted, rev2.GetRevId(), false, status);
            NUnit.Framework.Assert.IsTrue(status.IsSuccessful());
            // Reindex again:
            NUnit.Framework.Assert.IsTrue(view.IsStale());
            view.UpdateIndex();
            dumpResult = view.Dump();
            Log.V(Tag, "View dump: " + dumpResult);
            NUnit.Framework.Assert.AreEqual(3, dumpResult.Count);
            NUnit.Framework.Assert.AreEqual("\"one\"", dumpResult[2]["key"]);
            NUnit.Framework.Assert.AreEqual(1, dumpResult[2]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"3hree\"", dumpResult[0]["key"]);
            NUnit.Framework.Assert.AreEqual(5, dumpResult[0]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"four\"", dumpResult[1]["key"]);
            NUnit.Framework.Assert.AreEqual(6, dumpResult[1]["seq"]);
            // Now do a real query:
            IList <QueryRow> rows = view.QueryWithOptions(null);

            NUnit.Framework.Assert.AreEqual(3, rows.Count);
            NUnit.Framework.Assert.AreEqual("one", rows[2].Key);
            NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rows[2].DocumentId);
            NUnit.Framework.Assert.AreEqual("3hree", rows[0].Key);
            NUnit.Framework.Assert.AreEqual(rev3.GetDocId(), rows[0].DocumentId);
            NUnit.Framework.Assert.AreEqual("four", rows[1].Key);
            NUnit.Framework.Assert.AreEqual(rev4.GetDocId(), rows[1].DocumentId);
            view.DeleteIndex();
        }
Exemplo n.º 3
0
        private bool UploadMultipartRevision(RevisionInternal revision)
        {
            MultipartFormDataContent multiPart = null;
            var revProps = revision.GetProperties();

            revProps.Put("_revisions", LocalDatabase.GetRevisionHistoryDict(revision));

            var attachments = (IDictionary <string, object>)revProps.Get("_attachments");

            foreach (var attachmentKey in attachments.Keys)
            {
                var attachment = (IDictionary <String, Object>)attachments.Get(attachmentKey);
                if (attachment.ContainsKey("follows"))
                {
                    if (multiPart == null)
                    {
                        multiPart = new MultipartFormDataContent();
                        try
                        {
                            var json        = Manager.GetObjectMapper().WriteValueAsString(revProps);
                            var utf8charset = Encoding.UTF8;
                            multiPart.Add(new StringContent(json, utf8charset, "application/json"), "param1");
                        }
                        catch (IOException e)
                        {
                            throw new ArgumentException("Not able to serialize revision properties into a multipart request content.", e);
                        }
                    }

                    var blobStore    = LocalDatabase.Attachments;
                    var base64Digest = (string)attachment.Get("digest");

                    var blobKey     = new BlobKey(base64Digest);
                    var inputStream = blobStore.BlobStreamForKey(blobKey);

                    if (inputStream == null)
                    {
                        Log.W(Tag, "Unable to find blob file for blobKey: " + blobKey + " - Skipping upload of multipart revision.");
                        multiPart = null;
                    }
                    else
                    {
                        string contentType = null;
                        if (attachment.ContainsKey("content_type"))
                        {
                            contentType = (string)attachment.Get("content_type");
                        }
                        else
                        {
                            if (attachment.ContainsKey("content-type"))
                            {
                                var message = string.Format("Found attachment that uses content-type"
                                                            + " field name instead of content_type (see couchbase-lite-android"
                                                            + " issue #80): " + attachment);
                                Log.W(Tag, message);
                            }
                        }

                        var content = new StreamContent(inputStream);
                        content.Headers.ContentType = new MediaTypeHeaderValue(contentType);

                        multiPart.Add(content, attachmentKey);
                    }
                }
            }

            if (multiPart == null)
            {
                return(false);
            }

            var path = string.Format("/{0}?new_edits=false", revision.GetDocId());

            // TODO: need to throttle these requests
            Log.D(Tag, "Uploadeding multipart request.  Revision: " + revision);
            Log.D(Tag, this + "|" + Thread.CurrentThread() + ": uploadMultipartRevision() calling asyncTaskStarted()");

            AsyncTaskStarted();

            SendAsyncMultipartRequest(HttpMethod.Put, path, multiPart, (result, e) => {
                try
                {
                    if (e != null)
                    {
                        Log.E(Tag, "Exception uploading multipart request", e);
                        LastError = e;
                        RevisionFailed();
                    }
                    else
                    {
                        Log.D(Tag, "Uploaded multipart request.  Result: " + result);
                    }
                }
                finally
                {
                    AsyncTaskFinished(1);
                }
            });
            return(true);
        }
Exemplo n.º 4
0
        /// <summary>Fetches the contents of a revision from the remote db, including its parent revision ID.
        ///     </summary>
        /// <remarks>
        /// Fetches the contents of a revision from the remote db, including its parent revision ID.
        /// The contents are stored into rev.properties.
        /// </remarks>
        private void PullRemoteRevision(RevisionInternal rev)
        {
            Log.D(TAG, "PullRemoteRevision with rev: {0}", rev);
            _httpConnectionCount++;

            // Construct a query. We want the revision history, and the bodies of attachments that have
            // been added since the latest revisions we have locally.
            // See: http://wiki.apache.org/couchdb/HTTP_Document_API#Getting_Attachments_With_a_Document
            var path      = new StringBuilder("/" + Uri.EscapeUriString(rev.GetDocId()) + "?rev=" + Uri.EscapeUriString(rev.GetRevId()) + "&revs=true&attachments=true");
            var tmp       = LocalDatabase.Storage.GetPossibleAncestors(rev, MAX_ATTS_SINCE, true);
            var knownRevs = tmp == null ? null : tmp.ToList();

            if (knownRevs == null)
            {
                //this means something is wrong, possibly the replicator has shut down
                _httpConnectionCount--;
                return;
            }

            if (knownRevs.Count > 0)
            {
                path.Append("&atts_since=");
                path.Append(JoinQuotedEscaped(knownRevs));
            }

            //create a final version of this variable for the log statement inside
            //FIXME find a way to avoid this
            var pathInside = path.ToString();

            SendAsyncMultipartDownloaderRequest(HttpMethod.Get, pathInside, null, LocalDatabase, (result, e) =>
            {
                // OK, now we've got the response revision:
                Log.D(TAG, "PullRemoteRevision got response for rev: " + rev);

                if (e != null)
                {
                    Log.E(TAG, "Error pulling remote revision", e);
                    LastError = e;
                    RevisionFailed();
                    Log.D(TAG, "PullRemoteRevision updating completedChangesCount from " +
                          CompletedChangesCount + " -> " + (CompletedChangesCount + 1)
                          + " due to error pulling remote revision");
                    SafeIncrementCompletedChangesCount();
                }
                else
                {
                    var properties = result.AsDictionary <string, object>();
                    var gotRev     = new PulledRevision(properties);
                    gotRev.SetSequence(rev.GetSequence());
                    Log.D(TAG, "PullRemoteRevision add rev: " + gotRev + " to batcher");

                    if (_downloadsToInsert != null)
                    {
                        _downloadsToInsert.QueueObject(gotRev);
                    }
                    else
                    {
                        Log.E(TAG, "downloadsToInsert is null");
                    }
                }

                // Note that we've finished this task; then start another one if there
                // are still revisions waiting to be pulled:
                --_httpConnectionCount;
                PullRemoteRevisions();
            });
        }
 /// <summary>Constructor</summary>
 internal SavedRevision(Database database, RevisionInternal revision)
     : this(database.GetDocument(revision.GetDocId()), revision)
 {
 }
Exemplo n.º 6
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public virtual void TestRevTree()
        {
            RevisionInternal             rev           = new RevisionInternal("MyDocId", "4-foxy", false, database);
            IDictionary <string, object> revProperties = new Dictionary <string, object>();

            revProperties.Put("_id", rev.GetDocId());
            revProperties.Put("_rev", rev.GetRevId());
            revProperties["message"] = "hi";
            rev.SetProperties(revProperties);
            IList <string> revHistory = new AList <string>();

            revHistory.AddItem(rev.GetRevId());
            revHistory.AddItem("3-thrice");
            revHistory.AddItem("2-too");
            revHistory.AddItem("1-won");
            database.ForceInsert(rev, revHistory, null);
            NUnit.Framework.Assert.AreEqual(1, database.DocumentCount);
            VerifyHistory(database, rev, revHistory);
            RevisionInternal conflict = new RevisionInternal("MyDocId", "5-epsilon", false, database
                                                             );
            IDictionary <string, object> conflictProperties = new Dictionary <string, object>();

            conflictProperties.Put("_id", conflict.GetDocId());
            conflictProperties.Put("_rev", conflict.GetRevId());
            conflictProperties["message"] = "yo";
            conflict.SetProperties(conflictProperties);
            IList <string> conflictHistory = new AList <string>();

            conflictHistory.AddItem(conflict.GetRevId());
            conflictHistory.AddItem("4-delta");
            conflictHistory.AddItem("3-gamma");
            conflictHistory.AddItem("2-too");
            conflictHistory.AddItem("1-won");
            database.ForceInsert(conflict, conflictHistory, null);
            NUnit.Framework.Assert.AreEqual(1, database.DocumentCount);
            VerifyHistory(database, conflict, conflictHistory);
            // Add an unrelated document:
            RevisionInternal other = new RevisionInternal("AnotherDocID", "1-ichi", false, database
                                                          );
            IDictionary <string, object> otherProperties = new Dictionary <string, object>();

            otherProperties["language"] = "jp";
            other.SetProperties(otherProperties);
            IList <string> otherHistory = new AList <string>();

            otherHistory.AddItem(other.GetRevId());
            database.ForceInsert(other, otherHistory, null);
            // Fetch one of those phantom revisions with no body:
            RevisionInternal rev2 = database.GetDocumentWithIDAndRev(rev.GetDocId(), "2-too",
                                                                     EnumSet.NoneOf <TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(rev.GetDocId(), rev2.GetDocId());
            NUnit.Framework.Assert.AreEqual("2-too", rev2.GetRevId());
            //Assert.assertNull(rev2.getContent());
            // Make sure no duplicate rows were inserted for the common revisions:
            NUnit.Framework.Assert.AreEqual(8, database.GetLastSequenceNumber());
            // Make sure the revision with the higher revID wins the conflict:
            RevisionInternal current = database.GetDocumentWithIDAndRev(rev.GetDocId(), null,
                                                                        EnumSet.NoneOf <TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(conflict, current);
            // Get the _changes feed and verify only the winner is in it:
            ChangesOptions options         = new ChangesOptions();
            RevisionList   changes         = database.ChangesSince(0, options, null);
            RevisionList   expectedChanges = new RevisionList();

            expectedChanges.AddItem(conflict);
            expectedChanges.AddItem(other);
            NUnit.Framework.Assert.AreEqual(changes, expectedChanges);
            options.SetIncludeConflicts(true);
            changes         = database.ChangesSince(0, options, null);
            expectedChanges = new RevisionList();
            expectedChanges.AddItem(rev);
            expectedChanges.AddItem(conflict);
            expectedChanges.AddItem(other);
            NUnit.Framework.Assert.AreEqual(changes, expectedChanges);
        }
Exemplo n.º 7
0
        public void TestCRUDOperations()
        {
            database.Changed += (sender, e) => {
                var changes = e.Changes.ToList();
                foreach (DocumentChange change in changes)
                {
                    var rev = change.AddedRevision;
                    Assert.IsNotNull(rev);
                    Assert.IsNotNull(rev.GetDocId());
                    Assert.IsNotNull(rev.GetRevId());
                    Assert.AreEqual(rev.GetDocId(), rev.GetProperties()["_id"]);
                    Assert.AreEqual(rev.GetRevId(), rev.GetProperties()["_rev"]);
                }
            };

            var privateUUID = database.PrivateUUID();
            var publicUUID  = database.PublicUUID();

            Log.V(Tag, "DB private UUID = '" + privateUUID + "', public UUID = '" +
                  publicUUID + "'");
            Assert.IsTrue(privateUUID.Length >= 20);
            Assert.IsTrue(publicUUID.Length >= 20);

            //create a document
            var documentProperties = new Dictionary <string, object>();

            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;
            documentProperties["baz"] = "touch";

            var body = new Body(documentProperties);
            var rev1 = new RevisionInternal(body);

            var status = new Status();

            rev1 = database.PutRevision(rev1, null, false, status);
            Log.V(Tag, "Created " + rev1);
            Assert.IsTrue(rev1.GetDocId().Length >= 10);
            Assert.IsTrue(rev1.GetRevId().StartsWith("1-"));

            //read it back
            var readRev = database.GetDocument(rev1.GetDocId(), null,
                                               true);

            Assert.IsNotNull(readRev);

            var userReadRevProps = UserProperties(readRev.GetProperties());
            var userBodyProps    = UserProperties(body.GetProperties());

            Assert.AreEqual(userReadRevProps.Count, userBodyProps.Count);
            foreach (var key in userReadRevProps.Keys)
            {
                Assert.AreEqual(userReadRevProps[key], userBodyProps[key]);
            }

            //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.PutRevision(rev2, rev1.GetRevId(), false, status);
            Log.V(Tag, "Updated " + rev1);
            Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId());
            Assert.IsTrue(rev2.GetRevId().StartsWith("2-"));

            //read it back
            readRev = database.GetDocument(rev2.GetDocId(), null,
                                           true);
            Assert.IsNotNull(readRev);
            Assert.AreEqual(UserProperties(readRev.GetProperties()), UserProperties
                                (body.GetProperties()));

            // Try to update the first rev, which should fail:
            database.PutRevision(rev2input, rev1.GetRevId(), false, status);
            Assert.AreEqual(StatusCode.Conflict, status.Code);

            // Check the changes feed, with and without filters:
            var changeRevisions = database.ChangesSince(0, null, null, null);

            Log.V(Tag, "Changes = " + changeRevisions);
            Assert.AreEqual(1, changeRevisions.Count);

            changeRevisions = database.ChangesSince(0, null,
                                                    (revision, items) => "updated!".Equals(revision.Properties.Get("status")), null);
            Assert.AreEqual(1, changeRevisions.Count);

            changeRevisions = database.ChangesSince(0, null,
                                                    (revision, items) => "not updated!".Equals(revision.Properties.Get("status")), null);
            Assert.AreEqual(0, changeRevisions.Count);

            // Delete it:
            var revD = new RevisionInternal(rev2.GetDocId(), null, true);
            RevisionInternal revResult = null;

            revResult = database.PutRevision(revD, null, false, status);
            Assert.AreEqual(StatusCode.Conflict, status.Code);
            Assert.IsNull(revResult);

            revD = database.PutRevision(revD, rev2.GetRevId(), false, status);
            Assert.AreEqual(StatusCode.Ok, status.Code);
            Assert.AreEqual(revD.GetDocId(), rev2.GetDocId());
            Assert.IsTrue(revD.GetRevId().StartsWith("3-"));

            // Delete nonexistent doc:
            var revFake = new RevisionInternal("fake", null, true);

            database.PutRevision(revFake, null, false, status);
            Assert.AreEqual(StatusCode.NotFound, status.Code);

            // Read it back (should fail):
            readRev = database.GetDocument(revD.GetDocId(), null,
                                           true);
            Assert.IsNull(readRev);

            // Get Changes feed:
            changeRevisions = database.ChangesSince(0, null, null, null);
            Assert.IsTrue(changeRevisions.Count == 1);

            // Get Revision History:
            IList <RevisionInternal> history = database.Storage.GetRevisionHistory(revD, null);

            Assert.AreEqual(revD, history[0]);
            Assert.AreEqual(rev2, history[1]);
            Assert.AreEqual(rev1, history[2]);
        }
Exemplo n.º 8
0
        private bool UploadMultipartRevision(RevisionInternal revision)
        {
            MultipartEntity multiPart             = null;
            IDictionary <string, object> revProps = revision.GetProperties();

            revProps.Put("_revisions", db.GetRevisionHistoryDict(revision));
            // TODO: refactor this to
            IDictionary <string, object> attachments = (IDictionary <string, object>)revProps.Get
                                                           ("_attachments");

            foreach (string attachmentKey in attachments.Keys)
            {
                IDictionary <string, object> attachment = (IDictionary <string, object>)attachments
                                                          .Get(attachmentKey);
                if (attachment.ContainsKey("follows"))
                {
                    if (multiPart == null)
                    {
                        multiPart = new MultipartEntity();
                        try
                        {
                            string   json        = Manager.GetObjectMapper().WriteValueAsString(revProps);
                            Encoding utf8charset = Sharpen.Extensions.GetEncoding("UTF-8");
                            multiPart.AddPart("param1", new StringBody(json, "application/json", utf8charset)
                                              );
                        }
                        catch (IOException e)
                        {
                            throw new ArgumentException(e);
                        }
                    }
                    BlobStore   blobStore    = this.db.GetAttachments();
                    string      base64Digest = (string)attachment.Get("digest");
                    BlobKey     blobKey      = new BlobKey(base64Digest);
                    InputStream inputStream  = blobStore.BlobStreamForKey(blobKey);
                    if (inputStream == null)
                    {
                        Log.W(Database.Tag, "Unable to find blob file for blobKey: " + blobKey + " - Skipping upload of multipart revision."
                              );
                        multiPart = null;
                    }
                    else
                    {
                        string contentType = null;
                        if (attachment.ContainsKey("content_type"))
                        {
                            contentType = (string)attachment.Get("content_type");
                        }
                        else
                        {
                            if (attachment.ContainsKey("content-type"))
                            {
                                string message = string.Format("Found attachment that uses content-type" + " field name instead of content_type (see couchbase-lite-android"
                                                               + " issue #80): " + attachment);
                                Log.W(Database.Tag, message);
                            }
                        }
                        multiPart.AddPart(attachmentKey, new InputStreamBody(inputStream, contentType, attachmentKey
                                                                             ));
                    }
                }
            }
            if (multiPart == null)
            {
                return(false);
            }
            string path = string.Format("/%s?new_edits=false", revision.GetDocId());

            // TODO: need to throttle these requests
            Log.D(Database.Tag, "Uploadeding multipart request.  Revision: " + revision);
            Log.D(Database.Tag, this + "|" + Sharpen.Thread.CurrentThread() + ": uploadMultipartRevision() calling asyncTaskStarted()"
                  );
            AsyncTaskStarted();
            SendAsyncMultipartRequest("PUT", path, multiPart, new _RemoteRequestCompletionBlock_411
                                          (this));
            // TODO:
            return(true);
        }
Exemplo n.º 9
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public virtual void TestValidations()
        {
            Validator validator = new _Validator_19(this);

            database.SetValidation("hoopy", validator);
            // POST a valid new document:
            IDictionary <string, object> props = new Dictionary <string, object>();

            props.Put("name", "Zaphod Beeblebrox");
            props.Put("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(Status.Created, status.GetCode());
            // PUT a valid update:
            props.Put("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(Status.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() == Status.Forbidden);
            }
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            // POST an invalid new document:
            props = new Dictionary <string, object>();
            props.Put("name", "Vogon");
            props.Put("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() == Status.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.Put("_id", "ford");
            props.Put("name", "Ford Prefect");
            props.Put("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.Put("_id", "petunias");
            props.Put("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() == Status.Forbidden);
            }
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
        }
Exemplo n.º 10
0
        /// <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);
        }
Exemplo n.º 11
0
        public void TestValidations()
        {
            ValidateDelegate validator = (newRevision, context) =>
            {
                Assert.IsNotNull(newRevision);
                Assert.IsNotNull(context);
                Assert.IsTrue(newRevision.Properties != null || newRevision.IsDeletion);

                validationCalled = true;

                bool hoopy = newRevision.IsDeletion || (newRevision.Properties.Get("towel") != null);
                Log.V(ValidationsTest.Tag, string.Format("--- Validating {0} --> {1}", 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);

            validationCalled = false;
            rev = database.PutRevision(rev, null, false);
            Assert.IsTrue(validationCalled);

            // PUT a valid update:
            props["head_count"] = 3;
            rev.SetProperties(props);
            validationCalled = false;
            rev = database.PutRevision(rev, rev.GetRevId(), false);
            Assert.IsTrue(validationCalled);

            // PUT an invalid update:
            Sharpen.Collections.Remove(props, "towel");
            rev.SetProperties(props);
            validationCalled = false;
            Assert.Throws <CouchbaseLiteException>(() => rev = database.PutRevision(rev, rev.GetRevId(), false));
            Assert.IsTrue(validationCalled);

            // POST an invalid new document:
            props            = new Dictionary <string, object>();
            props["name"]    = "Vogon";
            props["poetry"]  = true;
            rev              = new RevisionInternal(props);
            validationCalled = false;
            Assert.Throws <CouchbaseLiteException>(() => database.PutRevision(rev, null, false));
            Assert.IsTrue(validationCalled);

            // 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);
            validationCalled = false;
            rev              = database.PutRevision(rev, null, false);
            Assert.IsTrue(validationCalled);
            Assert.AreEqual("ford", rev.GetDocId());

            // DELETE a document:
            rev = new RevisionInternal(rev.GetDocId(), rev.GetRevId(), true);
            Assert.IsTrue(rev.IsDeleted());
            validationCalled = false;
            rev = database.PutRevision(rev, rev.GetRevId(), false);
            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);
            validationCalled = false;
            Assert.Throws <CouchbaseLiteException>(() => rev = database.PutRevision(rev, null, false));
            Assert.IsTrue(validationCalled);
        }
Exemplo n.º 12
0
        public void TestRevTree()
        {
            var rev           = new RevisionInternal("MyDocId", "4-foxy", 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-thrice");
            revHistory.AddItem("2-too");
            revHistory.AddItem("1-won");
            database.ForceInsert(rev, revHistory, null);
            Assert.AreEqual(1, database.DocumentCount);

            VerifyHistory(database, rev, revHistory);
            var conflict           = new RevisionInternal("MyDocId", "5-epsilon", 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-delta");
            conflictHistory.AddItem("3-gamma");
            conflictHistory.AddItem("2-too");
            conflictHistory.AddItem("1-won");
            database.ForceInsert(conflict, conflictHistory, null);
            Assert.AreEqual(1, database.DocumentCount);
            VerifyHistory(database, conflict, conflictHistory);

            // Add an unrelated document:
            var other           = new RevisionInternal("AnotherDocID", "1-ichi", 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-too",
                                            true);

            Assert.IsNull(rev2);

            // Make sure no duplicate rows were inserted for the common revisions:
            Assert.AreEqual(8, database.LastSequenceNumber);
            // 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.SetIncludeConflicts(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));
        }
Exemplo n.º 13
0
 public virtual string GetDocumentId()
 {
     return(addedRevision.GetDocId());
 }
Exemplo n.º 14
0
        /// <summary>Fetches the contents of a revision from the remote db, including its parent revision ID.
        ///     </summary>
        /// <remarks>
        /// Fetches the contents of a revision from the remote db, including its parent revision ID.
        /// The contents are stored into rev.properties.
        /// </remarks>
        internal void PullRemoteRevision(RevisionInternal rev)
        {
            Log.D(Tag, this + "|" + Thread.CurrentThread() + ": pullRemoteRevision with rev: " + rev);
            Log.D(Tag, this + "|" + Thread.CurrentThread() + ": pullRemoteRevision() calling asyncTaskStarted()");

            AsyncTaskStarted();

            httpConnectionCount++;

            // Construct a query. We want the revision history, and the bodies of attachments that have
            // been added since the latest revisions we have locally.
            // See: http://wiki.apache.org/couchdb/HTTP_Document_API#Getting_Attachments_With_a_Document
            var path      = new StringBuilder("/" + HttpUtility.UrlEncode(rev.GetDocId()) + "?rev=" + HttpUtility.UrlEncode(rev.GetRevId()) + "&revs=true&attachments=true");
            var knownRevs = KnownCurrentRevIDs(rev);

            if (knownRevs == null)
            {
                //this means something is wrong, possibly the replicator has shut down
                Log.D(Tag, this + "|" + Thread.CurrentThread() + ": pullRemoteRevision() calling asyncTaskFinished()");
                AsyncTaskFinished(1);
                httpConnectionCount--;
                return;
            }

            if (knownRevs.Count > 0)
            {
                path.Append("&atts_since=");
                path.Append(JoinQuotedEscaped(knownRevs));
            }

            //create a final version of this variable for the log statement inside
            //FIXME find a way to avoid this
            var pathInside = path.ToString();

            SendAsyncMultipartDownloaderRequest(HttpMethod.Get, pathInside, null, LocalDatabase, (result, e) =>
            {
                try
                {
                    // OK, now we've got the response revision:
                    Log.D(Tag, this + ": pullRemoteRevision got response for rev: " + rev);
                    if (result != null)
                    {
                        var properties = ((JObject)result).ToObject <IDictionary <string, object> >();
                        var history    = Database.ParseCouchDBRevisionHistory(properties);

                        if (history != null)
                        {
                            rev.SetProperties(properties);
                            // Add to batcher ... eventually it will be fed to -insertRevisions:.
                            var toInsert = new AList <object> ();
                            toInsert.AddItem(rev);
                            toInsert.AddItem(history);
                            Log.D(Tag, this + ": pullRemoteRevision add rev: " + rev + " to batcher");
                            downloadsToInsert.QueueObject(toInsert);
                            Log.D(Tag, this + "|" + Thread.CurrentThread() + ": pullRemoteRevision.onCompletion() calling asyncTaskStarted()");
                            AsyncTaskStarted();
                        }
                        else
                        {
                            Log.W(Tag, this + ": Missing revision history in response from " + pathInside);
                            CompletedChangesCount += 1;
                        }
                    }
                    else
                    {
                        if (e != null)
                        {
                            Log.E(Tag, "Error pulling remote revision", e);
                            LastError = e;
                        }
                        CompletedChangesCount += 1;
                    }
                }
                finally
                {
                    Log.D(Tag, this + "|" + Thread.CurrentThread() + ": pullRemoteRevision.onCompletion() calling asyncTaskFinished()");
                    AsyncTaskFinished(1);
                }

                // Note that we've finished this task; then start another one if there
                // are still revisions waiting to be pulled:
                --httpConnectionCount;
                PullRemoteRevisions();
            });
        }
Exemplo n.º 15
0
        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 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());
        }
Exemplo n.º 17
0
        /// <exception cref="System.Exception"></exception>
        public virtual void TestPutLargeAttachment()
        {
            string    testAttachmentName = "test_attachment";
            BlobStore attachments        = database.GetAttachments();

            attachments.DeleteBlobs();
            NUnit.Framework.Assert.AreEqual(0, attachments.Count());
            Status status = new Status();
            IDictionary <string, object> rev1Properties = new Dictionary <string, object>();

            rev1Properties.Put("foo", 1);
            rev1Properties.Put("bar", false);
            RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties,
                                                                              database), null, false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            StringBuilder largeAttachment = new StringBuilder();

            for (int i = 0; i < Database.kBigAttachmentLength; i++)
            {
                largeAttachment.Append("big attachment!");
            }
            byte[] attach1 = Sharpen.Runtime.GetBytesForString(largeAttachment.ToString());
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
                                                                                         ), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
            Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
                                                                      );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
            byte[] data = IOUtils.ToByteArray(attachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            EnumSet <Database.TDContentOptions> contentOptions = EnumSet.Of(Database.TDContentOptions
                                                                            .TDIncludeAttachments, Database.TDContentOptions.TDBigAttachmentsFollow);
            IDictionary <string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
                                                                         (rev1.GetSequence(), contentOptions);
            IDictionary <string, object> innerDict = (IDictionary <string, object>)attachmentDictForSequence
                                                     .Get(testAttachmentName);

            if (!innerDict.ContainsKey("stub"))
            {
                throw new RuntimeException("Expected attachment dict to have 'stub' key");
            }
            if (((bool)innerDict.Get("stub")) == false)
            {
                throw new RuntimeException("Expected attachment dict 'stub' key to be true");
            }
            if (!innerDict.ContainsKey("follows"))
            {
                throw new RuntimeException("Expected attachment dict to have 'follows' key");
            }
            RevisionInternal rev1WithAttachments = database.GetDocumentWithIDAndRev(rev1.GetDocId
                                                                                        (), rev1.GetRevId(), contentOptions);
            // Map<String,Object> rev1PropertiesPrime = rev1WithAttachments.getProperties();
            // rev1PropertiesPrime.put("foo", 2);
            IDictionary <string, object> rev1WithAttachmentsProperties = rev1WithAttachments.GetProperties
                                                                             ();
            IDictionary <string, object> rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1WithAttachmentsProperties.Get("_id"));
            rev2Properties.Put("foo", 2);
            RevisionInternal newRev = new RevisionInternal(rev2Properties, database);
            RevisionInternal rev2   = database.PutRevision(newRev, rev1WithAttachments.GetRevId
                                                               (), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1WithAttachments
                                                               .GetSequence(), rev2.GetSequence());
            // Check the 2nd revision's attachment:
            Attachment rev2FetchedAttachment = database.GetAttachmentForSequence(rev2.GetSequence
                                                                                     (), testAttachmentName);

            NUnit.Framework.Assert.AreEqual(attachment.GetLength(), rev2FetchedAttachment.GetLength
                                                ());
            NUnit.Framework.Assert.AreEqual(attachment.GetMetadata(), rev2FetchedAttachment.GetMetadata
                                                ());
            NUnit.Framework.Assert.AreEqual(attachment.GetContentType(), rev2FetchedAttachment
                                            .GetContentType());
            // Add a third revision of the same document:
            IDictionary <string, object> rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetProperties().Get("_id"));
            rev3Properties.Put("foo", 3);
            rev3Properties.Put("baz", false);
            RevisionInternal rev3 = new RevisionInternal(rev3Properties, database);

            rev3 = database.PutRevision(rev3, rev2.GetRevId(), false, status);
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach3 = Sharpen.Runtime.GetBytesForString("<html><blink>attach3</blink></html>"
                                                               );
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach3
                                                                                         ), rev3.GetSequence(), testAttachmentName, "text/html", rev3.GetGeneration());
            // Check the 3rd revision's attachment:
            Attachment rev3FetchedAttachment = database.GetAttachmentForSequence(rev3.GetSequence
                                                                                     (), testAttachmentName);

            data = IOUtils.ToByteArray(rev3FetchedAttachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach3, data));
            NUnit.Framework.Assert.AreEqual("text/html", rev3FetchedAttachment.GetContentType
                                                ());
            // TODO: why doesn't this work?
            // Assert.assertEquals(attach3.length, rev3FetchedAttachment.getLength());
            ICollection <BlobKey> blobKeys = database.GetAttachments().AllKeys();

            NUnit.Framework.Assert.AreEqual(2, blobKeys.Count);
            database.Compact();
            blobKeys = database.GetAttachments().AllKeys();
            NUnit.Framework.Assert.AreEqual(1, blobKeys.Count);
        }
Exemplo n.º 18
0
        private bool UploadMultipartRevision(RevisionInternal revision)
        {
            MultipartContent multiPart = null;
            var revProps = revision.GetProperties();

            var attachments = revProps.Get("_attachments").AsDictionary <string, object>();

            foreach (var attachmentKey in attachments.Keys)
            {
                var attachment = attachments.Get(attachmentKey).AsDictionary <string, object>();
                if (attachment.ContainsKey("follows"))
                {
                    if (multiPart == null)
                    {
                        multiPart = new MultipartContent("related");
                        try
                        {
                            var json        = Manager.GetObjectMapper().WriteValueAsString(revProps);
                            var utf8charset = Encoding.UTF8;
                            //multiPart.Add(new StringContent(json, utf8charset, "application/json"), "param1");

                            var jsonContent = new StringContent(json, utf8charset, "application/json");
                            //jsonContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
                            multiPart.Add(jsonContent);
                        }
                        catch (IOException e)
                        {
                            throw new ArgumentException("Not able to serialize revision properties into a multipart request content.", e);
                        }
                    }

                    var blobStore    = LocalDatabase.Attachments;
                    var base64Digest = (string)attachment.Get("digest");

                    var blobKey     = new BlobKey(base64Digest);
                    var inputStream = blobStore.BlobStreamForKey(blobKey);

                    if (inputStream == null)
                    {
                        Log.W(Tag, "Unable to find blob file for blobKey: " + blobKey + " - Skipping upload of multipart revision.");
                        multiPart = null;
                    }
                    else
                    {
                        string contentType = null;
                        if (attachment.ContainsKey("content_type"))
                        {
                            contentType = (string)attachment.Get("content_type");
                        }
                        else
                        {
                            if (attachment.ContainsKey("content-type"))
                            {
                                var message = string.Format("Found attachment that uses content-type"
                                                            + " field name instead of content_type (see couchbase-lite-android"
                                                            + " issue #80): " + attachment);
                                Log.W(Tag, message);
                            }
                        }

                        var content = new StreamContent(inputStream);
                        content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                        {
                            FileName = Path.GetFileName(blobStore.PathForKey(blobKey))
                        };
                        content.Headers.ContentType = new MediaTypeHeaderValue(contentType);

                        multiPart.Add(content);
                    }
                }
            }

            if (multiPart == null)
            {
                return(false);
            }

            var path = string.Format("/{0}?new_edits=false", revision.GetDocId());

            // TODO: need to throttle these requests
            Log.D(Tag, "Uploading multipart request.  Revision: " + revision);
            Log.D(Tag, "uploadMultipartRevision() calling asyncTaskStarted()");

            SafeAddToChangesCount(1);
            AsyncTaskStarted();

            SendAsyncMultipartRequest(HttpMethod.Put, path, multiPart, (result, e) => {
                try
                {
                    if (e != null)
                    {
                        var httpError = e as HttpResponseException;
                        if (httpError != null)
                        {
                            if (httpError.StatusCode == System.Net.HttpStatusCode.UnsupportedMediaType)
                            {
                                dontSendMultipart = true;
                                UploadJsonRevision(revision);
                            }
                        }
                        else
                        {
                            Log.E(Tag, "Exception uploading multipart request", e);
                            SetLastError(e);
                            RevisionFailed();
                        }
                    }
                    else
                    {
                        Log.D(Tag, "Uploaded multipart request.  Result: " + result);
                        RemovePending(revision);
                    }
                }
                finally
                {
                    Log.D(Tag, "uploadMultipartRevision() calling asyncTaskFinished()");
                    // TODO: calling addToCompleteChangesCount(1)
                    AsyncTaskFinished(1);
                }
            });

            return(true);
        }
Exemplo n.º 19
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public virtual void TestPutAttachment()
        {
            string    testAttachmentName = "test_attachment";
            BlobStore attachments        = database.GetAttachments();

            attachments.DeleteBlobs();
            NUnit.Framework.Assert.AreEqual(0, attachments.Count());
            // Put a revision that includes an _attachments dict:
            byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1");
            string base64  = Base64.EncodeBytes(attach1);
            IDictionary <string, object> attachment = new Dictionary <string, object>();

            attachment.Put("content_type", "text/plain");
            attachment.Put("data", base64);
            IDictionary <string, object> attachmentDict = new Dictionary <string, object>();

            attachmentDict.Put(testAttachmentName, attachment);
            IDictionary <string, object> properties = new Dictionary <string, object>();

            properties.Put("foo", 1);
            properties.Put("bar", false);
            properties.Put("_attachments", attachmentDict);
            RevisionInternal rev1 = database.PutRevision(new RevisionInternal(properties, database
                                                                              ), null, false);

            // Examine the attachment store:
            NUnit.Framework.Assert.AreEqual(1, attachments.Count());
            // Get the revision:
            RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1
                                                                        .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>());
            IDictionary <string, object> gotAttachmentDict = (IDictionary <string, object>)gotRev1
                                                             .GetProperties().Get("_attachments");
            IDictionary <string, object> innerDict = new Dictionary <string, object>();

            innerDict.Put("content_type", "text/plain");
            innerDict.Put("digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=");
            innerDict.Put("length", 27);
            innerDict.Put("stub", true);
            innerDict.Put("revpos", 1);
            IDictionary <string, object> expectAttachmentDict = new Dictionary <string, object>
                                                                    ();

            expectAttachmentDict.Put(testAttachmentName, innerDict);
            NUnit.Framework.Assert.AreEqual(expectAttachmentDict, gotAttachmentDict);
            // Update the attachment directly:
            byte[] attachv2             = Sharpen.Runtime.GetBytesForString("Replaced body of attach");
            bool   gotExpectedErrorCode = false;

            try
            {
                database.UpdateAttachment(testAttachmentName, new ByteArrayInputStream(attachv2),
                                          "application/foo", rev1.GetDocId(), null);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.Conflict);
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;
            try
            {
                database.UpdateAttachment(testAttachmentName, new ByteArrayInputStream(attachv2),
                                          "application/foo", rev1.GetDocId(), "1-bogus");
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.Conflict);
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;
            RevisionInternal rev2 = null;

            try
            {
                rev2 = database.UpdateAttachment(testAttachmentName, new ByteArrayInputStream(attachv2
                                                                                              ), "application/foo", rev1.GetDocId(), rev1.GetRevId());
            }
            catch (CouchbaseLiteException)
            {
                gotExpectedErrorCode = true;
            }
            NUnit.Framework.Assert.IsFalse(gotExpectedErrorCode);
            NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId());
            NUnit.Framework.Assert.AreEqual(2, rev2.GetGeneration());
            // Get the updated revision:
            RevisionInternal gotRev2 = database.GetDocumentWithIDAndRev(rev2.GetDocId(), rev2
                                                                        .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>());

            attachmentDict = (IDictionary <string, object>)gotRev2.GetProperties().Get("_attachments"
                                                                                       );
            innerDict = new Dictionary <string, object>();
            innerDict.Put("content_type", "application/foo");
            innerDict.Put("digest", "sha1-mbT3208HI3PZgbG4zYWbDW2HsPk=");
            innerDict.Put("length", 23);
            innerDict.Put("stub", true);
            innerDict.Put("revpos", 2);
            expectAttachmentDict.Put(testAttachmentName, innerDict);
            NUnit.Framework.Assert.AreEqual(expectAttachmentDict, attachmentDict);
            // Delete the attachment:
            gotExpectedErrorCode = false;
            try
            {
                database.UpdateAttachment("nosuchattach", null, null, rev2.GetDocId(), rev2.GetRevId
                                              ());
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.NotFound);
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;
            try
            {
                database.UpdateAttachment("nosuchattach", null, null, "nosuchdoc", "nosuchrev");
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == Status.NotFound);
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedErrorCode);
            RevisionInternal rev3 = database.UpdateAttachment(testAttachmentName, null, null,
                                                              rev2.GetDocId(), rev2.GetRevId());

            NUnit.Framework.Assert.AreEqual(rev2.GetDocId(), rev3.GetDocId());
            NUnit.Framework.Assert.AreEqual(3, rev3.GetGeneration());
            // Get the updated revision:
            RevisionInternal gotRev3 = database.GetDocumentWithIDAndRev(rev3.GetDocId(), rev3
                                                                        .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>());

            attachmentDict = (IDictionary <string, object>)gotRev3.GetProperties().Get("_attachments"
                                                                                       );
            NUnit.Framework.Assert.IsNull(attachmentDict);
            database.Close();
        }
Exemplo n.º 20
0
        public void TestRevTreeChangeNotification()
        {
            const string DOCUMENT_ID = "MyDocId";

            var rev           = new RevisionInternal(DOCUMENT_ID, "1-one", false, database);
            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-three", false, database);
            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-two");
            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 ex)
                {
                    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-winner", false, database);
            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-two");
            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 ex)
                {
                    Assert.Fail();
                }
            };

            database.Changed += handler;
            database.ForceInsert(conflictRev, conflictRevHistory, null);
            database.Changed -= handler;
        }
Exemplo n.º 21
0
        /// <exception cref="System.Exception"></exception>
        public virtual void TestAttachments()
        {
            string    testAttachmentName = "test_attachment";
            BlobStore attachments        = database.GetAttachments();

            NUnit.Framework.Assert.AreEqual(0, attachments.Count());
            NUnit.Framework.Assert.AreEqual(new HashSet <object>(), attachments.AllKeys());
            Status status = new Status();
            IDictionary <string, object> rev1Properties = new Dictionary <string, object>();

            rev1Properties.Put("foo", 1);
            rev1Properties.Put("bar", false);
            RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties,
                                                                              database), null, false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1");
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
                                                                                         ), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
                                                                      );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
            byte[] data = IOUtils.ToByteArray(attachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            IDictionary <string, object> innerDict = new Dictionary <string, object>();

            innerDict.Put("content_type", "text/plain");
            innerDict.Put("digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=");
            innerDict.Put("length", 27);
            innerDict.Put("stub", true);
            innerDict.Put("revpos", 1);
            IDictionary <string, object> attachmentDict = new Dictionary <string, object>();

            attachmentDict.Put(testAttachmentName, innerDict);
            IDictionary <string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
                                                                         (rev1.GetSequence(), EnumSet.NoneOf <Database.TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1
                                                                        .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>());
            IDictionary <string, object> gotAttachmentDict = (IDictionary <string, object>)gotRev1
                                                             .GetProperties().Get("_attachments");

            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Check the attachment dict, with attachments included:
            Sharpen.Collections.Remove(innerDict, "stub");
            innerDict.Put("data", Base64.EncodeBytes(attach1));
            attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1
                                                                                          .GetSequence(), EnumSet.Of(Database.TDContentOptions.TDIncludeAttachments));
            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), EnumSet
                                                       .Of(Database.TDContentOptions.TDIncludeAttachments));
            gotAttachmentDict = (IDictionary <string, object>)gotRev1.GetProperties().Get("_attachments"
                                                                                          );
            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Add a second revision that doesn't update the attachment:
            IDictionary <string, object> rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1.GetDocId());
            rev2Properties.Put("foo", 2);
            rev2Properties.Put("bazz", false);
            RevisionInternal rev2 = database.PutRevision(new RevisionInternal(rev2Properties,
                                                                              database), rev1.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1.GetSequence
                                                                   (), rev2.GetSequence());
            // Add a third revision of the same document:
            IDictionary <string, object> rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetDocId());
            rev3Properties.Put("foo", 2);
            rev3Properties.Put("bazz", false);
            RevisionInternal rev3 = database.PutRevision(new RevisionInternal(rev3Properties,
                                                                              database), rev2.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach2 = Sharpen.Runtime.GetBytesForString("<html>And this is attach2</html>"
                                                               );
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach2
                                                                                         ), rev3.GetSequence(), testAttachmentName, "text/html", rev2.GetGeneration());
            // Check the 2nd revision's attachment:
            Attachment attachment2 = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment2.GetContentType());
            data = IOUtils.ToByteArray(attachment2.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            // Check the 3rd revision's attachment:
            Attachment attachment3 = database.GetAttachmentForSequence(rev3.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/html", attachment3.GetContentType());
            data = IOUtils.ToByteArray(attachment3.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach2, data));
            // Examine the attachment store:
            NUnit.Framework.Assert.AreEqual(2, attachments.Count());
            ICollection <BlobKey> expected = new HashSet <BlobKey>();

            expected.AddItem(BlobStore.KeyForBlob(attach1));
            expected.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected, attachments.AllKeys());
            database.Compact();
            // This clears the body of the first revision
            NUnit.Framework.Assert.AreEqual(1, attachments.Count());
            ICollection <BlobKey> expected2 = new HashSet <BlobKey>();

            expected2.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected2, attachments.AllKeys());
        }
        /// <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);
        }
Exemplo n.º 23
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public virtual void TestCRUDOperations()
        {
            database.AddChangeListener(this);
            string privateUUID = database.PrivateUUID();
            string publicUUID  = database.PublicUUID();

            Log.V(Tag, "DB private UUID = '" + privateUUID + "', public UUID = '" + publicUUID
                  + "'");
            NUnit.Framework.Assert.IsTrue(privateUUID.Length >= 20);
            NUnit.Framework.Assert.IsTrue(publicUUID.Length >= 20);
            //create a document
            IDictionary <string, object> documentProperties = new Dictionary <string, object>();

            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;
            documentProperties["baz"] = "touch";
            Body             body   = new Body(documentProperties);
            RevisionInternal rev1   = new RevisionInternal(body, database);
            Status           status = new Status();

            rev1 = database.PutRevision(rev1, null, false, status);
            Log.V(Tag, "Created " + rev1);
            NUnit.Framework.Assert.IsTrue(rev1.GetDocId().Length >= 10);
            NUnit.Framework.Assert.IsTrue(rev1.GetRevId().StartsWith("1-"));
            //read it back
            RevisionInternal readRev = database.GetDocumentWithIDAndRev(rev1.GetDocId(), null
                                                                        , EnumSet.NoneOf <TDContentOptions>());

            NUnit.Framework.Assert.IsNotNull(readRev);
            IDictionary <string, object> readRevProps = readRev.Properties;

            NUnit.Framework.Assert.AreEqual(UserProperties(readRevProps), UserProperties(body
                                                                                         .Properties));
            //now update it
            documentProperties           = readRev.Properties;
            documentProperties["status"] = "updated!";
            body = new Body(documentProperties);
            RevisionInternal rev2      = new RevisionInternal(body, database);
            RevisionInternal rev2input = rev2;

            rev2 = database.PutRevision(rev2, rev1.GetRevId(), false, status);
            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.GetDocumentWithIDAndRev(rev2.GetDocId(), null, EnumSet.NoneOf <
                                                           TDContentOptions>());
            NUnit.Framework.Assert.IsNotNull(readRev);
            NUnit.Framework.Assert.AreEqual(UserProperties(readRev.Properties), UserProperties
                                                (body.Properties));
            // Try to update the first rev, which should fail:
            bool gotExpectedError = false;

            try
            {
                database.PutRevision(rev2input, rev1.GetRevId(), false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.Conflict;
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            // Check the changes feed, with and without filters:
            RevisionList changes = database.ChangesSince(0, null, null);

            Log.V(Tag, "Changes = " + changes);
            NUnit.Framework.Assert.AreEqual(1, changes.Count);
            changes = database.ChangesSince(0, null, (revision, items) => "updated!".Equals(revision.Properties.Get("status")));
            NUnit.Framework.Assert.AreEqual(1, changes.Count);
            changes = database.ChangesSince(0, null, (revision, items) => "not updated!".Equals(revision.Properties.Get("status")));
            NUnit.Framework.Assert.AreEqual(0, changes.Count);
            // Delete it:
            RevisionInternal revD = new RevisionInternal(rev2.GetDocId(), null, true, database
                                                         );
            RevisionInternal revResult = null;

            gotExpectedError = false;
            try
            {
                revResult = database.PutRevision(revD, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.Conflict;
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            NUnit.Framework.Assert.IsNull(revResult);
            revD = database.PutRevision(revD, rev2.GetRevId(), false, status);
            NUnit.Framework.Assert.AreEqual(StatusCode.Ok, status.GetCode());
            NUnit.Framework.Assert.AreEqual(revD.GetDocId(), rev2.GetDocId());
            NUnit.Framework.Assert.IsTrue(revD.GetRevId().StartsWith("3-"));
            // Delete nonexistent doc:
            RevisionInternal revFake = new RevisionInternal("fake", null, true, database);

            gotExpectedError = false;
            try
            {
                database.PutRevision(revFake, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.NotFound;
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            // Read it back (should fail):
            readRev = database.GetDocumentWithIDAndRev(revD.GetDocId(), null, EnumSet.NoneOf <TDContentOptions>());
            NUnit.Framework.Assert.IsNull(readRev);
            // Get Changes feed
            changes = database.ChangesSince(0, null, null);
            NUnit.Framework.Assert.IsTrue(changes.Count == 1);
            // Get Revision History
            IList <RevisionInternal> history = database.GetRevisionHistory(revD);

            NUnit.Framework.Assert.AreEqual(revD, history[0]);
            NUnit.Framework.Assert.AreEqual(rev2, history[1]);
            NUnit.Framework.Assert.AreEqual(rev1, history[2]);
        }
        public void TestAttachments()
        {
            const string testAttachmentName = "test_attachment";
            var          attachments        = database.Attachments;

            Assert.AreEqual(0, attachments.Count());
            Assert.AreEqual(0, attachments.AllKeys().Count());

            var attach1 = Encoding.UTF8.GetBytes("This is the body of attach1");
            var props   = new Dictionary <string, object> {
                { "foo", 1 },
                { "bar", false },
                { "_attachments", CreateAttachmentsDict(attach1, testAttachmentName, "text/plain", false) }
            };

            RevisionInternal rev1 = database.PutRevision(new RevisionInternal(props), null, false);

            var att = database.GetAttachmentForRevision(rev1, testAttachmentName);

            Assert.AreEqual(attach1, att.Content);
            Assert.AreEqual("text/plain", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            var itemDict = new Dictionary <string, object> {
                { "content_type", "text/plain" },
                { "digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=" },
                { "length", 27 },
                { "stub", true },
                { "revpos", 1 }
            };
            var attachmentDict = new Dictionary <string, object> {
                { testAttachmentName, itemDict }
            };
            var gotRev1 = database.GetDocument(rev1.GetDocId(), rev1.GetRevId(), true);

            AssertDictionariesAreEqual(attachmentDict, gotRev1.GetAttachments());

            itemDict.Remove("stub");
            itemDict["data"] = Convert.ToBase64String(attach1);
            gotRev1          = database.GetDocument(rev1.GetDocId(), rev1.GetRevId(), true);
            var expandedRev = gotRev1.CopyWithDocID(rev1.GetDocId(), rev1.GetRevId());

            Assert.DoesNotThrow(() => database.ExpandAttachments(expandedRev, 0, false, true));
            AssertDictionariesAreEqual(attachmentDict, expandedRev.GetAttachments());

            // Add a second revision that doesn't update the attachment:
            props = new Dictionary <string, object> {
                { "_id", rev1.GetDocId() },
                { "foo", 2 },
                { "bazz", false },
                { "_attachments", CreateAttachmentsStub(testAttachmentName) }
            };
            var rev2 = database.PutRevision(new RevisionInternal(props), rev1.GetRevId());

            // Add a third revision of the same document:
            var attach2 = Encoding.UTF8.GetBytes("<html>And this is attach2</html>");

            props = new Dictionary <string, object> {
                { "_id", rev2.GetDocId() },
                { "foo", 2 },
                { "bazz", false },
                { "_attachments", CreateAttachmentsDict(attach2, testAttachmentName, "text/html", false) }
            };
            var rev3 = database.PutRevision(new RevisionInternal(props), rev2.GetRevId());

            // Check the second revision's attachment
            att = database.GetAttachmentForRevision(rev2, testAttachmentName);
            Assert.AreEqual(attach1, att.Content);
            Assert.AreEqual("text/plain", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            expandedRev = rev2.CopyWithDocID(rev2.GetDocId(), rev2.GetRevId());
            Assert.DoesNotThrow(() => database.ExpandAttachments(expandedRev, 2, false, true));
            AssertDictionariesAreEqual(new Dictionary <string, object> {
                { testAttachmentName, new Dictionary <string, object> {
                      { "stub", true },
                      { "revpos", 1 }
                  } }
            }, expandedRev.GetAttachments());

            // Check the 3rd revision's attachment:
            att = database.GetAttachmentForRevision(rev3, testAttachmentName);
            Assert.AreEqual(attach2, att.Content);
            Assert.AreEqual("text/html", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            expandedRev = rev3.CopyWithDocID(rev3.GetDocId(), rev3.GetRevId());
            Assert.DoesNotThrow(() => database.ExpandAttachments(expandedRev, 2, false, true));
            attachmentDict = new Dictionary <string, object> {
                { testAttachmentName, new Dictionary <string, object> {
                      { "content_type", "text/html" },
                      { "data", "PGh0bWw+QW5kIHRoaXMgaXMgYXR0YWNoMjwvaHRtbD4=" },
                      { "digest", "sha1-s14XRTXlwvzYfjo1t1u0rjB+ZUA=" },
                      { "length", 32 },
                      { "revpos", 3 }
                  } }
            };
            AssertDictionariesAreEqual(attachmentDict, expandedRev.GetAttachments());

            // Examine the attachment store:
            Assert.AreEqual(2, attachments.Count());
            Assert.AreEqual(new HashSet <BlobKey> {
                BlobStore.KeyForBlob(attach1), BlobStore.KeyForBlob(attach2)
            }, attachments.AllKeys());
            database.Compact();
            Assert.AreEqual(1, attachments.Count());
            Assert.AreEqual(new HashSet <BlobKey> {
                BlobStore.KeyForBlob(attach2)
            }, attachments.AllKeys());
        }
Exemplo n.º 25
0
        // Apply the options in the URL query to the specified revision and create a new revision object
        private static RevisionInternal ApplyOptions(DocumentContentOptions options, RevisionInternal rev, ICouchbaseListenerContext context,
                                                     Database db, Status outStatus)
        {
            if ((options & (DocumentContentOptions.IncludeRevs | DocumentContentOptions.IncludeRevsInfo | DocumentContentOptions.IncludeConflicts |
                            DocumentContentOptions.IncludeAttachments | DocumentContentOptions.IncludeLocalSeq)) != 0)
            {
                var dst = rev.GetProperties();
                if (options.HasFlag(DocumentContentOptions.IncludeLocalSeq))
                {
                    dst["_local_seq"] = rev.GetSequence();
                }

                if (options.HasFlag(DocumentContentOptions.IncludeRevs))
                {
                    dst["_revisions"] = db.GetRevisionHistoryDict(rev);
                }

                if (options.HasFlag(DocumentContentOptions.IncludeRevsInfo))
                {
                    dst["_revs_info"] = db.GetRevisionHistory(rev).Select(x =>
                    {
                        string status = "available";
                        if (x.IsDeleted())
                        {
                            status = "deleted";
                        }
                        else if (x.IsMissing())
                        {
                            status = "missing";
                        }

                        return(new Dictionary <string, object> {
                            { "rev", x.GetRevId() },
                            { "status", status }
                        });
                    });
                }

                if (options.HasFlag(DocumentContentOptions.IncludeConflicts))
                {
                    RevisionList revs = db.GetAllRevisionsOfDocumentID(rev.GetDocId(), true);
                    if (revs.Count > 1)
                    {
                        dst["_conflicts"] = revs.Select(x =>
                        {
                            return(x.Equals(rev) || x.IsDeleted() ? null : x.GetRevId());
                        });
                    }
                }

                RevisionInternal nuRev = new RevisionInternal(dst);
                if (options.HasFlag(DocumentContentOptions.IncludeAttachments))
                {
                    bool attEncodingInfo = context.GetQueryParam <bool>("att_encoding_info", bool.TryParse, false);
                    if (!db.ExpandAttachments(nuRev, 0, false, !attEncodingInfo, outStatus))
                    {
                        return(null);
                    }
                }

                rev = nuRev;
            }

            return(rev);
        }
Exemplo n.º 26
0
        public virtual void TestPutAttachment()
        {
            const string testAttachmentName = "test_attachment";
            var          attachments        = database.Attachments;

            attachments.DeleteBlobs();
            Assert.AreEqual(0, attachments.Count());

            // Put a revision that includes an _attachments dict:
            var attach1    = Encoding.UTF8.GetBytes("This is the body of attach1");
            var base64     = Convert.ToBase64String(attach1);
            var attachment = new Dictionary <string, object>();

            attachment["content_type"] = "text/plain";
            attachment["data"]         = base64;

            IDictionary <string, object> attachmentDict = new Dictionary <string, object>();

            attachmentDict[testAttachmentName] = attachment;
            var properties = new Dictionary <string, object>();

            properties["foo"]          = 1;
            properties["bar"]          = false;
            properties["_attachments"] = attachmentDict;

            var rev1 = database.PutRevision(new RevisionInternal(properties), null, false);

            // Examine the attachment store:
            Assert.AreEqual(1, attachments.Count());

            // Get the revision:
            var gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(),
                                                           rev1.GetRevId(), DocumentContentOptions.None);
            var gotAttachmentDict = gotRev1.GetPropertyForKey("_attachments").AsDictionary <string, object>();

            var innerDict = new JObject();

            innerDict["content_type"] = "text/plain";
            innerDict["digest"]       = "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=";
            innerDict["length"]       = 27;
            innerDict["stub"]         = true;
            innerDict["revpos"]       = 1;
            var expectAttachmentDict = new Dictionary <string, object>();

            expectAttachmentDict[testAttachmentName] = innerDict;
            Assert.AreEqual(expectAttachmentDict, gotAttachmentDict);

            // Update the attachment directly:
            var attachv2 = Encoding.UTF8.GetBytes("Replaced body of attach");
            var writer   = new BlobStoreWriter(database.Attachments);

            writer.AppendData(attachv2);
            writer.Finish();
            var gotExpectedErrorCode = false;

            try
            {
                database.UpdateAttachment(testAttachmentName, writer, "application/foo",
                                          AttachmentEncoding.None, rev1.GetDocId(), null);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.Conflict);
            }
            Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;

            try
            {
                database.UpdateAttachment(testAttachmentName, new BlobStoreWriter(database.Attachments), "application/foo",
                                          AttachmentEncoding.None, rev1.GetDocId(), "1-bogus");
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.Conflict);
            }

            Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;
            RevisionInternal rev2 = null;

            try
            {
                rev2 = database.UpdateAttachment(testAttachmentName, writer, "application/foo",
                                                 AttachmentEncoding.None, rev1.GetDocId(), rev1.GetRevId());
            }
            catch (CouchbaseLiteException)
            {
                gotExpectedErrorCode = true;
            }
            Assert.IsFalse(gotExpectedErrorCode);
            Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId());
            Assert.AreEqual(2, rev2.GetGeneration());
            // Get the updated revision:
            RevisionInternal gotRev2 = database.GetDocumentWithIDAndRev(rev2.GetDocId(), rev2
                                                                        .GetRevId(), DocumentContentOptions.None);

            attachmentDict            = gotRev2.GetProperties().Get("_attachments").AsDictionary <string, object>();
            innerDict                 = new JObject();
            innerDict["content_type"] = "application/foo";
            innerDict["digest"]       = "sha1-mbT3208HI3PZgbG4zYWbDW2HsPk=";
            innerDict["length"]       = 23;
            innerDict["stub"]         = true;
            innerDict["revpos"]       = 2;
            expectAttachmentDict[testAttachmentName] = innerDict;
            Assert.AreEqual(expectAttachmentDict, attachmentDict);
            // Delete the attachment:
            gotExpectedErrorCode = false;
            try
            {
                database.UpdateAttachment("nosuchattach", null, "application/foo",
                                          AttachmentEncoding.None, rev2.GetDocId(), rev2.GetRevId());
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.NotFound);
            }
            Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;
            try
            {
                database.UpdateAttachment("nosuchattach", null, null,
                                          AttachmentEncoding.None, "nosuchdoc", "nosuchrev");
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.NotFound);
            }
            Assert.IsTrue(gotExpectedErrorCode);
            RevisionInternal rev3 = database.UpdateAttachment(testAttachmentName, null, null,
                                                              AttachmentEncoding.None, rev2.GetDocId(), rev2.GetRevId());

            Assert.AreEqual(rev2.GetDocId(), rev3.GetDocId());
            Assert.AreEqual(3, rev3.GetGeneration());
            // Get the updated revision:
            RevisionInternal gotRev3 = database.GetDocumentWithIDAndRev(rev3.GetDocId(), rev3
                                                                        .GetRevId(), DocumentContentOptions.None);

            attachmentDict = gotRev3.GetProperties().Get("_attachments").AsDictionary <string, object>();
            Assert.IsNull(attachmentDict);
            database.Close();
        }
Exemplo n.º 27
0
        /// <exception cref="System.Exception"></exception>
        public virtual void TestAttachments()
        {
            string    testAttachmentName = "test_attachment";
            BlobStore attachments        = database.GetAttachments();

            NUnit.Framework.Assert.AreEqual(0, attachments.Count());
            NUnit.Framework.Assert.AreEqual(new HashSet <object>(), attachments.AllKeys());
            Status status = new Status();
            IDictionary <string, object> rev1Properties = new Dictionary <string, object>();

            rev1Properties.Put("foo", 1);
            rev1Properties.Put("bar", false);
            RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties,
                                                                              database), null, false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1");
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
                                                                                         ), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            //We must set the no_attachments column for the rev to false, as we are using an internal
            //private API call above (database.insertAttachmentForSequenceWithNameAndType) which does
            //not set the no_attachments column on revs table
            try
            {
                ContentValues args = new ContentValues();
                args.Put("no_attachments=", false);
                database.GetDatabase().Update("revs", args, "sequence=?", new string[] { rev1.GetSequence
                                                                                             ().ToString() });
            }
            catch (SQLException e)
            {
                Log.E(Database.Tag, "Error setting rev1 no_attachments to false", e);
                throw new CouchbaseLiteException(Status.InternalServerError);
            }
            Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
                                                                      );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
            byte[] data = IOUtils.ToByteArray(attachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            IDictionary <string, object> innerDict = new Dictionary <string, object>();

            innerDict.Put("content_type", "text/plain");
            innerDict.Put("digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=");
            innerDict.Put("length", 27);
            innerDict.Put("stub", true);
            innerDict.Put("revpos", 1);
            IDictionary <string, object> attachmentDict = new Dictionary <string, object>();

            attachmentDict.Put(testAttachmentName, innerDict);
            IDictionary <string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
                                                                         (rev1.GetSequence(), EnumSet.NoneOf <Database.TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1
                                                                        .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>());
            IDictionary <string, object> gotAttachmentDict = (IDictionary <string, object>)gotRev1
                                                             .GetProperties().Get("_attachments");

            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Check the attachment dict, with attachments included:
            Sharpen.Collections.Remove(innerDict, "stub");
            innerDict.Put("data", Base64.EncodeBytes(attach1));
            attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1
                                                                                          .GetSequence(), EnumSet.Of(Database.TDContentOptions.TDIncludeAttachments));
            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), EnumSet
                                                       .Of(Database.TDContentOptions.TDIncludeAttachments));
            gotAttachmentDict = (IDictionary <string, object>)gotRev1.GetProperties().Get("_attachments"
                                                                                          );
            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Add a second revision that doesn't update the attachment:
            IDictionary <string, object> rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1.GetDocId());
            rev2Properties.Put("foo", 2);
            rev2Properties.Put("bazz", false);
            RevisionInternal rev2 = database.PutRevision(new RevisionInternal(rev2Properties,
                                                                              database), rev1.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1.GetSequence
                                                                   (), rev2.GetSequence());
            // Add a third revision of the same document:
            IDictionary <string, object> rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetDocId());
            rev3Properties.Put("foo", 2);
            rev3Properties.Put("bazz", false);
            RevisionInternal rev3 = database.PutRevision(new RevisionInternal(rev3Properties,
                                                                              database), rev2.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach2 = Sharpen.Runtime.GetBytesForString("<html>And this is attach2</html>"
                                                               );
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach2
                                                                                         ), rev3.GetSequence(), testAttachmentName, "text/html", rev2.GetGeneration());
            // Check the 2nd revision's attachment:
            Attachment attachment2 = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment2.GetContentType());
            data = IOUtils.ToByteArray(attachment2.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            // Check the 3rd revision's attachment:
            Attachment attachment3 = database.GetAttachmentForSequence(rev3.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/html", attachment3.GetContentType());
            data = IOUtils.ToByteArray(attachment3.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach2, data));
            IDictionary <string, object> attachmentDictForRev3 = (IDictionary <string, object>)
                                                                 database.GetAttachmentsDictForSequenceWithContent(rev3.GetSequence(), EnumSet.NoneOf
                                                                                                                   <Database.TDContentOptions>()).Get(testAttachmentName);

            if (attachmentDictForRev3.ContainsKey("follows"))
            {
                if (((bool)attachmentDictForRev3.Get("follows")) == true)
                {
                    throw new RuntimeException("Did not expected attachment dict 'follows' key to be true"
                                               );
                }
                else
                {
                    throw new RuntimeException("Did not expected attachment dict to have 'follows' key"
                                               );
                }
            }
            // Examine the attachment store:
            NUnit.Framework.Assert.AreEqual(2, attachments.Count());
            ICollection <BlobKey> expected = new HashSet <BlobKey>();

            expected.AddItem(BlobStore.KeyForBlob(attach1));
            expected.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected, attachments.AllKeys());
            database.Compact();
            // This clears the body of the first revision
            NUnit.Framework.Assert.AreEqual(1, attachments.Count());
            ICollection <BlobKey> expected2 = new HashSet <BlobKey>();

            expected2.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected2, attachments.AllKeys());
        }