예제 #1
0
        public SavedRevision Update(Document.DocumentUpdater updater)
        {
            int lastErrorCode = Status.Unknown;

            do
            {
                UnsavedRevision newRev = CreateRevision();
                if (updater.Update(newRev) == false)
                {
                    break;
                }
                try
                {
                    SavedRevision savedRev = newRev.Save();
                    if (savedRev != null)
                    {
                        return(savedRev);
                    }
                }
                catch (CouchbaseLiteException e)
                {
                    lastErrorCode = e.GetCBLStatus().GetCode();
                }
            }while (lastErrorCode == Status.Conflict);
            return(null);
        }
예제 #2
0
        /// <summary>Saves a new revision by letting the caller update the existing properties.
        ///     </summary>
        /// <remarks>
        /// Saves a new revision by letting the caller update the existing properties.
        /// This method handles conflicts by retrying (calling the block again).
        /// The DocumentUpdater implementation should modify the properties of the new revision and return YES to save or
        /// NO to cancel. Be careful: the DocumentUpdater can be called multiple times if there is a conflict!
        /// </remarks>
        /// <param name="updateDelegate">
        /// the callback DocumentUpdater implementation.  Will be called on each
        /// attempt to save. Should update the given revision's properties and then
        /// return YES, or just return NO to cancel.
        /// </param>
        /// <returns>The new saved revision, or null on error or cancellation.</returns>
        /// <exception cref="CouchbaseLiteException">CouchbaseLiteException</exception>
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public SavedRevision Update(UpdateDelegate updateDelegate)
        {
            Debug.Assert(updateDelegate != null);

            var lastErrorCode = StatusCode.Unknown;

            do
            {
                UnsavedRevision newRev = CreateRevision();
                if (!updateDelegate(newRev))
                {
                    break;
                }
                try
                {
                    SavedRevision savedRev = newRev.Save();
                    if (savedRev != null)
                    {
                        return(savedRev);
                    }
                }
                catch (CouchbaseLiteException e)
                {
                    lastErrorCode = e.GetCBLStatus().GetCode();
                }
            }while (lastErrorCode == StatusCode.Conflict);
            return(null);
        }
예제 #3
0
        /// <summary>
        /// Replaces the current revision's attachments with copies of the
        /// attachments from another revision.
        /// </summary>
        /// <param name="revision">The revision.</param>
        /// <param name="sourceRevision">The revision with the attachments to be copied.</param>
        public static void ReplaceAttachmentsFrom(this UnsavedRevision revision, Revision sourceRevision)
        {
            Covenant.Requires <ArgumentNullException>(revision != null);
            Covenant.Requires <ArgumentNullException>(sourceRevision != null);

            // Remove the current attachments.

            foreach (var name in revision.AttachmentNames.ToArray())
            {
                revision.RemoveAttachment(name);
            }

            // Copy the source attachments.

            foreach (var sourceAttachment in sourceRevision.Attachments)
            {
                // $todo(jeff.lill):
                //
                // I'm a little nervous about this call.  [Attachment.Content] is going to
                // load the attachment into memory, which could be a significant overhead.
                // It's possible to pass a stream, but looking at the Couchbase Lite source,
                // it appears that the new attachment would try to take ownership of the
                // source attachment's stream (if I passed it), probably ending badly.

                revision.SetAttachment(sourceAttachment.Name, sourceAttachment.ContentType, sourceAttachment.Content);
            }
        }
