Пример #1
0
        public void TestStubOutAttachmentsInRevBeforeRevPos()
        {
            var hello = new JObject();

            hello["revpos"]  = 1;
            hello["follows"] = true;

            var goodbye = new JObject();

            goodbye["revpos"] = 2;
            goodbye["data"]   = "squeee";

            var attachments = new JObject();

            attachments["hello"]   = hello;
            attachments["goodbye"] = goodbye;

            var properties = new Dictionary <string, object>();

            properties["_attachments"] = attachments;

            IDictionary <string, object> expected = null;

            var rev = new RevisionInternal(properties);

            Database.StubOutAttachmentsInRevBeforeRevPos(rev, 3, false);
            var checkAttachments = rev.GetProperties()["_attachments"].AsDictionary <string, object>();
            var result           = (IDictionary <string, object>)checkAttachments["hello"];

            expected           = new Dictionary <string, object>();
            expected["revpos"] = 1;
            expected["stub"]   = true;
            AssertPropertiesAreEqual(expected, result);
            result             = (IDictionary <string, object>)checkAttachments["goodbye"];
            expected           = new Dictionary <string, object>();
            expected["revpos"] = 2;
            expected["stub"]   = true;
            AssertPropertiesAreEqual(expected, result);

            rev = new RevisionInternal(properties);
            Database.StubOutAttachmentsInRevBeforeRevPos(rev, 2, false);
            checkAttachments   = rev.GetProperties()["_attachments"].AsDictionary <string, object>();
            result             = checkAttachments["hello"].AsDictionary <string, object>();
            expected           = new Dictionary <string, object>();
            expected["revpos"] = 1;
            expected["stub"]   = true;
            AssertPropertiesAreEqual(expected, result);
            result   = checkAttachments["goodbye"].AsDictionary <string, object>();
            expected = goodbye.AsDictionary <string, object>();
            AssertPropertiesAreEqual(expected, result);

            rev = new RevisionInternal(properties);
            Database.StubOutAttachmentsInRevBeforeRevPos(rev, 1, false);
            checkAttachments = rev.GetProperties()["_attachments"].AsDictionary <string, object>();
            result           = checkAttachments["hello"].AsDictionary <string, object>();
            expected         = hello.AsDictionary <string, object>();
            AssertPropertiesAreEqual(expected, result);
            result   = checkAttachments["goodbye"].AsDictionary <string, object>();
            expected = goodbye.AsDictionary <string, object>();
            AssertPropertiesAreEqual(expected, result);

            //Test the follows mode
            rev = new RevisionInternal(properties);
            Database.StubOutAttachmentsInRevBeforeRevPos(rev, 3, true);
            checkAttachments   = rev.GetProperties()["_attachments"].AsDictionary <string, object>();
            result             = checkAttachments["hello"].AsDictionary <string, object>();
            expected           = new Dictionary <string, object>();
            expected["revpos"] = 1;
            expected["stub"]   = true;
            AssertPropertiesAreEqual(expected, result);
            result             = checkAttachments["goodbye"].AsDictionary <string, object>();
            expected           = new Dictionary <string, object>();
            expected["revpos"] = 2;
            expected["stub"]   = true;
            AssertPropertiesAreEqual(expected, result);

            rev = new RevisionInternal(properties);
            Database.StubOutAttachmentsInRevBeforeRevPos(rev, 2, true);
            checkAttachments   = rev.GetProperties()["_attachments"].AsDictionary <string, object>();
            result             = checkAttachments["hello"].AsDictionary <string, object>();
            expected           = new Dictionary <string, object>();
            expected["revpos"] = 1;
            expected["stub"]   = true;
            AssertPropertiesAreEqual(expected, result);
            result              = checkAttachments["goodbye"].AsDictionary <string, object>();
            expected            = new Dictionary <string, object>();
            expected["revpos"]  = 2;
            expected["follows"] = true;
            AssertPropertiesAreEqual(expected, result);

            rev = new RevisionInternal(properties);
            Database.StubOutAttachmentsInRevBeforeRevPos(rev, 1, true);
            checkAttachments    = rev.GetProperties()["_attachments"].AsDictionary <string, object>();
            result              = checkAttachments["hello"].AsDictionary <string, object>();
            expected            = new Dictionary <string, object>();
            expected["revpos"]  = 1;
            expected["follows"] = true;
            AssertPropertiesAreEqual(expected, result);
            result              = checkAttachments["goodbye"].AsDictionary <string, object>();
            expected            = new Dictionary <string, object>();
            expected["revpos"]  = 2;
            expected["follows"] = true;
            AssertPropertiesAreEqual(expected, result);
        }
