public IDictionary <string, object> GetProperties() { IDictionary <string, object> result = null; if (_body != null) { try { result = _body.GetProperties(); } catch (InvalidOperationException) { // handle when both object and json are null for this body return(null); } if (_docId != null) { result["_id"] = _docId; } if (_revId != null) { result.SetRevID(_revId); } if (Deleted) { result["_deleted"] = true; } } return(result); }
internal IDictionary <String, Object> GetProperties() { IDictionary <string, object> result = null; if (_body != null) { IDictionary <string, object> prop; try { prop = _body.GetProperties(); } catch (InvalidOperationException) { // handle when both object and json are null for this body return(null); } if (result == null) { result = new Dictionary <string, object>(); } result.PutAll(prop); if (_docId != null) { result["_id"] = _docId; } if (_revId != null) { result["_rev"] = _revId; } if (_deleted) { result["_deleted"] = true; } } return(result); }
public virtual IDictionary <string, object> GetProperties() { IDictionary <string, object> result = null; if (body != null) { IDictionary <string, object> prop; try { prop = body.GetProperties(); } catch (InvalidOperationException) { // handle when both object and json are null for this body return(null); } if (result == null) { result = new Dictionary <string, object>(); } result.PutAll(prop); } return(result); }
internal RevisionInternal(Body body) : this(body.GetProperties().CblID(), body.GetProperties().CblRev(), body.GetProperties().CblDeleted()) { _body = body; }
/// <summary> /// Creates (and executes) a temporary view based on the view function supplied in the JSON 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/temp-views.html#post--db-_temp_view /// </remarks> public static ICouchbaseResponseState ExecuteTemporaryViewFunction(ICouchbaseListenerContext context) { var response = context.CreateResponse(); if (context.RequestHeaders["Content-Type"] == null || !context.RequestHeaders["Content-Type"].StartsWith("application/json")) { response.InternalStatus = StatusCode.UnsupportedType; return response.AsDefaultState(); } IEnumerable<byte> json = context.BodyStream.ReadAllBytes(); var requestBody = new Body(json); if (!requestBody.IsValidJSON()) { response.InternalStatus = StatusCode.BadJson; return response.AsDefaultState(); } var props = requestBody.GetProperties(); if (props == null) { response.InternalStatus = StatusCode.BadJson; return response.AsDefaultState(); } var options = context.QueryOptions; if (options == null) { response.InternalStatus = StatusCode.BadRequest; return response.AsDefaultState(); } return PerformLogicWithDatabase(context, true, db => { if (context.CacheWithEtag(db.GetLastSequenceNumber().ToString())) { response.InternalStatus = StatusCode.NotModified; return response; } var view = db.GetView("@@TEMPVIEW@@"); var status = view.Compile(props, "javascript"); if(status.IsError) { response.InternalStatus = status.Code; return response; } try { view.UpdateIndex_Internal(); return QueryView(context, null, view, options); } catch(CouchbaseLiteException e) { response.InternalStatus = e.CBLStatus.Code; } return response; }).AsDefaultState(); }
public void TestLocalDocs() { //create a document var documentProperties = new Dictionary<string, object>(); documentProperties["_id"] = "_local/doc1"; documentProperties["foo"] = 1; documentProperties["bar"] = false; var body = new Body(documentProperties); var rev1 = new RevisionInternal(body); rev1 = database.Storage.PutLocalRevision(rev1, null, true); Log.V(Tag, "Created " + rev1); Assert.AreEqual("_local/doc1", rev1.GetDocId()); Assert.IsTrue(rev1.GetRevId().StartsWith("1-")); //read it back var readRev = database.Storage.GetLocalDocument(rev1.GetDocId(), null); Assert.IsNotNull(readRev); var readRevProps = readRev.GetProperties(); Assert.AreEqual(rev1.GetDocId(), readRevProps.Get("_id")); Assert.AreEqual(rev1.GetRevId(), readRevProps.Get("_rev")); AssertPropertiesAreEqual(UserProperties(readRevProps), UserProperties(body.GetProperties())); //now update it documentProperties = (Dictionary<string, object>)readRev.GetProperties(); documentProperties["status"] = "updated!"; body = new Body(documentProperties); var rev2 = new RevisionInternal(body); var rev2input = rev2; rev2 = database.Storage.PutLocalRevision(rev2, rev1.GetRevId(), true); Log.V(Tag, "Updated " + rev1); Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId()); Assert.IsTrue(rev2.GetRevId().StartsWith("2-")); //read it back readRev = database.Storage.GetLocalDocument(rev2.GetDocId(), null); Assert.IsNotNull(readRev); AssertPropertiesAreEqual(UserProperties(readRev.GetProperties()), UserProperties(body.GetProperties())); // Try to update the first rev, which should fail: var gotException = false; try { database.Storage.PutLocalRevision(rev2input, rev1.GetRevId(), true); } catch (CouchbaseLiteException e) { Assert.AreEqual(StatusCode.Conflict, e.CBLStatus.Code); gotException = true; } Assert.IsTrue(gotException); // Delete it: var revD = new RevisionInternal(rev2.GetDocId(), null, true); gotException = false; try { var revResult = database.Storage.PutLocalRevision(revD, null, true); Assert.IsNull(revResult); } catch (CouchbaseLiteException e) { Assert.AreEqual(StatusCode.Conflict, e.CBLStatus.Code); gotException = true; } Assert.IsTrue(gotException); revD = database.Storage.PutLocalRevision(revD, rev2.GetRevId(), true); // Delete nonexistent doc: gotException = false; var revFake = new RevisionInternal("_local/fake", null, true); try { database.Storage.PutLocalRevision(revFake, null, true); } catch (CouchbaseLiteException e) { Assert.AreEqual(StatusCode.NotFound, e.CBLStatus.Code); gotException = true; } Assert.IsTrue(gotException); // Read it back (should fail): readRev = database.Storage.GetLocalDocument(revD.GetDocId(), null); Assert.IsNull(readRev); }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestLocalDocs() { //create a document IDictionary<string, object> documentProperties = new Dictionary<string, object>(); documentProperties.Put("_id", "_local/doc1"); documentProperties.Put("foo", 1); documentProperties.Put("bar", false); Body body = new Body(documentProperties); RevisionInternal rev1 = new RevisionInternal(body, database); Status status = new Status(); rev1 = database.PutLocalRevision(rev1, null); Log.V(Tag, "Created " + rev1); NUnit.Framework.Assert.AreEqual("_local/doc1", rev1.GetDocId()); NUnit.Framework.Assert.IsTrue(rev1.GetRevId().StartsWith("1-")); //read it back RevisionInternal readRev = database.GetLocalDocument(rev1.GetDocId(), null); NUnit.Framework.Assert.IsNotNull(readRev); IDictionary<string, object> readRevProps = readRev.GetProperties(); NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), readRev.GetProperties().Get("_id" )); NUnit.Framework.Assert.AreEqual(rev1.GetRevId(), readRev.GetProperties().Get("_rev" )); NUnit.Framework.Assert.AreEqual(UserProperties(readRevProps), UserProperties(body .GetProperties())); //now update it documentProperties = readRev.GetProperties(); documentProperties.Put("status", "updated!"); body = new Body(documentProperties); RevisionInternal rev2 = new RevisionInternal(body, database); RevisionInternal rev2input = rev2; rev2 = database.PutLocalRevision(rev2, rev1.GetRevId()); Log.V(Tag, "Updated " + rev1); NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId()); NUnit.Framework.Assert.IsTrue(rev2.GetRevId().StartsWith("2-")); //read it back readRev = database.GetLocalDocument(rev2.GetDocId(), null); NUnit.Framework.Assert.IsNotNull(readRev); NUnit.Framework.Assert.AreEqual(UserProperties(readRev.GetProperties()), UserProperties (body.GetProperties())); // Try to update the first rev, which should fail: bool gotException = false; try { database.PutLocalRevision(rev2input, rev1.GetRevId()); } catch (CouchbaseLiteException e) { NUnit.Framework.Assert.AreEqual(Status.Conflict, e.GetCBLStatus().GetCode()); gotException = true; } NUnit.Framework.Assert.IsTrue(gotException); // Delete it: RevisionInternal revD = new RevisionInternal(rev2.GetDocId(), null, true, database ); gotException = false; try { RevisionInternal revResult = database.PutLocalRevision(revD, null); NUnit.Framework.Assert.IsNull(revResult); } catch (CouchbaseLiteException e) { NUnit.Framework.Assert.AreEqual(Status.Conflict, e.GetCBLStatus().GetCode()); gotException = true; } NUnit.Framework.Assert.IsTrue(gotException); revD = database.PutLocalRevision(revD, rev2.GetRevId()); // Delete nonexistent doc: gotException = false; RevisionInternal revFake = new RevisionInternal("_local/fake", null, true, database ); try { database.PutLocalRevision(revFake, null); } catch (CouchbaseLiteException e) { NUnit.Framework.Assert.AreEqual(Status.NotFound, e.GetCBLStatus().GetCode()); gotException = true; } NUnit.Framework.Assert.IsTrue(gotException); // Read it back (should fail): readRev = database.GetLocalDocument(revD.GetDocId(), null); NUnit.Framework.Assert.IsNull(readRev); }
// Perform a document operation on the specified database private static CouchbaseLiteResponse UpdateDb(ICouchbaseListenerContext context, Database db, string docId, Body body, bool deleting) { var response = context.CreateResponse(); if (docId != null) { // On PUT/DELETE, get revision ID from either ?rev= query, If-Match: header, or doc body: string revParam = context.GetQueryParam("rev"); string ifMatch = context.RequestHeaders["If-Match"]; if (ifMatch != null) { if (revParam == null) { revParam = ifMatch; } else if (!revParam.Equals(ifMatch)) { return context.CreateResponse(StatusCode.BadRequest); } } if (revParam != null && body != null) { var revProp = body.GetPropertyForKey("_rev"); if (revProp == null) { // No _rev property in body, so use ?rev= query param instead: var props = body.GetProperties(); props["_rev"] = revParam; body = new Body(props); } else if (!revProp.Equals(revParam)) { return context.CreateResponse(StatusCode.BadRequest); // mismatch between _rev and rev } } } RevisionInternal rev; StatusCode status = UpdateDocument(context, db, docId, body, deleting, false, out rev); if ((int)status < 300) { context.CacheWithEtag(rev.GetRevId()); // set ETag if (!deleting) { var url = context.RequestUrl; if (docId != null) { response["Location"] = url.AbsoluteUri; } } response.JsonBody = new Body(new Dictionary<string, object> { { "ok", true }, { "id", rev.GetDocId() }, { "rev", rev.GetRevId() } }); } response.InternalStatus = status; return response; }
/// <summary> /// Attempt to update a document based on the information in the HTTP request /// </summary> /// <returns>The resulting status of the operation</returns> /// <param name="context">The request context</param> /// <param name="db">The database in which the document exists</param> /// <param name="docId">The ID of the document being updated</param> /// <param name="body">The new document body</param> /// <param name="deleting">Whether or not the document is being deleted</param> /// <param name="allowConflict">Whether or not to allow a conflict to be inserted</param> /// <param name="outRev">The resulting revision of the document</param> public static StatusCode UpdateDocument(ICouchbaseListenerContext context, Database db, string docId, Body body, bool deleting, bool allowConflict, out RevisionInternal outRev) { outRev = null; if (body != null && !body.IsValidJSON()) { return StatusCode.BadJson; } string prevRevId; if (!deleting) { var properties = body.GetProperties(); deleting = properties.GetCast<bool>("_deleted"); if (docId == null) { // POST's doc ID may come from the _id field of the JSON body. docId = properties.GetCast<string>("_id"); if (docId == null && deleting) { return StatusCode.BadId; } } // PUT's revision ID comes from the JSON body. prevRevId = properties.GetCast<string>("_rev"); } else { // DELETE's revision ID comes from the ?rev= query param prevRevId = context.GetQueryParam("rev"); } // A backup source of revision ID is an If-Match header: if (prevRevId == null) { prevRevId = context.IfMatch(); } if (docId == null && deleting) { return StatusCode.BadId; } RevisionInternal rev = new RevisionInternal(docId, null, deleting); rev.SetBody(body); StatusCode status = StatusCode.Created; try { if (docId != null && docId.StartsWith("_local")) { outRev = db.Storage.PutLocalRevision(rev, prevRevId, true); //TODO: Doesn't match iOS } else { Status retStatus = new Status(); outRev = db.PutRevision(rev, prevRevId, allowConflict, retStatus); status = retStatus.Code; } } catch(CouchbaseLiteException e) { status = e.Code; } return status; }
public void TestCRUDOperations() { database.Changed += (sender, e) => { var changes = e.Changes.ToList(); foreach (DocumentChange change in changes) { var rev = change.AddedRevision; Assert.IsNotNull(rev); Assert.IsNotNull(rev.GetDocId()); Assert.IsNotNull(rev.GetRevId()); Assert.AreEqual(rev.GetDocId(), rev.GetProperties()["_id"]); Assert.AreEqual(rev.GetRevId(), rev.GetProperties()["_rev"]); } }; var privateUUID = database.PrivateUUID(); var publicUUID = database.PublicUUID(); Log.V(Tag, "DB private UUID = '" + privateUUID + "', public UUID = '" + publicUUID + "'"); Assert.IsTrue(privateUUID.Length >= 20); Assert.IsTrue(publicUUID.Length >= 20); //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); rev1 = database.PutRevision(rev1, null, false); Log.V(Tag, "Created " + rev1); Assert.IsTrue(rev1.GetDocId().Length >= 10); Assert.IsTrue(rev1.GetRevId().StartsWith("1-")); //read it back var readRev = database.GetDocument(rev1.GetDocId(), null, true); Assert.IsNotNull(readRev); var userReadRevProps = UserProperties(readRev.GetProperties()); var userBodyProps = UserProperties(body.GetProperties()); Assert.AreEqual(userReadRevProps.Count, userBodyProps.Count); foreach(var key in userReadRevProps.Keys) { Assert.AreEqual(userReadRevProps[key], userBodyProps[key]); } //now update it documentProperties = (Dictionary<string, object>)readRev.GetProperties(); documentProperties["status"] = "updated!"; body = new Body(documentProperties); var rev2 = new RevisionInternal(body); var rev2input = rev2; rev2 = database.PutRevision(rev2, rev1.GetRevId(), false); Log.V(Tag, "Updated " + rev1); Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId()); Assert.IsTrue(rev2.GetRevId().StartsWith("2-")); //read it back readRev = database.GetDocument(rev2.GetDocId(), null, true); Assert.IsNotNull(readRev); Assert.AreEqual(UserProperties(readRev.GetProperties()), UserProperties (body.GetProperties())); // Try to update the first rev, which should fail: var ex = Assert.Throws<CouchbaseLiteException>(() => database.PutRevision(rev2input, rev1.GetRevId(), false)); Assert.AreEqual(StatusCode.Conflict, ex.Code); // Check the changes feed, with and without filters: var changeRevisions = database.ChangesSince(0, ChangesOptions.Default, null, null); Log.V(Tag, "Changes = " + changeRevisions); Assert.AreEqual(1, changeRevisions.Count); changeRevisions = database.ChangesSince(0, ChangesOptions.Default, (revision, items) => "updated!".Equals (revision.Properties.Get("status")), null); Assert.AreEqual(1, changeRevisions.Count); changeRevisions = database.ChangesSince(0, ChangesOptions.Default, (revision, items) => "not updated!".Equals (revision.Properties.Get("status")), null); Assert.AreEqual(0, changeRevisions.Count); // Delete it: var revD = new RevisionInternal(rev2.GetDocId(), null, true); ex = Assert.Throws<CouchbaseLiteException>(() => database.PutRevision(revD, null, false)); Assert.AreEqual(StatusCode.Conflict, ex.Code); revD = database.PutRevision(revD, rev2.GetRevId(), false); Assert.AreEqual(revD.GetDocId(), rev2.GetDocId()); Assert.IsTrue(revD.GetRevId().StartsWith("3-")); // Delete nonexistent doc: var revFake = new RevisionInternal("fake", null, true); ex = Assert.Throws<CouchbaseLiteException>(() => database.PutRevision(revFake, null, false)); Assert.AreEqual(StatusCode.NotFound, ex.Code); // Read it back (should fail): readRev = database.GetDocument(revD.GetDocId(), null, true); Assert.IsNull(readRev); // Get Changes feed: changeRevisions = database.ChangesSince(0, ChangesOptions.Default, null, null); Assert.IsTrue(changeRevisions.Count == 1); // Get Revision History: IList<RevisionInternal> history = database.Storage.GetRevisionHistory(revD, null); Assert.AreEqual(revD, history[0]); Assert.AreEqual(rev2, history[1]); Assert.AreEqual(rev1, history[2]); }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestCRUDOperations() { database.AddChangeListener(this); string privateUUID = database.PrivateUUID(); string publicUUID = database.PublicUUID(); Log.V(Tag, "DB private UUID = '" + privateUUID + "', public UUID = '" + publicUUID + "'"); NUnit.Framework.Assert.IsTrue(privateUUID.Length >= 20); NUnit.Framework.Assert.IsTrue(publicUUID.Length >= 20); //create a document IDictionary<string, object> documentProperties = new Dictionary<string, object>(); documentProperties.Put("foo", 1); documentProperties.Put("bar", false); documentProperties.Put("baz", "touch"); Body body = new Body(documentProperties); RevisionInternal rev1 = new RevisionInternal(body, database); Status status = new Status(); rev1 = database.PutRevision(rev1, null, false, status); Log.V(Tag, "Created " + rev1); NUnit.Framework.Assert.IsTrue(rev1.GetDocId().Length >= 10); NUnit.Framework.Assert.IsTrue(rev1.GetRevId().StartsWith("1-")); //read it back RevisionInternal readRev = database.GetDocumentWithIDAndRev(rev1.GetDocId(), null , EnumSet.NoneOf<Database.TDContentOptions>()); NUnit.Framework.Assert.IsNotNull(readRev); IDictionary<string, object> readRevProps = readRev.GetProperties(); NUnit.Framework.Assert.AreEqual(UserProperties(readRevProps), UserProperties(body .GetProperties())); //now update it documentProperties = readRev.GetProperties(); documentProperties.Put("status", "updated!"); body = new Body(documentProperties); RevisionInternal rev2 = new RevisionInternal(body, database); RevisionInternal rev2input = rev2; rev2 = database.PutRevision(rev2, rev1.GetRevId(), false, status); Log.V(Tag, "Updated " + rev1); NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId()); NUnit.Framework.Assert.IsTrue(rev2.GetRevId().StartsWith("2-")); //read it back readRev = database.GetDocumentWithIDAndRev(rev2.GetDocId(), null, EnumSet.NoneOf< Database.TDContentOptions>()); NUnit.Framework.Assert.IsNotNull(readRev); NUnit.Framework.Assert.AreEqual(UserProperties(readRev.GetProperties()), UserProperties (body.GetProperties())); // Try to update the first rev, which should fail: bool gotExpectedError = false; try { database.PutRevision(rev2input, rev1.GetRevId(), false, status); } catch (CouchbaseLiteException e) { gotExpectedError = e.GetCBLStatus().GetCode() == Status.Conflict; } NUnit.Framework.Assert.IsTrue(gotExpectedError); // Check the changes feed, with and without filters: RevisionList changes = database.ChangesSince(0, null, null); Log.V(Tag, "Changes = " + changes); NUnit.Framework.Assert.AreEqual(1, changes.Count); changes = database.ChangesSince(0, null, new _ReplicationFilter_95()); NUnit.Framework.Assert.AreEqual(1, changes.Count); changes = database.ChangesSince(0, null, new _ReplicationFilter_105()); NUnit.Framework.Assert.AreEqual(0, changes.Count); // Delete it: RevisionInternal revD = new RevisionInternal(rev2.GetDocId(), null, true, database ); RevisionInternal revResult = null; gotExpectedError = false; try { revResult = database.PutRevision(revD, null, false, status); } catch (CouchbaseLiteException e) { gotExpectedError = e.GetCBLStatus().GetCode() == Status.Conflict; } NUnit.Framework.Assert.IsTrue(gotExpectedError); NUnit.Framework.Assert.IsNull(revResult); revD = database.PutRevision(revD, rev2.GetRevId(), false, status); NUnit.Framework.Assert.AreEqual(Status.Ok, status.GetCode()); NUnit.Framework.Assert.AreEqual(revD.GetDocId(), rev2.GetDocId()); NUnit.Framework.Assert.IsTrue(revD.GetRevId().StartsWith("3-")); // Delete nonexistent doc: RevisionInternal revFake = new RevisionInternal("fake", null, true, database); gotExpectedError = false; try { database.PutRevision(revFake, null, false, status); } catch (CouchbaseLiteException e) { gotExpectedError = e.GetCBLStatus().GetCode() == Status.NotFound; } NUnit.Framework.Assert.IsTrue(gotExpectedError); // Read it back (should fail): readRev = database.GetDocumentWithIDAndRev(revD.GetDocId(), null, EnumSet.NoneOf< Database.TDContentOptions>()); NUnit.Framework.Assert.IsNull(readRev); // Get Changes feed changes = database.ChangesSince(0, null, null); NUnit.Framework.Assert.IsTrue(changes.Count == 1); // Get Revision History IList<RevisionInternal> history = database.GetRevisionHistory(revD); NUnit.Framework.Assert.AreEqual(revD, history[0]); NUnit.Framework.Assert.AreEqual(rev2, history[1]); NUnit.Framework.Assert.AreEqual(rev1, history[2]); }
/// <summary> /// Attempt to update a document based on the information in the HTTP request /// </summary> /// <returns>The resulting status of the operation</returns> /// <param name="context">The request context</param> /// <param name="db">The database in which the document exists</param> /// <param name="docId">The ID of the document being updated</param> /// <param name="body">The new document body</param> /// <param name="deleting">Whether or not the document is being deleted</param> /// <param name="allowConflict">Whether or not to allow a conflict to be inserted</param> /// <param name="outRev">The resulting revision of the document</param> public static StatusCode UpdateDocument(ICouchbaseListenerContext context, Database db, string docId, Body body, bool deleting, bool allowConflict, out RevisionInternal outRev) { outRev = null; if (body != null && !body.IsValidJSON()) { return StatusCode.BadJson; } string prevRevId; if (!deleting) { var properties = body.GetProperties(); deleting = properties.GetCast<bool>("_deleted"); if (docId == null) { // POST's doc ID may come from the _id field of the JSON body. docId = properties.CblID(); if (docId == null && deleting) { return StatusCode.BadId; } } // PUT's revision ID comes from the JSON body. prevRevId = properties.GetCast<string>("_rev"); } else { // DELETE's revision ID comes from the ?rev= query param prevRevId = context.GetQueryParam("rev"); } // A backup source of revision ID is an If-Match header: if (prevRevId == null) { prevRevId = context.IfMatch(); } if (docId == null && deleting) { return StatusCode.BadId; } RevisionInternal rev = new RevisionInternal(docId, null, deleting); rev.SetBody(body); // Check for doc expiration var expirationTime = default(DateTime?); var tmp = default(object); var props = rev.GetProperties(); var hasValue = false; if(props != null && props.TryGetValue("_exp", out tmp)) { hasValue = true; if(tmp != null) { try { expirationTime = Convert.ToDateTime(tmp); } catch(Exception) { try { var num = Convert.ToInt64(tmp); expirationTime = Misc.OffsetFromEpoch(TimeSpan.FromSeconds(num)); } catch(Exception) { Log.To.Router.E(TAG, "Invalid value for _exp: {0}", tmp); return StatusCode.BadRequest; } } } props.Remove("_exp"); rev.SetProperties(props); } var castContext = context as ICouchbaseListenerContext2; var source = castContext != null && !castContext.IsLoopbackRequest ? castContext.Sender : null; StatusCode status = deleting ? StatusCode.Ok : StatusCode.Created; try { if(docId != null && docId.StartsWith("_local")) { if(expirationTime.HasValue) { return StatusCode.BadRequest; } outRev = db.Storage.PutLocalRevision(rev, prevRevId.AsRevID(), true); //TODO: Doesn't match iOS } else { outRev = db.PutRevision(rev, prevRevId.AsRevID(), allowConflict, source); if(hasValue) { db.Storage?.SetDocumentExpiration(rev.DocID, expirationTime); } } } catch(CouchbaseLiteException e) { status = e.Code; } return status; }