예제 #4
0
        /// <summary>
        /// Merges the attachments from another revision.
        /// </summary>
        /// <param name="revision">The revision.</param>
        /// <param name="sourceRevision">The revision with the attachments to be merged.</param>
        /// <param name="keepMine">Controls what happens when the source and target have the same attachment (see remarks).</param>
        /// <remarks>
        /// <para>
        /// The <paramref name="keepMine"/> parameter determines what happens when the source and target
        /// revisions has an attachment with the same name.  When <paramref name="keepMine"/>=<c>true</c>
        /// (the default), then matching attachments from the source revision will be ignored.
        /// </para>
        /// <para>
        /// When <paramref name="keepMine"/>=<c>false</c>, matching attachments from the source
        /// revision will be copied to the current revision, overwriting the existion attachment.
        /// </para>
        /// </remarks>
        public static void MergeAttachmentsFrom(this UnsavedRevision revision, Revision sourceRevision, bool keepMine = true)
        {
            Covenant.Requires <ArgumentNullException>(revision != null);
            Covenant.Requires <ArgumentNullException>(sourceRevision != null);

            // Merge the source attachments.

            foreach (var sourceAttachment in sourceRevision.Attachments)
            {
                var existingAttachment = revision.GetAttachment(sourceAttachment.Name);

                if (existingAttachment != null && keepMine)
                {
                    continue;
                }

                // $todo(jeff.lill):
                //
                // I'm a little nervous about this call.  [Attachment.Content] is going to
                // load the attachment into memory, which could be a significant overhead.
                // It's possible to pass a stream, but looking at the Couchbase Lite source,
                // it appears that the new attachment would try to take ownership of the
                // source attachment's stream (if I passed it), probably ending badly.

                revision.SetAttachment(sourceAttachment.Name, sourceAttachment.ContentType, sourceAttachment.Content);
            }
        }
예제 #5
0
        /// <summary>https://github.com/couchbase/couchbase-lite-java-core/issues/164</summary>
        /// <exception cref="System.Exception"></exception>
        public virtual void TestRevisionIdEquivalentRevisions()
        {
            // two revisions with the same content and the same json
            // should have the exact same revision id, because their content
            // will have an identical hash
            IDictionary <string, object> properties = new Dictionary <string, object>();

            properties.Put("testName", "testCreateRevisions");
            properties.Put("tag", 1337);
            IDictionary <string, object> properties2 = new Dictionary <string, object>();

            properties2.Put("testName", "testCreateRevisions");
            properties2.Put("tag", 1338);
            Document        doc    = database.CreateDocument();
            UnsavedRevision newRev = doc.CreateRevision();

            newRev.SetUserProperties(properties);
            SavedRevision   rev1     = newRev.Save();
            UnsavedRevision newRev2a = rev1.CreateRevision();

            newRev2a.SetUserProperties(properties2);
            SavedRevision   rev2a    = newRev2a.Save();
            UnsavedRevision newRev2b = rev1.CreateRevision();

            newRev2b.SetUserProperties(properties2);
            SavedRevision rev2b = newRev2b.Save(true);

            NUnit.Framework.Assert.AreEqual(rev2a.GetId(), rev2b.GetId());
        }
예제 #6
0
        /// <summary>Regression test for https://github.com/couchbase/couchbase-lite-android-core/issues/70
        ///     </summary>
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        /// <exception cref="System.IO.IOException"></exception>
        public virtual void TestAttachmentDisappearsAfterSave()
        {
            // create a doc with an attachment
            Document             doc     = database.CreateDocument();
            string               content = "This is a test attachment!";
            ByteArrayInputStream body    = new ByteArrayInputStream(Sharpen.Runtime.GetBytesForString
                                                                        (content));
            UnsavedRevision rev = doc.CreateRevision();

            rev.SetAttachment("index.html", "text/plain; charset=utf-8", body);
            rev.Save();
            // make sure the doc's latest revision has the attachment
            IDictionary <string, object> attachments = (IDictionary)doc.GetCurrentRevision().GetProperty
                                                           ("_attachments");

            NUnit.Framework.Assert.IsNotNull(attachments);
            NUnit.Framework.Assert.AreEqual(1, attachments.Count);
            // make sure the rev has the attachment
            attachments = (IDictionary)rev.GetProperty("_attachments");
            NUnit.Framework.Assert.IsNotNull(attachments);
            NUnit.Framework.Assert.AreEqual(1, attachments.Count);
            // create new properties to add
            IDictionary <string, object> properties = new Dictionary <string, object>();

            properties.Put("foo", "bar");
            // make sure the new rev still has the attachment
            UnsavedRevision rev2 = doc.CreateRevision();

            rev2.GetProperties().PutAll(properties);
            rev2.Save();
            attachments = (IDictionary)rev2.GetProperty("_attachments");
            NUnit.Framework.Assert.IsNotNull(attachments);
            NUnit.Framework.Assert.AreEqual(1, attachments.Count);
        }