Пример #2
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();
        }
Пример #3
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());
        }
Пример #4
0
 public void OnCompletion(object response, Exception e)
 {
     try
     {
         Log.V(Log.TagSync, "%s: got /_revs_diff response");
         IDictionary <string, object> results = (IDictionary <string, object>)response;
         if (e != null)
         {
             this._enclosing.SetError(e);
             this._enclosing.RevisionFailed();
         }
         else
         {
             if (results.Count != 0)
             {
                 // Go through the list of local changes again, selecting the ones the destination server
                 // said were missing and mapping them to a JSON dictionary in the form _bulk_docs wants:
                 IList <object> docsToSend = new AList <object>();
                 RevisionList   revsToSend = new RevisionList();
                 foreach (RevisionInternal rev in changes)
                 {
                     // Is this revision in the server's 'missing' list?
                     IDictionary <string, object> properties = null;
                     IDictionary <string, object> revResults = (IDictionary <string, object>)results.Get
                                                                   (rev.GetDocId());
                     if (revResults == null)
                     {
                         continue;
                     }
                     IList <string> revs = (IList <string>)revResults.Get("missing");
                     if (revs == null || !revs.Contains(rev.GetRevId()))
                     {
                         this._enclosing.RemovePending(rev);
                         continue;
                     }
                     // Get the revision's properties:
                     EnumSet <Database.TDContentOptions> contentOptions = EnumSet.Of(Database.TDContentOptions
                                                                                     .TDIncludeAttachments);
                     if (!this._enclosing.dontSendMultipart && this._enclosing.revisionBodyTransformationBlock
                         == null)
                     {
                         contentOptions.AddItem(Database.TDContentOptions.TDBigAttachmentsFollow);
                     }
                     RevisionInternal loadedRev;
                     try
                     {
                         loadedRev  = this._enclosing.db.LoadRevisionBody(rev, contentOptions);
                         properties = new Dictionary <string, object>(rev.GetProperties());
                     }
                     catch (CouchbaseLiteException)
                     {
                         Log.W(Log.TagSync, "%s Couldn't get local contents of %s", rev, this._enclosing);
                         this._enclosing.RevisionFailed();
                         continue;
                     }
                     RevisionInternal populatedRev      = this._enclosing.TransformRevision(loadedRev);
                     IList <string>   possibleAncestors = (IList <string>)revResults.Get("possible_ancestors"
                                                                                         );
                     properties = new Dictionary <string, object>(populatedRev.GetProperties());
                     IDictionary <string, object> revisions = this._enclosing.db.GetRevisionHistoryDictStartingFromAnyAncestor
                                                                  (populatedRev, possibleAncestors);
                     properties.Put("_revisions", revisions);
                     populatedRev.SetProperties(properties);
                     // Strip any attachments already known to the target db:
                     if (properties.ContainsKey("_attachments"))
                     {
                         // Look for the latest common ancestor and stub out older attachments:
                         int minRevPos = Couchbase.Lite.Replicator.Pusher.FindCommonAncestor(populatedRev,
                                                                                             possibleAncestors);
                         Database.StubOutAttachmentsInRevBeforeRevPos(populatedRev, minRevPos + 1, false);
                         properties = populatedRev.GetProperties();
                         if (!this._enclosing.dontSendMultipart && this._enclosing.UploadMultipartRevision
                                 (populatedRev))
                         {
                             continue;
                         }
                     }
                     if (properties == null || !properties.ContainsKey("_id"))
                     {
                         throw new InvalidOperationException("properties must contain a document _id");
                     }
                     revsToSend.AddItem(rev);
                     docsToSend.AddItem(properties);
                 }
                 //TODO: port this code from iOS
                 // Post the revisions to the destination:
                 this._enclosing.UploadBulkDocs(docsToSend, revsToSend);
             }
             else
             {
                 // None of the revisions are new to the remote
                 foreach (RevisionInternal revisionInternal in changes)
                 {
                     this._enclosing.RemovePending(revisionInternal);
                 }
             }
         }
     }
     finally
     {
         Log.V(Log.TagSync, "%s | %s: processInbox.sendAsyncRequest() calling asyncTaskFinished()"
               , this, Sharpen.Thread.CurrentThread());
         this._enclosing.AsyncTaskFinished(1);
     }
 }
