/// <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()); }