예제 #7
0
 public static void AddImageAttachment(UnsavedRevision rev, string name, Image img)
 {
     if (img == null) {
         if (rev.AttachmentNames.Contains (name)) {
             rev.RemoveAttachment (name);
         }
     } else {
         rev.SetAttachment (name, "image/png", img.Serialize());
     }
 }
예제 #8
0
        /// <exception cref="System.Exception"></exception>
        public static SavedRevision CreateRevisionWithRandomProps(SavedRevision createRevFrom
                                                                  , bool allowConflict)
        {
            IDictionary <string, object> properties = new Dictionary <string, object>();

            properties.Put(UUID.RandomUUID().ToString(), "val");
            UnsavedRevision unsavedRevision = createRevFrom.CreateRevision();

            unsavedRevision.SetUserProperties(properties);
            return(unsavedRevision.Save(allowConflict));
        }
        /// <summary>https://github.com/couchbase/couchbase-lite-android/issues/134</summary>
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        /// <exception cref="System.IO.IOException"></exception>
        public virtual void TestGetAttachmentBodyUsingPrefetch()
        {
            // add a doc with an attachment
            Document        doc = database.CreateDocument();
            UnsavedRevision rev = doc.CreateRevision();
            IDictionary <string, object> properties = new Dictionary <string, object>();

            properties["foo"] = "bar";
            rev.SetUserProperties(properties);
            byte[]     attachBodyBytes = Sharpen.Runtime.GetBytesForString("attach body");
            Attachment attachment      = new Attachment(new ByteArrayInputStream(attachBodyBytes),
                                                        "text/plain");
            string attachmentName = "test_attachment.txt";

            rev.AddAttachment(attachment, attachmentName);
            rev.Save();
            // do query that finds that doc with prefetch
            View view = database.GetView("aview");

            view.SetMapReduce((IDictionary <string, object> document, EmitDelegate emitter) =>
            {
                string id = (string)document["_id"];
                emitter.Emit(id, null);
            }, null, "1");
            // try to get the attachment
            Query query = view.CreateQuery();

            query.Prefetch = true;
            QueryEnumerator results = query.Run();

            while (results.MoveNext())
            {
                QueryRow row = results.Current;
                // This returns the revision just fine, but the sequence number
                // is set to 0.
                SavedRevision  revision    = row.Document.CurrentRevision;
                IList <string> attachments = revision.AttachmentNames;
                // This returns an Attachment object which looks ok, except again
                // its sequence number is 0. The metadata property knows about
                // the length and mime type of the attachment. It also says
                // "stub" -> "true".
                Attachment attachmentRetrieved = revision.GetAttachment(attachmentName);
                // This throws a CouchbaseLiteException with StatusCode.NOT_FOUND.
                InputStream @is = attachmentRetrieved.GetContent();
                NUnit.Framework.Assert.IsNotNull(@is);
                byte[] attachmentDataRetrieved       = TextUtils.Read(@is);
                string attachmentDataRetrievedString = Sharpen.Runtime.GetStringForBytes(attachmentDataRetrieved
                                                                                         );
                string attachBodyString = Sharpen.Runtime.GetStringForBytes(attachBodyBytes);
                NUnit.Framework.Assert.AreEqual(attachBodyString, attachmentDataRetrievedString);
            }
        }
예제 #10
0
        /// <summary>
        /// Marks the revision as deleted while optionally retaining non-reserved properties.
        /// </summary>
        /// <param name="revision">The revision.</param>
        /// <param name="keepProperties">
        /// Optionally indicates that the non-reserved properties should be retained.
        /// This defaults to <c>false</c>.
        /// </param>
        public static void Delete(this UnsavedRevision revision, bool keepProperties = false)
        {
            Covenant.Requires <ArgumentNullException>(revision != null);

            revision.IsDeletion = true;

            if (!keepProperties)
            {
                foreach (var nonReservedKey in revision.Properties.Keys.Where(k => !k.StartsWith("_")).ToArray())
                {
                    revision.Properties.Remove(nonReservedKey);
                }
            }
        }