Пример #5
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);
        }
        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.GetDocument(rev1.GetDocId(),
                                               rev1.GetRevId(), true);
            var gotAttachmentDict = gotRev1.GetPropertyForKey("_attachments").AsDictionary <string, object>();

            gotAttachmentDict[testAttachmentName] = gotAttachmentDict[testAttachmentName].AsDictionary <string, object>();

            var innerDict = new Dictionary <string, object>();

            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.CBLStatus.Code == StatusCode.Conflict);
            }
            Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;

            try
            {
                database.UpdateAttachment(testAttachmentName, new BlobStoreWriter(database.Attachments), "application/foo",
                                          AttachmentEncoding.None, rev1.GetDocId(), "1-fafafa");
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.CBLStatus.Code == 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.GetDocument(rev2.GetDocId(), rev2
                                                            .GetRevId(), true);

            attachmentDict = gotRev2.GetProperties().Get("_attachments").AsDictionary <string, object>();
            attachmentDict[testAttachmentName] = attachmentDict[testAttachmentName].AsDictionary <string, object>();
            innerDict = new Dictionary <string, object>();
            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.CBLStatus.Code == StatusCode.AttachmentNotFound);
            }
            Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;
            try
            {
                database.UpdateAttachment("nosuchattach", null, null,
                                          AttachmentEncoding.None, "nosuchdoc", "nosuchrev");
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.CBLStatus.Code == 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.GetDocument(rev3.GetDocId(), rev3
                                                            .GetRevId(), true);

            attachmentDict = gotRev3.GetProperties().Get("_attachments").AsDictionary <string, object>();
            Assert.IsNull(attachmentDict);
            database.Close();
        }
Пример #7
0
        // Apply the options in the URL query to the specified revision and create a new revision object
        internal 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)
                 | DocumentContentOptions.IncludeExpiration) != 0)
            {
                var dst = rev.GetProperties() ?? new Dictionary <string, object>();
                if (options.HasFlag(DocumentContentOptions.IncludeLocalSeq))
                {
                    dst["_local_seq"] = rev.Sequence;
                }

                if (options.HasFlag(DocumentContentOptions.IncludeRevs))
                {
                    var revs = db.GetRevisionHistory(rev, null);
                    dst["_revisions"] = TreeRevisionID.MakeRevisionHistoryDict(revs);
                }

                if (options.HasFlag(DocumentContentOptions.IncludeRevsInfo))
                {
                    dst["_revs_info"] = db.GetRevisionHistory(rev, null).Select(x =>
                    {
                        string status = "available";
                        var ancestor  = db.GetDocument(rev.DocID, x, true);
                        if (ancestor.Deleted)
                        {
                            status = "deleted";
                        }
                        else if (ancestor.Missing)
                        {
                            status = "missing";
                        }

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

                if (options.HasFlag(DocumentContentOptions.IncludeConflicts))
                {
                    RevisionList revs = db.Storage.GetAllDocumentRevisions(rev.DocID, true, false);
                    if (revs.Count > 1)
                    {
                        dst["_conflicts"] = from r in revs
                                            where !r.Equals(rev) && !r.Deleted
                                            select r.RevID.ToString();
                    }
                }

                if (options.HasFlag(DocumentContentOptions.IncludeExpiration))
                {
                    var expirationTime = db.Storage?.GetDocumentExpiration(rev.DocID);
                    if (expirationTime.HasValue)
                    {
                        dst["_exp"] = expirationTime;
                    }
                }

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

                rev = nuRev;
            }

            return(rev);
        }
        /// <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.Put("foo", 1);
            documentProperties.Put("bar", false);
            documentProperties.Put("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 <Database.TDContentOptions>());

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

            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.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 <
                                                           Database.TDContentOptions>());
            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 gotExpectedError = false;

            try
            {
                database.PutRevision(rev2input, rev1.GetRevId(), false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == Status.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, new _ReplicationFilter_95());
            NUnit.Framework.Assert.AreEqual(1, changes.Count);
            changes = database.ChangesSince(0, null, new _ReplicationFilter_105());
            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() == Status.Conflict;
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            NUnit.Framework.Assert.IsNull(revResult);
            revD = database.PutRevision(revD, rev2.GetRevId(), false, status);
            NUnit.Framework.Assert.AreEqual(Status.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() == Status.NotFound;
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            // Read it back (should fail):
            readRev = database.GetDocumentWithIDAndRev(revD.GetDocId(), null, EnumSet.NoneOf <
                                                           Database.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]);
        }
Пример #9
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);
        }
