// 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);
        }
 public virtual void SetContentOptions(DocumentContentOptions contentOptions
     )
 {
     this.contentOptions = contentOptions;
 }
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        internal RevisionInternal LoadRevisionBody(RevisionInternal rev, DocumentContentOptions contentOptions)
        {
            if (rev.GetBody() != null && contentOptions == DocumentContentOptions.None && rev.GetSequence() != 0)
            {
                return rev;
            }

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

                cursor = StorageEngine.RawQuery(sql, CommandBehavior.SequentialAccess, args);
                if (cursor.MoveToNext())
                {
                    result.SetCode(StatusCode.Ok);
                    rev.SetSequence(cursor.GetLong(0));
                    ExpandStoredJSONIntoRevisionWithAttachments(cursor.GetBlob(1), rev, contentOptions);
                }
            }
            catch (SQLException e)
            {
                Log.E(Tag, "Error loading revision body", e);
                throw new CouchbaseLiteException(StatusCode.InternalServerError);
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
            }
            if (result.GetCode() == StatusCode.NotFound)
            {
                throw new CouchbaseLiteException(result.GetCode());
            }
            return rev;
        }
        /// <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", StringComparison.InvariantCulture);

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


                        rev = db.GetDocument(docId, revId, true, status);
                        if (rev != null)
                        {
                            rev = ApplyOptions(options, rev, context, db, status);
                        }

                        if (rev == null)
                        {
                            response.StatusReason = status.Code == StatusCode.Deleted ? "deleted" : "missing";
                            response.InternalStatus = StatusCode.NotFound;
                            return response;
                        }
                    }

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

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

                    if (!isLocalDoc && includeAttachments)
                    {
                        int minRevPos = 1;
                        var attsSince = context.GetJsonQueryParam("atts_since")?.AsList <string>()?.AsRevIDs();
                        var ancestorId = db.Storage.FindCommonAncestor(rev, attsSince);
                        if (ancestorId != null)
                        {
                            minRevPos = ancestorId.Generation + 1;
                        }

                        bool attEncodingInfo = context.GetQueryParam <bool>("att_encoding_info", bool.TryParse, false);
                        db.ExpandAttachments(rev, minRevPos, sendMultipart, attEncodingInfo);
                    }

                    if (sendMultipart)
                    {
                        response.MultipartWriter = MultipartWriterForRev(db, 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.Storage.GetAllDocumentRevisions(docId, true, includeDeleted);

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

                            var 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.Code <= StatusCode.InternalServerError)
                            {
                                result.Add(new Dictionary <string, object> {
                                    { "missing", rev.RevID }
                                });
                            }
                            else
                            {
                                response.InternalStatus = status.Code;
                                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;
                            }

                            var rev = db.GetDocument(docId, revID.AsRevID(), true);
                            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());
        }
        /// <summary>Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev.
        ///     </summary>
        /// <remarks>
        /// Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev.
        /// Rev must already have its revID and sequence properties set.
        /// </remarks>
        internal IDictionary<String, Object> ExtraPropertiesForRevision(RevisionInternal rev, DocumentContentOptions contentOptions)
        {
            var docId = rev.GetDocId();
            var revId = rev.GetRevId();

            var sequenceNumber = rev.GetSequence();

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

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

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

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

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

            if (rev.IsDeleted())
            {
                result["_deleted"] = true;
            }
            if (attachmentsDict != null)
            {
                result["_attachments"] = attachmentsDict;
            }
            if (localSeq > -1)
            {
                result["_local_seq"] = localSeq;
            }
            if (revHistory != null)
            {
                result["_revisions"] = revHistory;
            }
            if (revsInfo != null)
            {
                result["_revs_info"] = revsInfo;
            }
            if (conflicts != null)
            {
                result["_conflicts"] = conflicts;
            }
            return result;
        }
        /// <summary>Constructs an "_attachments" dictionary for a revision, to be inserted in its JSON body.</summary>
        internal IDictionary<String, Object> GetAttachmentsDictForSequenceWithContent(long sequence, DocumentContentOptions contentOptions)
        {
            Debug.Assert((sequence > 0));

            Cursor cursor = null;
            var args = new Object[] { sequence };

            try
            {
                cursor = StorageEngine.RawQuery("SELECT filename, key, type, length, revpos FROM attachments WHERE sequence=?", CommandBehavior.SequentialAccess, args);
                if (!cursor.MoveToNext())
                {
                    return null;
                }

                var result = new Dictionary<String, Object>();

                while (!cursor.IsAfterLast())
                {
                    var dataSuppressed = false;
                    var filename = cursor.GetString(0);
                    var keyData = cursor.GetBlob(1);
                    var contentType = cursor.GetString(2);
                    var length = cursor.GetInt(3);
                    var revpos = cursor.GetInt(4);

                    var key = new BlobKey(keyData);
                    var digestString = "sha1-" + Convert.ToBase64String(keyData);

                    var dataBase64 = (string) null;
                    if (contentOptions.HasFlag(DocumentContentOptions.IncludeAttachments))
                    {
                        if (contentOptions.HasFlag(DocumentContentOptions.BigAttachmentsFollow) && 
                            length >= Database.BigAttachmentLength)
                        {
                            dataSuppressed = true;
                        }
                        else
                        {
                            byte[] data = Attachments.BlobForKey(key);
                            if (data != null)
                            {
                                dataBase64 = Convert.ToBase64String(data);
                            }
                            else
                            {
                                // <-- very expensive
                                Log.W(Tag, "Error loading attachment");
                            }
                        }
                    }
                    var attachment = new Dictionary<string, object>();
                    if (!(dataBase64 != null || dataSuppressed))
                    {
                        attachment["stub"] = true;
                    }
                    if (dataBase64 != null)
                    {
                        attachment["data"] = dataBase64;
                    }
                    if (dataSuppressed) {
                        attachment.Put ("follows", true);
                    }
                    attachment["digest"] = digestString;

                    attachment["content_type"] = contentType;
                    attachment["length"] = length;
                    attachment["revpos"] = revpos;

                    result[filename] = attachment;

                    cursor.MoveToNext();
                }
                return result;
            }
            catch (SQLException e)
            {
                Log.E(Tag, "Error getting attachments for sequence", e);
                return null;
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
            }
        }
 internal RevisionInternal GetDocumentWithIDAndRev(String id, String rev, DocumentContentOptions contentOptions)
 {
     RevisionInternal result = null;
     string sql;
     Cursor cursor = null;
     try
     {
         cursor = null;
         var cols = "revid, deleted, sequence, no_attachments";
         if (!contentOptions.HasFlag(DocumentContentOptions.NoBody))
         {
             cols += ", json";
         }
         if (rev != null)
         {
             sql = "SELECT " + cols + " FROM revs, docs WHERE docs.docid=? AND revs.doc_id=docs.doc_id AND revid=? LIMIT 1";
             //TODO: mismatch w iOS: {sql = "SELECT " + cols + " FROM revs WHERE revs.doc_id=? AND revid=? AND json notnull LIMIT 1";}
             var args = new[] { id, rev };
             cursor = StorageEngine.RawQuery(sql, args);
         }
         else
         {
             sql = "SELECT " + cols + " FROM revs, docs WHERE docs.docid=? AND revs.doc_id=docs.doc_id and current=1 and deleted=0 ORDER BY revid DESC LIMIT 1";
             //TODO: mismatch w iOS: {sql = "SELECT " + cols + " FROM revs WHERE revs.doc_id=? and current=1 and deleted=0 ORDER BY revid DESC LIMIT 1";}
             var args = new[] { id };
             cursor = StorageEngine.RawQuery(sql, CommandBehavior.SequentialAccess, args);
         }
         if (cursor.MoveToNext())
         {
             if (rev == null)
             {
                 rev = cursor.GetString(0);
             }
             var deleted = cursor.GetInt(1) > 0;
             result = new RevisionInternal(id, rev, deleted, this);
             result.SetSequence(cursor.GetLong(2));
             if (contentOptions != DocumentContentOptions.NoBody)
             {
                 byte[] json = null;
                 if (!contentOptions.HasFlag(DocumentContentOptions.NoBody))
                 {
                     json = cursor.GetBlob(4);
                 }
                 if (cursor.GetInt(3) > 0)
                 {
                     // no_attachments == true
                     contentOptions |= DocumentContentOptions.NoAttachments;
                 }
                 ExpandStoredJSONIntoRevisionWithAttachments(json, result, contentOptions);
             }
         }
     }
     catch (Exception e)
     {
         Log.E(Tag, "Error getting document with id and rev", e);
     }
     finally
     {
         if (cursor != null)
         {
             cursor.Close();
         }
     }
     return result;
 }
        /// <summary>Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev.
        ///     </summary>
        /// <remarks>
        /// Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev.
        /// Rev must already have its revID and sequence properties set.
        /// </remarks>
        internal void ExpandStoredJSONIntoRevisionWithAttachments(IEnumerable<Byte> json, RevisionInternal rev, DocumentContentOptions contentOptions)
        {
            var extra = ExtraPropertiesForRevision(rev, contentOptions);

            if (json != null && json.Any())
            {
                rev.SetJson(AppendDictToJSON(json, extra));
            }
            else
            {
                rev.SetProperties(extra);
                if (json == null)
                {
                    rev.SetMissing(true);
                }
            }
        }
        // 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)) != 0) {
                var dst = rev.GetProperties(); 
                if (options.HasFlag(DocumentContentOptions.IncludeLocalSeq)) {
                    dst["_local_seq"] = rev.GetSequence();
                }

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

                if (options.HasFlag(DocumentContentOptions.IncludeRevsInfo)) {
                    dst["_revs_info"] = db.Storage.GetRevisionHistory(rev, null).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.Storage.GetAllDocumentRevisions(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 != null && context.GetQueryParam<bool>("att_encoding_info", bool.TryParse, false);
                    if(!db.ExpandAttachments(nuRev, 0, false, !attEncodingInfo, outStatus)) {
                        return null;
                    }
                }

                rev = nuRev;
            }

            return rev;
        }
        internal IDictionary<String, Object> DocumentPropertiesFromJSON(IEnumerable<Byte> json, String docId, String revId, Boolean deleted, Int64 sequence, DocumentContentOptions contentOptions)
        {
            var rev = new RevisionInternal(docId, revId, deleted, this);
            rev.SetSequence(sequence);

            IDictionary<String, Object> extra = ExtraPropertiesForRevision(rev, contentOptions);
            if (json == null)
            {
                return extra;
            }

            IDictionary<String, Object> docProperties = null;
            try
            {
                docProperties = Manager.GetObjectMapper().ReadValue<IDictionary<string, object>>(json);
                docProperties.PutAll(extra);
            }
            catch (Exception e)
            {
                Log.E(Tag, "Error serializing properties to JSON", e);
            }
            return docProperties;
        }
 public void SetContentOptions(DocumentContentOptions contentOptions )
 {
     ContentOptions = contentOptions;
 }