예제 #11
0
        /// <summary>https://github.com/couchbase/couchbase-lite-java-core/issues/106</summary>
        /// <exception cref="System.Exception"></exception>
        public virtual void TestResolveConflict()
        {
            IDictionary <string, object> properties = new Dictionary <string, object>();

            properties.Put("testName", "testCreateRevisions");
            properties.Put("tag", 1337);
            // Create a conflict on purpose
            Document        doc     = database.CreateDocument();
            UnsavedRevision newRev1 = doc.CreateRevision();

            newRev1.SetUserProperties(properties);
            SavedRevision rev1       = newRev1.Save();
            SavedRevision rev2a      = CreateRevisionWithRandomProps(rev1, false);
            SavedRevision rev2b      = CreateRevisionWithRandomProps(rev1, true);
            SavedRevision winningRev = null;
            SavedRevision losingRev  = null;

            if (doc.GetCurrentRevisionId().Equals(rev2a.GetId()))
            {
                winningRev = rev2a;
                losingRev  = rev2b;
            }
            else
            {
                winningRev = rev2b;
                losingRev  = rev2a;
            }
            NUnit.Framework.Assert.AreEqual(2, doc.GetConflictingRevisions().Count);
            NUnit.Framework.Assert.AreEqual(2, doc.GetLeafRevisions().Count);
            // let's manually choose the losing rev as the winner.  First, delete winner, which will
            // cause losing rev to be the current revision.
            SavedRevision         deleteRevision       = winningRev.DeleteDocument();
            IList <SavedRevision> conflictingRevisions = doc.GetConflictingRevisions();

            NUnit.Framework.Assert.AreEqual(1, conflictingRevisions.Count);
            NUnit.Framework.Assert.AreEqual(2, doc.GetLeafRevisions().Count);
            NUnit.Framework.Assert.AreEqual(3, deleteRevision.GetGeneration());
            NUnit.Framework.Assert.AreEqual(losingRev.GetId(), doc.GetCurrentRevision().GetId
                                                ());
            // Finally create a new revision rev3 based on losing rev
            SavedRevision rev3 = CreateRevisionWithRandomProps(losingRev, true);

            NUnit.Framework.Assert.AreEqual(rev3.GetId(), doc.GetCurrentRevisionId());
            IList <SavedRevision> conflictingRevisions1 = doc.GetConflictingRevisions();

            NUnit.Framework.Assert.AreEqual(1, conflictingRevisions1.Count);
            NUnit.Framework.Assert.AreEqual(2, doc.GetLeafRevisions().Count);
        }
예제 #12
0
        /// <exception cref="System.Exception"></exception>
        public virtual void TestWinningRevIDOfDoc()
        {
            IDictionary <string, object> properties = new Dictionary <string, object>();

            properties.Put("testName", "testCreateRevisions");
            properties.Put("tag", 1337);
            IDictionary <string, object> properties2a = new Dictionary <string, object>();

            properties2a.Put("testName", "testCreateRevisions");
            properties2a.Put("tag", 1338);
            IDictionary <string, object> properties2b = new Dictionary <string, object>();

            properties2b.Put("testName", "testCreateRevisions");
            properties2b.Put("tag", 1339);
            IList <bool> outIsDeleted  = new AList <bool>();
            IList <bool> outIsConflict = new AList <bool>();
            // Create a conflict on purpose
            Document        doc     = database.CreateDocument();
            UnsavedRevision newRev1 = doc.CreateRevision();

            newRev1.SetUserProperties(properties);
            SavedRevision rev1         = newRev1.Save();
            long          docNumericId = database.GetDocNumericID(doc.GetId());

            NUnit.Framework.Assert.IsTrue(docNumericId != 0);
            NUnit.Framework.Assert.AreEqual(rev1.GetId(), database.WinningRevIDOfDoc(docNumericId
                                                                                     , outIsDeleted, outIsConflict));
            NUnit.Framework.Assert.IsTrue(outIsConflict.Count == 0);
            outIsDeleted  = new AList <bool>();
            outIsConflict = new AList <bool>();
            UnsavedRevision newRev2a = rev1.CreateRevision();

            newRev2a.SetUserProperties(properties2a);
            SavedRevision rev2a = newRev2a.Save();

            NUnit.Framework.Assert.AreEqual(rev2a.GetId(), database.WinningRevIDOfDoc(docNumericId
                                                                                      , outIsDeleted, outIsConflict));
            NUnit.Framework.Assert.IsTrue(outIsConflict.Count == 0);
            outIsDeleted  = new AList <bool>();
            outIsConflict = new AList <bool>();
            UnsavedRevision newRev2b = rev1.CreateRevision();

            newRev2b.SetUserProperties(properties2b);
            SavedRevision rev2b = newRev2b.Save(true);

            database.WinningRevIDOfDoc(docNumericId, outIsDeleted, outIsConflict);
            NUnit.Framework.Assert.IsTrue(outIsConflict.Count > 0);
        }