Пример #10
0
        public IList <QueryRow> QueryWithOptions(QueryOptions options)
        {
            if (options == null)
            {
                options = new QueryOptions();
            }
            Cursor           cursor = null;
            IList <QueryRow> rows   = new AList <QueryRow>();

            try
            {
                cursor = ResultSetWithOptions(options);
                int  groupLevel = options.GetGroupLevel();
                bool group      = options.IsGroup() || (groupLevel > 0);
                bool reduce     = options.IsReduce() || group;
                if (reduce && (reduceBlock == null) && !group)
                {
                    string msg = "Cannot use reduce option in view " + name + " which has no reduce block defined";
                    Log.W(Database.Tag, msg);
                    throw new CouchbaseLiteException(new Status(Status.BadRequest));
                }
                if (reduce || group)
                {
                    // Reduced or grouped query:
                    rows = ReducedQuery(cursor, group, groupLevel);
                }
                else
                {
                    // regular query
                    cursor.MoveToNext();
                    while (!cursor.IsAfterLast())
                    {
                        object keyData = FromJSON(cursor.GetBlob(0));
                        // TODO: delay parsing this for increased efficiency
                        object value = FromJSON(cursor.GetBlob(1));
                        // TODO: ditto
                        string docId    = cursor.GetString(2);
                        int    sequence = Sharpen.Extensions.ValueOf(cursor.GetString(3));
                        IDictionary <string, object> docContents = null;
                        if (options.IsIncludeDocs())
                        {
                            // http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views#Linked_documents
                            if (value is IDictionary && ((IDictionary)value).ContainsKey("_id"))
                            {
                                string           linkedDocId = (string)((IDictionary)value).Get("_id");
                                RevisionInternal linkedDoc   = database.GetDocumentWithIDAndRev(linkedDocId, null,
                                                                                                EnumSet.NoneOf <Database.TDContentOptions>());
                                docContents = linkedDoc.GetProperties();
                            }
                            else
                            {
                                docContents = database.DocumentPropertiesFromJSON(cursor.GetBlob(5), docId, cursor
                                                                                  .GetString(4), false, cursor.GetLong(3), options.GetContentOptions());
                            }
                        }
                        QueryRow row = new QueryRow(docId, sequence, keyData, value, docContents);
                        row.SetDatabase(database);
                        rows.AddItem(row);
                        cursor.MoveToNext();
                    }
                }
            }
            catch (SQLException e)
            {
                string errMsg = string.Format("Error querying view: %s", this);
                Log.E(Database.Tag, errMsg, e);
                throw new CouchbaseLiteException(errMsg, e, new Status(Status.DbError));
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
            }
            return(rows);
        }
Пример #11
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);
        }
