public void TestChangeNotification() { var changeNotifications = 0; EventHandler<DatabaseChangeEventArgs> handler = (sender, e) => changeNotifications++; database.Changed += handler; // create a document var documentProperties = new Dictionary<string, object>(); documentProperties["foo"] = 1; documentProperties["bar"] = false; documentProperties["baz"] = "touch"; var body = new Body(documentProperties); var rev1 = new RevisionInternal(body, database); var status = new Status(); database.PutRevision(rev1, null, false, status); Assert.AreEqual(1, changeNotifications); // Analysis disable once DelegateSubtraction database.Changed -= handler; }
public void TestChangeNotification() { var countDown = new CountdownEvent(1); EventHandler<DatabaseChangeEventArgs> handler = (sender, e) => countDown.Signal(); database.Changed += handler; // create a document var documentProperties = new Dictionary<string, object>(); documentProperties["foo"] = 1; documentProperties["bar"] = false; documentProperties["baz"] = "touch"; var body = new Body(documentProperties); var rev1 = new RevisionInternal(body); database.PutRevision(rev1, null, false); Sleep(500); Assert.IsTrue(countDown.Wait(TimeSpan.FromSeconds(1))); // Analysis disable once DelegateSubtraction database.Changed -= handler; }
internal ValidationContextImpl(Database database, RevisionInternal currentRevision , RevisionInternal newRev) { this.database = database; this.currentRevision = currentRevision; this.newRev = newRev; }
public bool Run() { string[] bigObj = new string[this._enclosing.GetSizeOfDocument()]; for (int i = 0; i < this._enclosing.GetSizeOfDocument(); i++) { bigObj[i] = Test10_DeleteDB._propertyValue; } for (int i_1 = 0; i_1 < this._enclosing.GetNumberOfDocuments(); i_1++) { //create a document IDictionary<string, object> props = new Dictionary<string, object>(); props.Put("bigArray", bigObj); Body body = new Body(props); RevisionInternal rev1 = new RevisionInternal(body, this._enclosing.database); Status status = new Status(); try { rev1 = this._enclosing.database.PutRevision(rev1, null, false, status); } catch (Exception t) { Log.E(Test10_DeleteDB.Tag, "Document create failed", t); return false; } } return true; }
// Reproduces issue #167 // https://github.com/couchbase/couchbase-lite-android/issues/167 /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestLoadRevisionBody() { Document document = database.CreateDocument(); IDictionary<string, object> properties = new Dictionary<string, object>(); properties.Put("foo", "foo"); properties.Put("bar", false); document.PutProperties(properties); NUnit.Framework.Assert.IsNotNull(document.GetCurrentRevision()); bool deleted = false; RevisionInternal revisionInternal = new RevisionInternal(document.GetId(), document .GetCurrentRevisionId(), deleted, database); EnumSet<Database.TDContentOptions> contentOptions = EnumSet.Of(Database.TDContentOptions .TDIncludeAttachments, Database.TDContentOptions.TDBigAttachmentsFollow); database.LoadRevisionBody(revisionInternal, contentOptions); // now lets purge the document, and then try to load the revision body again NUnit.Framework.Assert.IsTrue(document.Purge()); bool gotExpectedException = false; try { database.LoadRevisionBody(revisionInternal, contentOptions); } catch (CouchbaseLiteException e) { if (e.GetCBLStatus().GetCode() == Status.NotFound) { gotExpectedException = true; } } NUnit.Framework.Assert.IsTrue(gotExpectedException); }
internal DocumentChange(RevisionInternal addedRevision, RevisionInternal winningRevision, bool isConflict, Uri sourceUrl) { AddedRevision = addedRevision; WinningRevision = winningRevision; IsConflict = isConflict; SourceUrl = sourceUrl; }
internal DocumentChange(RevisionInternal addedRevision, RevisionInternal winningRevision , bool isConflict, Uri sourceUrl) { this.addedRevision = addedRevision; this.winningRevision = winningRevision; this.isConflict = isConflict; this.sourceUrl = sourceUrl; }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> private RevisionInternal PutDoc(Database db, IDictionary<string, object> props) { RevisionInternal rev = new RevisionInternal(props, db); Status status = new Status(); rev = db.PutRevision(rev, null, false, status); NUnit.Framework.Assert.IsTrue(status.IsSuccessful()); return rev; }
public void TestForceInsertEmptyHistory() { var rev = new RevisionInternal("FakeDocId", "1-abcd".AsRevID(), false); var revProperties = new Dictionary<string, object>(); revProperties.SetDocRevID(rev.DocID, rev.RevID); revProperties["message"] = "hi"; rev.SetProperties(revProperties); IList<RevisionID> revHistory = null; database.ForceInsert(rev, revHistory, null); }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestForceInsertEmptyHistory() { IList<string> revHistory = null; RevisionInternal rev = new RevisionInternal("FakeDocId", "1-tango", false, database ); IDictionary<string, object> revProperties = new Dictionary<string, object>(); revProperties.Put("_id", rev.GetDocId()); revProperties.Put("_rev", rev.GetRevId()); revProperties.Put("message", "hi"); rev.SetProperties(revProperties); database.ForceInsert(rev, revHistory, null); }
public void TestForceInsertEmptyHistory() { var rev = new RevisionInternal("FakeDocId", "1-abcd", false); var revProperties = new Dictionary<string, object>(); revProperties.Put("_id", rev.GetDocId()); revProperties.Put("_rev", rev.GetRevId()); revProperties["message"] = "hi"; rev.SetProperties(revProperties); IList<string> revHistory = null; database.ForceInsert(rev, revHistory, null); }
internal QueryRow(string documentId, long sequence, object key, object value, RevisionInternal revision, IQueryRowStore storage) { // Don't initialize _database yet. I might be instantiated on a background thread (if the // query is async) which has a different CBLDatabase instance than the original caller. // Instead, the database property will be filled in when I'm added to a CBLQueryEnumerator. SourceDocumentId = documentId; SequenceNumber = sequence; _key = key; _value = value; _documentRevision = revision; _storage = storage; }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestChangeNotification() { // add listener to database database.Changed += (sender, e) => changeNotifications++; // create a document IDictionary<string, object> documentProperties = new Dictionary<string, object>(); documentProperties["foo"] = 1; documentProperties["bar"] = false; documentProperties["baz"] = "touch"; Body body = new Body(documentProperties); RevisionInternal rev1 = new RevisionInternal(body, database); Status status = new Status(); rev1 = database.PutRevision(rev1, null, false, status); NUnit.Framework.Assert.AreEqual(1, changeNotifications); }
public override bool Equals(object o) { bool result = false; if (o is Couchbase.Lite.Internal.RevisionInternal) { Couchbase.Lite.Internal.RevisionInternal other = (Couchbase.Lite.Internal.RevisionInternal )o; if (docId.Equals(other.docId) && revId.Equals(other.revId)) { result = true; } } return(result); }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestChangeNotification() { Database.ChangeListener changeListener = new _ChangeListener_16(this); // add listener to database database.AddChangeListener(changeListener); // 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); NUnit.Framework.Assert.AreEqual(1, changeNotifications); }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestLoadDBPerformance() { long startMillis = Runtime.CurrentTimeMillis(); string[] bigObj = new string[GetSizeOfDocument()]; for (int i = 0; i < GetSizeOfDocument(); i++) { bigObj[i] = _propertyValue; } for (int j = 0; j < GetNumberOfShutAndReloadCycles(); j++) { //Force close and reopen of manager and database to ensure cold //start before doc creation try { TearDown(); manager = new Manager(new LiteTestContext(), Manager.DefaultOptions); database = manager.GetExistingDatabase(DefaultTestDb); } catch (Exception ex) { Log.E(Tag, "DB teardown", ex); Fail(); } for (int k = 0; k < GetNumberOfDocuments(); k++) { //create a document IDictionary<string, object> props = new Dictionary<string, object>(); props.Put("bigArray", bigObj); Body body = new Body(props); RevisionInternal rev1 = new RevisionInternal(body, database); Status status = new Status(); try { rev1 = database.PutRevision(rev1, null, false, status); } catch (Exception t) { Log.E(Tag, "Document creation failed", t); Fail(); } } } Log.V("PerformanceStats", Tag + "," + Sharpen.Extensions.ValueOf(Runtime.CurrentTimeMillis () - startMillis).ToString() + "," + GetNumberOfDocuments() + "," + GetSizeOfDocument () + ",," + GetNumberOfShutAndReloadCycles()); }
public virtual Couchbase.Lite.Internal.RevisionInternal CopyWithDocID(string docId , string revId) { System.Diagnostics.Debug.Assert(((docId != null) && (revId != null))); System.Diagnostics.Debug.Assert(((this.docId == null) || (this.docId.Equals(docId )))); Couchbase.Lite.Internal.RevisionInternal result = new Couchbase.Lite.Internal.RevisionInternal (docId, revId, deleted, database); IDictionary <string, object> unmodifiableProperties = GetProperties(); IDictionary <string, object> properties = new Dictionary <string, object>(); if (unmodifiableProperties != null) { properties.PutAll(unmodifiableProperties); } properties.Put("_id", docId); properties.Put("_rev", revId); result.SetProperties(properties); return(result); }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestCreateDocsUnoptimizedWayPerformance() { long startMillis = Runtime.CurrentTimeMillis(); string[] bigObj = new string[GetSizeOfDocument()]; for (int i = 0; i < GetSizeOfDocument(); i++) { bigObj[i] = _propertyValue; } for (int i_1 = 0; i_1 < GetNumberOfDocuments(); i_1++) { //create a document IDictionary<string, object> props = new Dictionary<string, object>(); props.Put("bigArray", bigObj); Body body = new Body(props); RevisionInternal rev1 = new RevisionInternal(body, database); Status status = new Status(); rev1 = database.PutRevision(rev1, null, false, status); } Log.V("PerformanceStats", Tag + "," + Sharpen.Extensions.ValueOf(Runtime.CurrentTimeMillis () - startMillis).ToString() + "," + GetNumberOfDocuments() + "," + GetSizeOfDocument ()); }
public static ICouchbaseResponseState RevsDiff(ICouchbaseListenerContext context) { // Collect all of the input doc/revision IDs as CBL_Revisions: var revs = new RevisionList(); var body = context.BodyAs<Dictionary<string, object>>(); if (body == null) { return context.CreateResponse(StatusCode.BadJson).AsDefaultState(); } foreach (var docPair in body) { var revIDs = docPair.Value.AsList<string>(); if (revIDs == null) { return context.CreateResponse(StatusCode.BadParam).AsDefaultState(); } foreach (var revID in revIDs) { var rev = new RevisionInternal(docPair.Key, revID.AsRevID(), false); revs.Add(rev); } } return PerformLogicWithDatabase(context, true, db => { var response = context.CreateResponse(); // Look them up, removing the existing ones from revs: db.Storage.FindMissingRevisions(revs); // Return the missing revs in a somewhat different format: IDictionary<string, object> diffs = new Dictionary<string, object>(); foreach(var rev in revs) { var docId = rev.DocID; IList<RevisionID> missingRevs = null; if(!diffs.ContainsKey(docId)) { missingRevs = new List<RevisionID>(); diffs[docId] = new Dictionary<string, IList<RevisionID>> { { "missing", missingRevs } }; } else { missingRevs = ((Dictionary<string, IList<RevisionID>>)diffs[docId])["missing"]; } missingRevs.Add(rev.RevID); } // Add the possible ancestors for each missing revision: foreach(var docPair in diffs) { IDictionary<string, IList<RevisionID>> docInfo = (IDictionary<string, IList<RevisionID>>)docPair.Value; int maxGen = 0; RevisionID maxRevID = null; foreach(var revId in docInfo["missing"]) { if(revId.Generation > maxGen) { maxGen = revId.Generation; maxRevID = revId; } } var rev = new RevisionInternal(docPair.Key, maxRevID, false); var ancestors = db.Storage.GetPossibleAncestors(rev, 0, ValueTypePtr<bool>.NULL)?.ToList(); if(ancestors != null && ancestors.Count > 0) { docInfo["possible_ancestors"] = ancestors; } } response.JsonBody = new Body(diffs); return response; }).AsDefaultState(); }
/// <summary> /// Queries the specified view using the specified options /// </summary> /// <returns>The HTTP response containing the results of the query</returns> /// <param name="context">The request context</param> /// <param name="db">The database to run the query in</param> /// <param name="view">The view to query</param> /// <param name="options">The options to apply to the query</param> public static CouchbaseLiteResponse QueryView(ICouchbaseListenerContext context, Database db, View view, QueryOptions options) { var result = view.QueryWithOptions(options); object updateSeq = options.UpdateSeq ? (object)view.LastSequenceIndexed : null; var mappedResult = new List<object>(); foreach (var row in result) { row.Database = db; var dict = row.AsJSONDictionary(); if (context.ContentOptions != DocumentContentOptions.None) { var doc = dict.Get("doc").AsDictionary<string, object>(); if (doc != null) { // Add content options: RevisionInternal rev = new RevisionInternal(doc); var status = new Status(); rev = DocumentMethods.ApplyOptions(context.ContentOptions, rev, context, db, status); if (rev != null) { dict["doc"] = rev.GetProperties(); } } } mappedResult.Add(dict); } var body = new Body(new NonNullDictionary<string, object> { { "rows", mappedResult }, { "total_rows", view.TotalRows }, { "offset", options.Skip }, { "update_seq", updateSeq } }); var retVal = context.CreateResponse(); retVal.JsonBody = body; return retVal; }
/// <summary> /// Creates a dictionary of metadata for one specific revision /// </summary> /// <returns>The metadata dictionary</returns> /// <param name="rev">The revision to examine</param> /// <param name="responseState">The current response state</param> public static IDictionary<string, object> ChangesDictForRev(RevisionInternal rev, DBMonitorCouchbaseResponseState responseState) { if (responseState.ChangesIncludeDocs) { var status = new Status(); var rev2 = DocumentMethods.ApplyOptions(responseState.ContentOptions, rev, responseState.Context, responseState.Db, status); if (rev2 != null) { rev2.Sequence = rev.Sequence; rev = rev2; } } return new NonNullDictionary<string, object> { { "seq", rev.Sequence }, { "id", rev.DocID }, { "changes", new List<object> { new Dictionary<string, object> { { "rev", rev.RevID } } } }, { "deleted", rev.Deleted ? (object)true : null }, { "doc", responseState.ChangesIncludeDocs ? rev.GetProperties() : null } }; }
/// <summary> /// Create and update multiple documents at the same time within a single request. /// </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/database/bulk-api.html#post--db-_bulk_docs /// </remarks> public static ICouchbaseResponseState ProcessDocumentChangeOperations(ICouchbaseListenerContext context) { return PerformLogicWithDatabase(context, true, db => { var postBody = context.BodyAs<Dictionary<string, object>>(); if(postBody == null) { return context.CreateResponse(StatusCode.BadJson); } if(!postBody.ContainsKey("docs")) { return context.CreateResponse(StatusCode.BadParam); } var docs = postBody["docs"].AsList<IDictionary<string, object>>(); bool allOrNothing; postBody.TryGetValue<bool>("all_or_nothing", out allOrNothing); bool newEdits; postBody.TryGetValue<bool>("new_edits", out newEdits); var response = context.CreateResponse(); StatusCode status = StatusCode.Ok; bool success = db.RunInTransaction(() => { List<IDictionary<string, object>> results = new List<IDictionary<string, object>>(docs.Count); var castContext = context as ICouchbaseListenerContext2; var source = castContext != null && !castContext.IsLoopbackRequest ? castContext.Sender : null; foreach(var doc in docs) { string docId = doc.CblID(); RevisionInternal rev = null; Body body = new Body(doc); if(!newEdits) { if(!RevisionInternal.IsValid(body)) { status = StatusCode.BadParam; } else { rev = new RevisionInternal(body); var history = Database.ParseCouchDBRevisionHistory(doc); try { db.ForceInsert(rev, history, source); } catch(CouchbaseLiteException e) { status = e.Code; } } } else { status = DocumentMethods.UpdateDocument(context, db, docId, body, false, allOrNothing, out rev); } IDictionary<string, object> result = null; if((int)status < 300) { Debug.Assert(rev != null && rev.RevID != null); if(newEdits) { result = new Dictionary<string, object> { { "id", rev.DocID }, { "rev", rev.RevID }, { "status", (int)status } }; } } else if((int)status >= 500) { return false; // abort the whole thing if something goes badly wrong } else if(allOrNothing) { return false; // all_or_nothing backs out if there's any error } else { var info = Status.ToHttpStatus(status); result = new Dictionary<string, object> { { "id", docId }, { "error", info.Item2 }, { "status", info.Item1 } }; } if(result != null) { results.Add(result); } } response.JsonBody = new Body(results.Cast<object>().ToList()); return true; }); if(!success) { response.InternalStatus = status; } return response; }).AsDefaultState(); }
internal bool RunFilter(FilterDelegate filter, IDictionary<string, object> filterParams, RevisionInternal rev) { if (filter == null) { return true; } var publicRev = new SavedRevision(this, rev); return filter(publicRev, filterParams); }
private void SetupRevisionBodyTransformationFunction() { var xformer = TransformationFunction; if (xformer != null) { RevisionBodyTransformationFunction = (rev) => { var properties = rev.GetProperties(); var xformedProperties = xformer(properties); if (xformedProperties == null) { return null; } if (xformedProperties != properties) { Debug.Assert (xformedProperties != null); Debug.Assert (xformedProperties ["_id"].Equals (properties ["_id"])); Debug.Assert (xformedProperties ["_rev"].Equals (properties ["_rev"])); var nuRev = new RevisionInternal (rev.GetProperties ()); nuRev.SetProperties (xformedProperties); return nuRev; } return rev; }; } }
/// <summary>VALIDATION</summary> /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> internal Status ValidateRevision(RevisionInternal newRev, RevisionInternal oldRev, String parentRevId) { var validations = Shared.GetValues("validation", Name); if (validations == null || validations.Count == 0) { return new Status(StatusCode.Ok); } var publicRev = new SavedRevision(this, newRev, parentRevId); var context = new ValidationContext(this, oldRev, newRev); Status status = new Status(StatusCode.Ok); foreach (var validationName in validations.Keys) { var validation = GetValidation(validationName); try { validation(publicRev, context); } catch(Exception e) { Log.E(TAG, String.Format("Validation block '{0}'", validationName), e); status.Code = StatusCode.Exception; break; } if (context.RejectMessage != null) { Log.D(TAG, "Failed update of {0}: {1}:{2} Old doc = {3}{2} New doc = {4}", oldRev, context.RejectMessage, Environment.NewLine, oldRev == null ? null : oldRev.GetProperties(), newRev.GetProperties()); status.Code = StatusCode.Forbidden; break; } } return status; }
/// <summary>Updates or deletes an attachment, creating a new document revision in the process. /// </summary> /// <remarks> /// Updates or deletes an attachment, creating a new document revision in the process. /// Used by the PUT / DELETE methods called on attachment URLs. /// </remarks> /// <exclude></exclude> /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> internal RevisionInternal UpdateAttachment(string filename, BlobStoreWriter body, string contentType, AttachmentEncoding encoding, string docID, string oldRevID) { if(StringEx.IsNullOrWhiteSpace(filename) || (body != null && contentType == null) || (oldRevID != null && docID == null) || (body != null && docID == null)) { throw new CouchbaseLiteException(StatusCode.BadAttachment); } var oldRev = new RevisionInternal(docID, oldRevID, false); if (oldRevID != null) { // Load existing revision if this is a replacement: try { oldRev = LoadRevisionBody(oldRev); } catch (CouchbaseLiteException e) { if (e.Code == StatusCode.NotFound && GetDocument(docID, null, false) != null) { throw new CouchbaseLiteException(StatusCode.Conflict); } throw; } } else { // If this creates a new doc, it needs a body: oldRev.SetBody(new Body(new Dictionary<string, object>())); } // Update the _attachments dictionary: var attachments = oldRev.GetProperties().Get("_attachments").AsDictionary<string, object>(); if (attachments == null) { attachments = new Dictionary<string, object>(); } if (body != null) { var key = body.GetBlobKey(); string digest = key.Base64Digest(); RememberAttachmentWriter(body); string encodingName = (encoding == AttachmentEncoding.GZIP) ? "gzip" : null; attachments[filename] = new NonNullDictionary<string, object> { { "digest", digest }, { "length", body.GetLength() }, { "follows", true }, { "content_type", contentType }, { "encoding", encodingName } }; } else { if (oldRevID != null && attachments.Get(filename) == null) { throw new CouchbaseLiteException(StatusCode.AttachmentNotFound); } attachments.Remove(filename); } var properties = oldRev.GetProperties(); properties["_attachments"] = attachments; oldRev.SetProperties(properties); Status status = new Status(); var newRev = PutRevision(oldRev, oldRevID, false, status); if (status.IsError) { throw new CouchbaseLiteException(status.Code); } return newRev; }
//Doesn't handle CouchbaseLiteException internal RevisionInternal LoadRevisionBody(RevisionInternal rev) { if (rev.GetSequence() > 0) { var props = rev.GetProperties(); if (props != null && props.GetCast<string>("_rev") != null && props.GetCast<string>("_id") != null) { return rev; } } Debug.Assert(rev.GetDocId() != null && rev.GetRevId() != null); Storage.LoadRevisionBody(rev); return rev; }
internal RevisionInternal TransformRevision(RevisionInternal rev) { if (RevisionBodyTransformationFunction != null) { try { var generation = rev.GetGeneration(); var xformed = RevisionBodyTransformationFunction(rev); if (xformed == null) { return null; } if (xformed != rev) { Debug.Assert((xformed.GetDocId().Equals(rev.GetDocId()))); Debug.Assert((xformed.GetRevId().Equals(rev.GetRevId()))); Debug.Assert((xformed.GetProperties().Get("_revisions").Equals(rev.GetProperties().Get("_revisions")))); if (xformed.GetProperties().ContainsKey("_attachments")) { // Insert 'revpos' properties into any attachments added by the callback: var mx = new RevisionInternal(xformed.GetProperties()); xformed = mx; mx.MutateAttachments((name, info) => { if (info.Get("revpos") != null) { return info; } if (info.Get("data") == null) { throw new InvalidOperationException("Transformer added attachment without adding data"); } var newInfo = new Dictionary<string, object>(info); newInfo["revpos"] = generation; return newInfo; }); } } } catch (Exception e) { Log.W(TAG, String.Format("Exception transforming a revision of doc '{0}'", rev.GetDocId()), e); } } return rev; }
internal void AddToInbox(RevisionInternal rev) { Debug.Assert(IsRunning); Batcher.QueueObject(rev); }
/// <summary> /// Sets the contents of the local <see cref="Couchbase.Lite.Document" /> with the given id. If <param name="properties"/> is null, the /// <see cref="Couchbase.Lite.Document" /> is deleted. /// </summary> /// <param name="id">The id of the local document whos contents to set.</param> /// <param name="properties">The contents to set for the local document.</param> /// <exception cref="Couchbase.Lite.CouchbaseLiteException">Thrown if an issue occurs /// while setting the contents of the local document.</exception> public bool PutLocalDocument(string id, IDictionary<string, object> properties) { id = MakeLocalDocumentId(id); var rev = new RevisionInternal(id, null, properties == null); if (properties != null) { rev.SetProperties(properties); } bool ok = Storage.PutLocalRevision(rev, null, false) != null; return ok; }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestValidations() { Database.ValidateDelegate validator = (Revision newRevision, ValidationContext context)=> { NUnit.Framework.Assert.IsNotNull(newRevision); NUnit.Framework.Assert.IsNotNull(context); NUnit.Framework.Assert.IsTrue(newRevision.Properties != null || newRevision. IsDeletion); this._enclosing.validationCalled = true; bool hoopy = newRevision.IsDeletion || (newRevision.Properties.Get("towel" ) != null); Log.V(ValidationsTest.Tag, string.Format("--- Validating %s --> %b", newRevision. Properties, hoopy)); if (!hoopy) { context.Reject("Where's your towel?"); } return hoopy; }; database.SetValidation("hoopy", validator); // POST a valid new document: IDictionary<string, object> props = new Dictionary<string, object>(); props["name"] = "Zaphod Beeblebrox"; props["towel"] = "velvet"; RevisionInternal rev = new RevisionInternal(props, database); Status status = new Status(); validationCalled = false; rev = database.PutRevision(rev, null, false, status); NUnit.Framework.Assert.IsTrue(validationCalled); NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode()); // PUT a valid update: props["head_count"] = 3; rev.SetProperties(props); validationCalled = false; rev = database.PutRevision(rev, rev.GetRevId(), false, status); NUnit.Framework.Assert.IsTrue(validationCalled); NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode()); // PUT an invalid update: Sharpen.Collections.Remove(props, "towel"); rev.SetProperties(props); validationCalled = false; bool gotExpectedError = false; try { rev = database.PutRevision(rev, rev.GetRevId(), false, status); } catch (CouchbaseLiteException e) { gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden); } NUnit.Framework.Assert.IsTrue(validationCalled); NUnit.Framework.Assert.IsTrue(gotExpectedError); // POST an invalid new document: props = new Dictionary<string, object>(); props["name"] = "Vogon"; props["poetry"] = true; rev = new RevisionInternal(props, database); validationCalled = false; gotExpectedError = false; try { rev = database.PutRevision(rev, null, false, status); } catch (CouchbaseLiteException e) { gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden); } NUnit.Framework.Assert.IsTrue(validationCalled); NUnit.Framework.Assert.IsTrue(gotExpectedError); // PUT a valid new document with an ID: props = new Dictionary<string, object>(); props["_id"] = "ford"; props["name"] = "Ford Prefect"; props["towel"] = "terrycloth"; rev = new RevisionInternal(props, database); validationCalled = false; rev = database.PutRevision(rev, null, false, status); NUnit.Framework.Assert.IsTrue(validationCalled); NUnit.Framework.Assert.AreEqual("ford", rev.GetDocId()); // DELETE a document: rev = new RevisionInternal(rev.GetDocId(), rev.GetRevId(), true, database); NUnit.Framework.Assert.IsTrue(rev.IsDeleted()); validationCalled = false; rev = database.PutRevision(rev, rev.GetRevId(), false, status); NUnit.Framework.Assert.IsTrue(validationCalled); // PUT an invalid new document: props = new Dictionary<string, object>(); props["_id"] = "petunias"; props["name"] = "Pot of Petunias"; rev = new RevisionInternal(props, database); validationCalled = false; gotExpectedError = false; try { rev = database.PutRevision(rev, null, false, status); } catch (CouchbaseLiteException e) { gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden); } NUnit.Framework.Assert.IsTrue(validationCalled); NUnit.Framework.Assert.IsTrue(gotExpectedError); }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException">When attempting to add an invalid revision</exception> internal void ForceInsert(RevisionInternal inRev, IList<string> revHistory, Uri source) { if (revHistory == null) { revHistory = new List<string>(0); } var rev = inRev.CopyWithDocID(inRev.GetDocId(), inRev.GetRevId()); rev.SetSequence(0); string revID = rev.GetRevId(); if (!IsValidDocumentId(rev.GetDocId()) || revID == null) { throw new CouchbaseLiteException(StatusCode.BadId); } if (revHistory.Count == 0) { revHistory.Add(revID); } else if (revID != revHistory[0]) { throw new CouchbaseLiteException(StatusCode.BadId); } if (inRev.GetAttachments() != null) { var updatedRev = inRev.CopyWithDocID(inRev.GetDocId(), inRev.GetRevId()); string prevRevID = revHistory.Count >= 2 ? revHistory[1] : null; Status status = new Status(); if (!ProcessAttachmentsForRevision(updatedRev, prevRevID, status)) { throw new CouchbaseLiteException(status.Code); } inRev = updatedRev; } StoreValidation validationBlock = null; if (Shared != null && Shared.HasValues("validation", Name)) { validationBlock = ValidateRevision; } var insertStatus = Storage.ForceInsert(inRev, revHistory, validationBlock, source); if(insertStatus.IsError) { throw new CouchbaseLiteException(insertStatus.Code); } }