예제 #13
0
        /// <summary>https://github.com/couchbase/couchbase-lite-java-core/issues/164</summary>
        /// <exception cref="System.Exception"></exception>
        public virtual void TestRevisionIdDifferentRevisions()
        {
            // two revisions with different json should have different rev-id's
            // because their content will have a different hash (even though
            // they have the same generation number)
            IDictionary <string, object> properties = new Dictionary <string, object>();

            properties.Put("testName", "testCreateRevisions");
            properties.Put("tag", 1337);
            Document        doc    = database.CreateDocument();
            UnsavedRevision newRev = doc.CreateRevision();

            newRev.SetUserProperties(properties);
            SavedRevision rev1  = newRev.Save();
            SavedRevision rev2a = CreateRevisionWithRandomProps(rev1, false);
            SavedRevision rev2b = CreateRevisionWithRandomProps(rev1, true);

            NUnit.Framework.Assert.AreNotSame(rev2a.GetId(), rev2b.GetId());
        }
예제 #14
0
        /// <summary>
        /// Creates and saves a new <see cref="Couchbase.Lite.Revision"/> by allowing the caller to update
        /// the existing properties. Conflicts are handled by calling the delegate again.
        /// </summary>
        /// <remarks>
        /// Saves a new revision by letting the caller update the existing properties.
        /// This method handles conflicts by retrying (calling the block again).
        /// The DocumentUpdater implementation should modify the properties of the new revision and return YES to save or
        /// NO to cancel. Be careful: the DocumentUpdater can be called multiple times if there is a conflict!
        /// </remarks>
        /// <param name="updateDelegate">
        /// The delegate that will be called to update the new <see cref="Couchbase.Lite.Revision"/>'s properties.
        /// return YES, or just return NO to cancel.
        /// </param>
        /// <returns>The new <see cref="Couchbase.Lite.SavedRevision"/>, or null on error or cancellation.</returns>
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException">
        /// Thrown if an error occurs while creating or saving the new <see cref="Couchbase.Lite.Revision"/>.
        /// </exception>
        public SavedRevision Update(UpdateDelegate updateDelegate)
        {
            Debug.Assert(updateDelegate != null);
            if (updateDelegate == null)
            {
                return(null);
            }

            var lastErrorCode = StatusCode.Unknown;

            do
            {
                // Force the database to load the current revision
                // from disk, which will happen when CreateRevision
                // sees that currentRevision is null.
                if (lastErrorCode == StatusCode.Conflict)
                {
                    currentRevision = null;
                }

                using (UnsavedRevision newRev = CreateRevision()) {
                    if (!updateDelegate(newRev))
                    {
                        break;
                    }

                    try {
                        SavedRevision savedRev = newRev.Save();
                        if (savedRev != null)
                        {
                            return(savedRev);
                        }
                    } catch (CouchbaseLiteException e) {
                        lastErrorCode = e.CBLStatus.Code;
                    }
                }
            } while(lastErrorCode == StatusCode.Conflict);

            return(null);
        }