Пример #12
0
 // This invokes the tranformation block if one is installed and queues the resulting CBL_Revision
 private void QueueDownloadedRevision(RevisionInternal rev)
 {
     if (revisionBodyTransformationBlock != null)
     {
         // Add 'file' properties to attachments pointing to their bodies:
         foreach (KeyValuePair <string, IDictionary <string, object> > entry in ((IDictionary
                                                                                  <string, IDictionary <string, object> >)rev.GetProperties().Get("_attachments")).EntrySet
                      ())
         {
             string name = entry.Key;
             IDictionary <string, object> attachment = entry.Value;
             Sharpen.Collections.Remove(attachment, "file");
             if (attachment.Get("follows") != null && attachment.Get("data") == null)
             {
                 string filePath = db.FileForAttachmentDict(attachment).AbsolutePath;
                 if (filePath != null)
                 {
                     attachment.Put("file", filePath);
                 }
             }
         }
         RevisionInternal xformed = TransformRevision(rev);
         if (xformed == null)
         {
             Log.V(Log.TagSync, "%s: Transformer rejected revision %s", this, rev);
             pendingSequences.RemoveSequence(rev.GetSequence());
             lastSequence = pendingSequences.GetCheckpointedValue();
             return;
         }
         rev = xformed;
         // Clean up afterwards
         IDictionary <string, object> attachments = (IDictionary <string, object>)rev.GetProperties
                                                        ().Get("_attachments");
         foreach (KeyValuePair <string, IDictionary <string, object> > entry_1 in ((IDictionary
                                                                                    <string, IDictionary <string, object> >)rev.GetProperties().Get("_attachments")).EntrySet
                      ())
         {
             IDictionary <string, object> attachment = entry_1.Value;
             Sharpen.Collections.Remove(attachment, "file");
         }
     }
     //TODO: rev.getBody().compact();
     Log.V(Log.TagSync, "%s | %s: queueDownloadedRevision() calling asyncTaskStarted()"
           , this, Sharpen.Thread.CurrentThread());
     AsyncTaskStarted();
     downloadsToInsert.QueueObject(rev);
 }
        public bool UpdateIndexes(IEnumerable <IViewStore> views)
        {
            Log.D(TAG, "Checking indexes of ({0}) for {1}", ViewNames(views), Name);

            // Creates an array of tuples -> [[view1, view1 last sequence, view1 native handle],
            // [view2, view2 last sequence, view2 native handle], ...]
            var viewsArray  = views.Cast <ForestDBViewStore>().ToArray();
            var viewInfo    = viewsArray.Select(x => Tuple.Create(x, x.LastSequenceIndexed)).ToArray();
            var nativeViews = new C4View *[viewsArray.Length];

            for (int i = 0; i < viewsArray.Length; i++)
            {
                nativeViews[i] = viewsArray[i]._indexDB;
            }

            var indexer = (C4Indexer *)ForestDBBridge.Check(err => Native.c4indexer_begin(_dbStorage.Forest, nativeViews, err));

            var enumerator = new CBForestDocEnumerator(indexer);

            var commit = false;

            try {
                foreach (var next in enumerator)
                {
                    var seq = next.Sequence;

                    for (int i = 0; i < viewInfo.Length; i++)
                    {
                        var info = viewInfo[i];
                        if (seq <= info.Item2)
                        {
                            continue; // This view has already indexed this sequence
                        }

                        var viewDelegate = info.Item1.Delegate;
                        if (viewDelegate == null || viewDelegate.Map == null)
                        {
                            Log.V(TAG, "    {0} has no map block; skipping it", info.Item1.Name);
                            continue;
                        }

                        var rev    = new RevisionInternal(next, true);
                        var keys   = new List <object>();
                        var values = new List <string>();

                        var conflicts = default(List <string>);
                        foreach (var leaf in new CBForestHistoryEnumerator(next, true, false))
                        {
                            if (leaf.SelectedRev.revID.Equals(leaf.CurrentRevID))
                            {
                                continue;
                            }

                            if (leaf.IsDeleted)
                            {
                                break;
                            }

                            if (conflicts == null)
                            {
                                conflicts = new List <string>();
                            }

                            conflicts.Add((string)leaf.SelectedRev.revID);
                        }

                        if (conflicts != null)
                        {
                            rev.SetPropertyForKey("_conflicts", conflicts);
                        }

                        try {
                            viewDelegate.Map(rev.GetProperties(), (key, value) =>
                            {
                                keys.Add(key);
                                values.Add(Manager.GetObjectMapper().WriteValueAsString(value));
                            });
                        } catch (Exception e) {
                            Log.W(TAG, String.Format("Exception thrown in map function of {0}", info.Item1.Name), e);
                            continue;
                        }

                        WithC4Keys(keys.ToArray(), true, c4keys =>
                                   ForestDBBridge.Check(err => Native.c4indexer_emit(indexer, next.GetDocument(), (uint)i, c4keys, values.ToArray(), err))
                                   );
                    }
                }

                commit = true;
            } catch (Exception e) {
                Log.W(TAG, "Error updates indexes", e);
            } finally {
                ForestDBBridge.Check(err => Native.c4indexer_end(indexer, commit, err));
            }

            return(true);
        }
Пример #14
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);
        }