Beispiel #12
0
 public void SetContentOptions(DocumentContentOptions contentOptions
                               )
 {
     this.contentOptions = contentOptions;
 }
        public void TestPutLargeAttachment()
        {
            var testAttachmentName = "test_attachment";
            var attachments        = database.Attachments;

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

            var status         = new Status();
            var rev1Properties = new Dictionary <string, object>();

            rev1Properties["foo"] = 1;
            rev1Properties["bar"] = false;
            database.BeginTransaction();
            var rev1 = database.PutRevision(new RevisionInternal(rev1Properties), null, false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());
            var largeAttachment = new StringBuilder();

            for (int i = 0; i < Database.BigAttachmentLength; i++)
            {
                largeAttachment.Append("big attachment!");
            }

            var attach1 = Encoding.UTF8.GetBytes(largeAttachment.ToString());

            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach1), rev1.GetSequence(),
                testAttachmentName, "text/plain", rev1.GetGeneration());
            database.EndTransaction(true);
            var attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName);

            Assert.AreEqual("text/plain", attachment.ContentType);
            var data = attachment.Content.ToArray();

            Assert.IsTrue(Arrays.Equals(attach1, data));
            attachment.Dispose();

            const DocumentContentOptions contentOptions = DocumentContentOptions.IncludeAttachments | DocumentContentOptions.BigAttachmentsFollow;
            var attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1.GetSequence(), contentOptions);
            var innerDict = (IDictionary <string, object>)attachmentDictForSequence[testAttachmentName];

            if (innerDict.ContainsKey("stub"))
            {
                if (((bool)innerDict["stub"]))
                {
                    throw new RuntimeException("Expected attachment dict 'stub' key to be true");
                }
                else
                {
                    throw new RuntimeException("Expected attachment dict to have 'stub' key");
                }
            }
            if (!innerDict.ContainsKey("follows"))
            {
                throw new RuntimeException("Expected attachment dict to have 'follows' key");
            }

            attachment.Dispose();

            var rev1WithAttachments = database.GetDocumentWithIDAndRev(
                rev1.GetDocId(), rev1.GetRevId(), contentOptions);

            var rev1WithAttachmentsProperties = rev1WithAttachments.GetProperties();
            var rev2Properties = new Dictionary <string, object>();

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

            Assert.AreEqual(StatusCode.Created, status.GetCode());
            database.CopyAttachmentNamedFromSequenceToSequence(
                testAttachmentName, rev1WithAttachments.GetSequence(), rev2.GetSequence());
            database.EndTransaction(true);

            // Check the 2nd revision's attachment:
            var rev2FetchedAttachment = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName);

            Assert.AreEqual(attachment.Length, rev2FetchedAttachment.Length);
            AssertPropertiesAreEqual(attachment.Metadata, rev2FetchedAttachment.Metadata);
            Assert.AreEqual(attachment.ContentType, rev2FetchedAttachment.ContentType);
            rev2FetchedAttachment.Dispose();

            // Add a third revision of the same document:
            var rev3Properties = new Dictionary <string, object>();

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

            rev3 = database.PutRevision(rev3, rev2.GetRevId(), false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());
            var attach3 = Encoding.UTF8.GetBytes("<html><blink>attach3</blink></html>");

            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach3), rev3.GetSequence(),
                testAttachmentName, "text/html", rev3.GetGeneration());

            // Check the 3rd revision's attachment:
            var rev3FetchedAttachment = database.GetAttachmentForSequence(
                rev3.GetSequence(), testAttachmentName);

            data = rev3FetchedAttachment.Content.ToArray();
            Assert.IsTrue(Arrays.Equals(attach3, data));
            Assert.AreEqual("text/html", rev3FetchedAttachment.ContentType);
            rev3FetchedAttachment.Dispose();

            // TODO: why doesn't this work?
            // Assert.assertEquals(attach3.length, rev3FetchedAttachment.getLength());
            var blobKeys = database.Attachments.AllKeys();

            Assert.AreEqual(2, blobKeys.Count);
            database.Compact();
            blobKeys = database.Attachments.AllKeys();
            Assert.AreEqual(1, blobKeys.Count);
        }
Beispiel #14
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);
        }
        // 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;
        }