예제 #15
0
        /// <exception cref="System.Exception"></exception>
        public static Document CreateDocWithAttachment(Database database, string attachmentName
                                                       , string content)
        {
            IDictionary <string, object> properties = new Dictionary <string, object>();

            properties.Put("foo", "bar");
            Document      doc = CreateDocumentWithProperties(database, properties);
            SavedRevision rev = doc.GetCurrentRevision();

            NUnit.Framework.Assert.AreEqual(rev.GetAttachments().Count, 0);
            NUnit.Framework.Assert.AreEqual(rev.GetAttachmentNames().Count, 0);
            NUnit.Framework.Assert.IsNull(rev.GetAttachment(attachmentName));
            ByteArrayInputStream body = new ByteArrayInputStream(Sharpen.Runtime.GetBytesForString
                                                                     (content));
            UnsavedRevision rev2 = doc.CreateRevision();

            rev2.SetAttachment(attachmentName, "text/plain; charset=utf-8", body);
            SavedRevision rev3 = rev2.Save();

            NUnit.Framework.Assert.IsNotNull(rev3);
            NUnit.Framework.Assert.AreEqual(rev3.GetAttachments().Count, 1);
            NUnit.Framework.Assert.AreEqual(rev3.GetAttachmentNames().Count, 1);
            Attachment attach = rev3.GetAttachment(attachmentName);

            NUnit.Framework.Assert.IsNotNull(attach);
            NUnit.Framework.Assert.AreEqual(doc, attach.GetDocument());
            NUnit.Framework.Assert.AreEqual(attachmentName, attach.GetName());
            IList <string> attNames = new AList <string>();

            attNames.AddItem(attachmentName);
            NUnit.Framework.Assert.AreEqual(rev3.GetAttachmentNames(), attNames);
            NUnit.Framework.Assert.AreEqual("text/plain; charset=utf-8", attach.GetContentType
                                                ());
            NUnit.Framework.Assert.AreEqual(IOUtils.ToString(attach.GetContent(), "UTF-8"), content
                                            );
            NUnit.Framework.Assert.AreEqual(Sharpen.Runtime.GetBytesForString(content).Length
                                            , attach.GetLength());
            return(doc);
        }
	protected void EmitBaseData(UnsavedRevision rev, SaveData dataToSave)
	{
		if (dataToSave.HasFlag (SaveData.PositionXY)) {
			var pos = transform.position;
			rev.Properties ["CouchbaseObject.Position.X"] = pos.x;
			rev.Properties ["CouchbaseObject.Position.Y"] = pos.y;

			if(dataToSave.HasFlag(SaveData.PositionZ)) {
				rev.Properties ["CouchbaseObject.Position.Z"] = pos.z;
			}
		}

		if (dataToSave.HasFlag (SaveData.Rotation)) {
			transform.rotation.InsertIntoDictionary(rev.Properties, ROTATION_KEYS);
		}

		if (dataToSave.HasFlag (SaveData.Scale)) {
			transform.localScale.InsertIntoDictionary(rev.Properties, SCALE_KEYS);
		}

		if (dataToSave.HasFlag (SaveData.Color)) {
			gameObject.GetComponent<MeshRenderer>().material.color.InsertIntoDictionary(rev.Properties, COLOR_KEYS);
		}
	}
 /// <summary>
 /// Creates a new <see cref="Couchbase.Lite.UnsavedRevision"/> whose properties and attachments are initially identical to this one.
 /// </summary>
 /// <remarks>
 /// Creates a new mutable child revision whose properties and attachments are initially identical
 /// to this one's, which you can modify and then save.
 /// </remarks>
 /// <returns>
 /// A new child <see cref="Couchbase.Lite.UnsavedRevision"/> whose properties and attachments 
 /// are initially identical to this one.
 /// </returns>
 public UnsavedRevision CreateRevision() {
     var newRevision = new UnsavedRevision(Document, this);
     return newRevision;
 }
	protected override bool UpdateRevision (UnsavedRevision rev)
	{
		rev.Properties ["type"] = "CouchbaseSphere";
		EmitBaseData (rev, SaveData.Position | SaveData.Color);
		return true;
	}
예제 #19
0
        /// <summary>
        /// Creates a new <see cref="Couchbase.Lite.UnsavedRevision"/> whose properties and attachments are initially identical to this one.
        /// </summary>
        /// <remarks>
        /// Creates a new mutable child revision whose properties and attachments are initially identical
        /// to this one's, which you can modify and then save.
        /// </remarks>
        /// <returns>
        /// A new child <see cref="Couchbase.Lite.UnsavedRevision"/> whose properties and attachments
        /// are initially identical to this one.
        /// </returns>
        public UnsavedRevision CreateRevision()
        {
            var newRevision = new UnsavedRevision(Document, this);

            return(newRevision);
        }
	protected abstract bool UpdateRevision (UnsavedRevision rev);