Пример #15
0
        /// <summary>
        /// Returns document by the specified docid from the specified db. Unless you request a
        /// specific revision, the latest revision of the document will always be returned.
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        /// <remarks>
        /// http://docs.couchdb.org/en/latest/api/document/common.html#get--db-docid
        /// <remarks>
        public static ICouchbaseResponseState GetDocument(ICouchbaseListenerContext context)
        {
            return(DatabaseMethods.PerformLogicWithDatabase(context, true, db => {
                var response = context.CreateResponse();
                string docId = context.DocumentName;
                bool isLocalDoc = docId.StartsWith("_local");

                DocumentContentOptions options = context.ContentOptions;
                string openRevsParam = context.GetQueryParam("open_revs");
                bool mustSendJson = context.ExplicitlyAcceptsType("application/json");
                if (openRevsParam == null || isLocalDoc)
                {
                    //Regular GET:
                    string revId = context.GetQueryParam("rev"); //often null
                    RevisionInternal rev;
                    bool includeAttachments = false, sendMultipart = false;
                    if (isLocalDoc)
                    {
                        rev = db.GetLocalDocument(docId, revId);
                    }
                    else
                    {
                        includeAttachments = options.HasFlag(DocumentContentOptions.IncludeAttachments);
                        if (includeAttachments)
                        {
                            sendMultipart = !mustSendJson;
                            options &= ~DocumentContentOptions.IncludeAttachments;
                        }

                        Status status = new Status();
                        rev = db.GetDocumentWithIDAndRev(docId, revId, options, status);
                        if (rev != null)
                        {
                            rev = ApplyOptions(options, rev, context, db, status);
                        }

                        if (rev == null)
                        {
                            if (status.GetCode() == StatusCode.Deleted)
                            {
                                response.StatusReason = "deleted";
                            }
                            else
                            {
                                response.StatusReason = "missing";
                            }

                            response.InternalStatus = status.GetCode();
                            return response;
                        }
                    }

                    if (rev == null)
                    {
                        response.InternalStatus = StatusCode.NotFound;
                        return response;
                    }

                    if (context.CacheWithEtag(rev.GetRevId()))
                    {
                        response.InternalStatus = StatusCode.NotModified;
                        return response;
                    }

                    if (!isLocalDoc && includeAttachments)
                    {
                        int minRevPos = 1;
                        IList <string> attsSince = context.GetJsonQueryParam("atts_since").AsList <string>();
                        string ancestorId = db.FindCommonAncestor(rev, attsSince);
                        if (ancestorId != null)
                        {
                            minRevPos = RevisionInternal.GenerationFromRevID(ancestorId) + 1;
                        }

                        Status status = new Status();
                        bool attEncodingInfo = context.GetQueryParam <bool>("att_encoding_info", bool.TryParse, false);
                        if (!db.ExpandAttachments(rev, minRevPos, sendMultipart, attEncodingInfo, status))
                        {
                            response.InternalStatus = status.GetCode();
                            return response;
                        }
                    }

                    if (sendMultipart)
                    {
                        response.MultipartWriter = db.MultipartWriterForRev(rev, "multipart/related");
                    }
                    else
                    {
                        response.JsonBody = rev.GetBody();
                    }
                }
                else
                {
                    // open_revs query:
                    IList <IDictionary <string, object> > result;
                    if (openRevsParam.Equals("all"))
                    {
                        // ?open_revs=all returns all current/leaf revisions:
                        bool includeDeleted = context.GetQueryParam <bool>("include_deleted", bool.TryParse, false);
                        RevisionList allRevs = db.GetAllRevisionsOfDocumentID(docId, true);

                        result = new List <IDictionary <string, object> >();
                        foreach (var rev in allRevs)
                        {
                            if (!includeDeleted && rev.IsDeleted())
                            {
                                continue;
                            }

                            Status status = new Status();
                            RevisionInternal loadedRev = db.RevisionByLoadingBody(rev, status);
                            if (loadedRev != null)
                            {
                                ApplyOptions(options, loadedRev, context, db, status);
                            }

                            if (loadedRev != null)
                            {
                                result.Add(new Dictionary <string, object> {
                                    { "ok", loadedRev.GetProperties() }
                                });
                            }
                            else if (status.GetCode() <= StatusCode.InternalServerError)
                            {
                                result.Add(new Dictionary <string, object> {
                                    { "missing", rev.GetRevId() }
                                });
                            }
                            else
                            {
                                response.InternalStatus = status.GetCode();
                                return response;
                            }
                        }
                    }
                    else
                    {
                        // ?open_revs=[...] returns an array of specific revisions of the document:
                        var openRevs = context.GetJsonQueryParam("open_revs").AsList <object>();
                        if (openRevs == null)
                        {
                            response.InternalStatus = StatusCode.BadParam;
                            return response;
                        }

                        result = new List <IDictionary <string, object> >();
                        foreach (var revIDObj in openRevs)
                        {
                            var revID = revIDObj as string;
                            if (revID == null)
                            {
                                response.InternalStatus = StatusCode.BadId;
                                return response;
                            }

                            Status status = new Status();
                            var rev = db.GetDocumentWithIDAndRev(docId, revID, DocumentContentOptions.None, status);
                            if (rev != null)
                            {
                                rev = ApplyOptions(options, rev, context, db, status);
                            }

                            if (rev != null)
                            {
                                result.Add(new Dictionary <string, object> {
                                    { "ok", rev.GetProperties() }
                                });
                            }
                            else
                            {
                                result.Add(new Dictionary <string, object> {
                                    { "missing", revID }
                                });
                            }
                        }
                    }

                    if (mustSendJson)
                    {
                        response["Content-Type"] = "application/json";
                        response.JsonBody = new Body(result.Cast <object>().ToList());
                    }
                    else
                    {
                        response.SetMultipartBody(result.Cast <object>().ToList(), "multipart/mixed");
                    }
                }

                return response;
            }).AsDefaultState());
        }
