public void TestMakeRevisionHistoryDict() { var revs = new List <RevisionID>(); revs.Add("4-jkl".AsRevID()); revs.Add("3-ghi".AsRevID()); revs.Add("2-def".AsRevID()); var expectedSuffixes = new List <string>(); expectedSuffixes.Add("jkl"); expectedSuffixes.Add("ghi"); expectedSuffixes.Add("def"); var expectedHistoryDict = new Dictionary <string, object>(); expectedHistoryDict["start"] = 4; expectedHistoryDict["ids"] = expectedSuffixes; var historyDict = TreeRevisionID.MakeRevisionHistoryDict(revs); Assert.AreEqual(expectedHistoryDict, historyDict); revs = new List <RevisionID>(); revs.Add("4-jkl".AsRevID()); revs.Add("2-def".AsRevID()); expectedSuffixes = new List <string>(); expectedSuffixes.Add("4-jkl"); expectedSuffixes.Add("2-def"); expectedHistoryDict = new Dictionary <string, object>(); expectedHistoryDict["ids"] = expectedSuffixes; historyDict = TreeRevisionID.MakeRevisionHistoryDict(revs); Assert.AreEqual(expectedHistoryDict, historyDict); revs = new List <RevisionID>(); revs.Add("12345".AsRevID()); revs.Add("6789".AsRevID()); expectedSuffixes = new List <string>(); expectedSuffixes.Add("12345"); expectedSuffixes.Add("6789"); expectedHistoryDict = new Dictionary <string, object>(); expectedHistoryDict["ids"] = expectedSuffixes; historyDict = TreeRevisionID.MakeRevisionHistoryDict(revs); Assert.AreEqual(expectedHistoryDict, historyDict); }
// 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); }
private void UploadChanges(IList <RevisionInternal> changes, IDictionary <string, object> revsDiffResults) { // 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: var docsToSend = new List <object> (); var revsToSend = new RevisionList(); IDictionary <string, object> revResults = null; foreach (var rev in changes) { // Is this revision in the server's 'missing' list? if (revsDiffResults != null) { revResults = revsDiffResults.Get(rev.DocID).AsDictionary <string, object>(); if (revResults == null) { continue; } var revs = revResults.Get("missing").AsList <string>(); if (revs == null || !revs.Any(id => id.Equals(rev.RevID.ToString()))) { RemovePending(rev); continue; } } IDictionary <string, object> properties = null; RevisionInternal loadedRev; try { loadedRev = LocalDatabase.LoadRevisionBody(rev); if (loadedRev == null) { throw Misc.CreateExceptionAndLog(Log.To.Sync, StatusCode.NotFound, TAG, "Unable to load revision body"); } properties = new Dictionary <string, object>(rev.GetProperties()); } catch (Exception e1) { Log.To.Sync.E(TAG, String.Format("Couldn't get local contents of {0}, marking revision failed", rev), e1); RevisionFailed(); continue; } var populatedRev = TransformRevision(loadedRev); IList <RevisionID> possibleAncestors = null; if (revResults != null && revResults.ContainsKey("possible_ancestors")) { possibleAncestors = revResults["possible_ancestors"].AsList <RevisionID>(); } properties = new Dictionary <string, object>(populatedRev.GetProperties()); try { var history = LocalDatabase.GetRevisionHistory(populatedRev, possibleAncestors); if (history == null) { throw Misc.CreateExceptionAndLog(Log.To.Sync, StatusCode.DbError, TAG, "Unable to load revision history"); } properties["_revisions"] = TreeRevisionID.MakeRevisionHistoryDict(history); } catch (Exception e1) { Log.To.Sync.E(TAG, "Error getting revision history, marking revision failed", e1); RevisionFailed(); continue; } populatedRev.SetProperties(properties); if (properties.GetCast <bool>("_removed")) { RemovePending(rev); continue; } // Strip any attachments already known to the target db: if (properties.ContainsKey("_attachments")) { // Look for the latest common ancestor and stuf out older attachments: var minRevPos = FindCommonAncestor(populatedRev, possibleAncestors); try { LocalDatabase.ExpandAttachments(populatedRev, minRevPos + 1, !_dontSendMultipart, false); } catch (Exception ex) { Log.To.Sync.E(TAG, "Error expanding attachments, marking revision failed", ex); RevisionFailed(); continue; } properties = populatedRev.GetProperties(); if (!_dontSendMultipart && UploadMultipartRevision(populatedRev)) { continue; } } if (properties == null || !properties.ContainsKey("_id")) { throw Misc.CreateExceptionAndLog(Log.To.Sync, StatusCode.BadParam, TAG, "properties must contain a document _id"); } // Add the _revisions list: revsToSend.Add(rev); //now add it to the docs to send docsToSend.Add(properties); } UploadBulkDocs(docsToSend, revsToSend); }