Пример #16
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);
        }
Пример #17
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);
        }
Пример #18
0
 public bool ArePropertiesAvailable()
 {
     return(revisionInternal.GetProperties() != null);
 }
Пример #19
0
        /// <summary>
        /// Attempt to update a document based on the information in the HTTP request
        /// </summary>
        /// <returns>The resulting status of the operation</returns>
        /// <param name="context">The request context</param>
        /// <param name="db">The database in which the document exists</param>
        /// <param name="docId">The ID of the document being updated</param>
        /// <param name="body">The new document body</param>
        /// <param name="deleting">Whether or not the document is being deleted</param>
        /// <param name="allowConflict">Whether or not to allow a conflict to be inserted</param>
        /// <param name="outRev">The resulting revision of the document</param>
        public static StatusCode UpdateDocument(ICouchbaseListenerContext context, Database db, string docId, Body body, bool deleting,
                                                bool allowConflict, out RevisionInternal outRev)
        {
            outRev = null;
            if (body != null && !body.IsValidJSON())
            {
                return(StatusCode.BadJson);
            }

            string prevRevId;

            if (!deleting)
            {
                var properties = body.GetProperties();
                deleting = properties.GetCast <bool>("_deleted");
                if (docId == null)
                {
                    // POST's doc ID may come from the _id field of the JSON body.
                    docId = properties.CblID();
                    if (docId == null && deleting)
                    {
                        return(StatusCode.BadId);
                    }
                }

                // PUT's revision ID comes from the JSON body.
                prevRevId = properties.GetCast <string>("_rev");
            }
            else
            {
                // DELETE's revision ID comes from the ?rev= query param
                prevRevId = context.GetQueryParam("rev");
            }

            // A backup source of revision ID is an If-Match header:
            if (prevRevId == null)
            {
                prevRevId = context.IfMatch();
            }

            if (docId == null && deleting)
            {
                return(StatusCode.BadId);
            }

            RevisionInternal rev = new RevisionInternal(docId, null, deleting);

            rev.SetBody(body);

            // Check for doc expiration
            var expirationTime = default(DateTime?);
            var tmp            = default(object);
            var props          = rev.GetProperties();
            var hasValue       = false;

            if (props != null && props.TryGetValue("_exp", out tmp))
            {
                hasValue = true;
                if (tmp != null)
                {
                    try {
                        expirationTime = Convert.ToDateTime(tmp);
                    } catch (Exception) {
                        try {
                            var num = Convert.ToInt64(tmp);
                            expirationTime = Misc.OffsetFromEpoch(TimeSpan.FromSeconds(num));
                        } catch (Exception) {
                            Log.To.Router.E(TAG, "Invalid value for _exp: {0}", tmp);
                            return(StatusCode.BadRequest);
                        }
                    }
                }

                props.Remove("_exp");
                rev.SetProperties(props);
            }

            var        castContext = context as ICouchbaseListenerContext2;
            var        source      = castContext != null && !castContext.IsLoopbackRequest ? castContext.Sender : null;
            StatusCode status      = deleting ? StatusCode.Ok : StatusCode.Created;

            try {
                if (docId != null && docId.StartsWith("_local"))
                {
                    if (expirationTime.HasValue)
                    {
                        return(StatusCode.BadRequest);
                    }

                    outRev = db.Storage.PutLocalRevision(rev, prevRevId.AsRevID(), true); //TODO: Doesn't match iOS
                }
                else
                {
                    outRev = db.PutRevision(rev, prevRevId.AsRevID(), allowConflict, source);
                    if (hasValue)
                    {
                        db.Storage?.SetDocumentExpiration(rev.DocID, expirationTime);
                    }
                }
            } catch (CouchbaseLiteException e) {
                status = e.Code;
            }

            return(status);
        }
Пример #20
0
        /// <summary>Queries the view.</summary>
        /// <remarks>Queries the view. Does NOT first update the index.</remarks>
        /// <param name="options">The options to use.</param>
        /// <returns>An array of QueryRow objects.</returns>
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        internal IEnumerable <QueryRow> QueryWithOptions(QueryOptions options)
        {
            if (options == null)
            {
                options = new QueryOptions();
            }

            Cursor           cursor = null;
            IList <QueryRow> rows   = new AList <QueryRow>();

            try
            {
                cursor = ResultSetWithOptions(options);
                int groupLevel  = options.GetGroupLevel();
                var group       = options.IsGroup() || (groupLevel > 0);
                var reduce      = options.IsReduce() || group;
                var reduceBlock = Reduce;

                if (reduce && (reduceBlock == null) && !group)
                {
                    var msg = "Cannot use reduce option in view " + Name + " which has no reduce block defined";
                    Log.W(Database.Tag, msg);
                    throw new CouchbaseLiteException(StatusCode.BadRequest);
                }

                if (reduce || group)
                {
                    // Reduced or grouped query:
                    rows = ReducedQuery(cursor, group, groupLevel);
                }
                else
                {
                    // regular query
                    cursor.MoveToNext();
                    while (!cursor.IsAfterLast())
                    {
                        var keyData = FromJSON(cursor.GetBlob(0));
                        // TODO: delay parsing this for increased efficiency
                        var value = FromJSON(cursor.GetBlob(1));
                        // TODO: ditto
                        var docId        = cursor.GetString(2);
                        var sequenceLong = cursor.GetLong(3);
                        var sequence     = Convert.ToInt32(sequenceLong);

                        IDictionary <string, object> docContents = null;
                        if (options.IsIncludeDocs())
                        {
                            // http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views#Linked_documents
                            if (value is IDictionary <string, object> && ((IDictionary <string, object>)value).ContainsKey("_id"))
                            {
                                string           linkedDocId = (string)((IDictionary <string, object>)value).Get("_id");
                                RevisionInternal linkedDoc   = Database.GetDocumentWithIDAndRev(linkedDocId, null,
                                                                                                EnumSet.NoneOf <TDContentOptions>());
                                docContents = linkedDoc.GetProperties();
                            }
                            else
                            {
                                var revId = cursor.GetString(4);
                                docContents = Database.DocumentPropertiesFromJSON(cursor.GetBlob(5), docId, revId, false, sequenceLong, options.GetContentOptions());
                            }
                        }
                        var row = new QueryRow(docId, sequence, keyData, value, docContents);
                        row.Database = Database;
                        rows.AddItem <QueryRow>(row);  // NOTE.ZJG: Change to `yield return row` to convert to a generator.
                        cursor.MoveToNext();
                    }
                }
            }
            catch (SQLException e)
            {
                var errMsg = string.Format("Error querying view: {0}", this);
                Log.E(Database.Tag, errMsg, e);
                throw new CouchbaseLiteException(errMsg, e, new Status(StatusCode.DbError));
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
            }
            return(rows);
        }
Пример #21
0
        private bool UploadMultipartRevision(RevisionInternal revision)
        {
            MultipartContent multiPart = null;
            var length   = default(double);
            var revProps = revision.GetProperties();

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

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

            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);
                            length += json.Length;
                        } catch (Exception e) {
                            throw Misc.CreateExceptionAndLog(Log.To.Sync, e, TAG,
                                                             "Not able to serialize revision properties into a multipart request content.");
                        }
                    }

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

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

                    if (inputStream == null)
                    {
                        Log.To.Sync.W(TAG, "Unable to find blob file for blobKey: {0} - Skipping upload of multipart revision.", blobKey);
                        multiPart = null;
                        length    = 0;
                    }
                    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.To.Sync.W(TAG, message);
                            }
                        }

                        var content = new StreamContent(inputStream);
                        content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                        {
                            FileName = attachmentKey
                        };
                        content.Headers.ContentType = new MediaTypeHeaderValue(contentType ?? "application/octet-stream");

                        multiPart.Add(content);
                        length += inputStream.Length;
                    }
                }
            }

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

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

            // TODO: need to throttle these requests
            Log.To.Sync.D(TAG, "{0} uploading multipart request.  Revision: {1}", this, revision);
            SafeAddToChangesCount(1);
            _remoteSession.SendAsyncMultipartRequest(HttpMethod.Put, path, multiPart, (result, e) =>
            {
                if (e != null)
                {
                    var httpError = Misc.Flatten(e).FirstOrDefault(ex => ex is HttpResponseException) as HttpResponseException;
                    if (httpError != null)
                    {
                        if (httpError.StatusCode == System.Net.HttpStatusCode.UnsupportedMediaType)
                        {
                            _dontSendMultipart = true;
                            UploadJsonRevision(revision);
                        }
                    }
                    else
                    {
                        LastError = e;
                        RevisionFailed();
                    }
                }
                else
                {
                    Log.To.Sync.V(TAG, "{0} sent multipart {1}", this, revision);
                    SafeIncrementCompletedChangesCount();
                    RemovePending(revision);
                }
            });

            Log.To.Sync.V(TAG, "{0} queuing revision (multipart, {1}kb)", this, length / 1024.0);
            return(true);
        }
Пример #22
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());
        }