You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2009/11/15 05:11:12 UTC

svn commit: r836324 - in /couchdb/trunk: share/www/script/test/attachments_multipart.js src/couchdb/couch_db.erl src/couchdb/couch_doc.erl src/couchdb/couch_httpd.erl src/couchdb/couch_httpd_db.erl

Author: damien
Date: Sun Nov 15 04:11:11 2009
New Revision: 836324

URL: http://svn.apache.org/viewvc?rev=836324&view=rev
Log:
Testing of incremental attachments and more testing of multipart/handling.

Modified:
    couchdb/trunk/share/www/script/test/attachments_multipart.js
    couchdb/trunk/src/couchdb/couch_db.erl
    couchdb/trunk/src/couchdb/couch_doc.erl
    couchdb/trunk/src/couchdb/couch_httpd.erl
    couchdb/trunk/src/couchdb/couch_httpd_db.erl

Modified: couchdb/trunk/share/www/script/test/attachments_multipart.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/attachments_multipart.js?rev=836324&r1=836323&r2=836324&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/attachments_multipart.js (original)
+++ couchdb/trunk/share/www/script/test/attachments_multipart.js Sun Nov 15 04:11:11 2009
@@ -79,6 +79,7 @@
   // now edit an attachment
   
   var doc = db.open("multipart");
+  var firstrev = doc._rev;
   
   T(doc._attachments["foo.txt"].stub == true);
   T(doc._attachments["bar.txt"].stub == true);
@@ -111,29 +112,29 @@
   xhr = CouchDB.request("GET", "/test_suite_db/multipart/baz.txt");
   T(xhr.status == 404);
   
-  xhr = CouchDB.request("GET", "/test_suite_db/multipart?attachments=true",
-    {headers:{"accept": "multipart/related,*/*;"}});
-
-  var headers = xhr.getAllResponseHeaders();
+  // now test receiving multipart docs
   
-  var ctype = xhr.getResponseHeader("Content-Type");
+  function getBoundary(xhr) {
+    var ctype = xhr.getResponseHeader("Content-Type");
   
-  var ctypeArgs = ctype.split("; ").slice(1);
-  var boundary = null;
-  for(var i=0; i<ctypeArgs.length; i++) {
-    if (ctypeArgs[i].indexOf("boundary=") == 0) {
-      boundary = ctypeArgs[i].split("=")[1];
-      if (boundary.charAt(0) == '"') {
-        // stringified boundary, parse as json 
-        // (will maybe not if there are escape quotes)
-        boundary = JSON.parse(boundary);
+    var ctypeArgs = ctype.split("; ").slice(1);
+    var boundary = null;
+    for(var i=0; i<ctypeArgs.length; i++) {
+      if (ctypeArgs[i].indexOf("boundary=") == 0) {
+        boundary = ctypeArgs[i].split("=")[1];
+        if (boundary.charAt(0) == '"') {
+          // stringified boundary, parse as json 
+          // (will maybe not if there are escape quotes)
+          boundary = JSON.parse(boundary);
+        }
       }
     }
+    return boundary;
   }
   
-  T(boundary != null);
-  
-  function parseMime(boundary, mimetext) {
+  function parseMultipart(xhr) {
+    var boundary = getBoundary(xhr);
+    var mimetext = xhr.responseText;
     // strip off leading boundary
     var leading = "--" + boundary + "\r\n";
     var last = "\r\n--" + boundary + "--";
@@ -163,11 +164,17 @@
     return sections;
   }
   
-  // parse out the multipart
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/multipart?attachments=true",
+    {headers:{"accept": "multipart/related,*/*;"}});
   
   T(xhr.status == 200);
   
-  var sections = parseMime(boundary, xhr.responseText);
+  // parse out the multipart
+  
+  var sections = parseMultipart(xhr);
+  
+  T(sections.length == 3);
   
   // The first section is the json doc. Check it's content-type. It contains
   // the metadata for all the following attachments
@@ -176,10 +183,66 @@
   
   var doc = JSON.parse(sections[0].body);
   
-  T(doc._attachments['foo.txt'].follows = true);
-  T(doc._attachments['bar.txt'].follows = true);
+  T(doc._attachments['foo.txt'].follows == true);
+  T(doc._attachments['bar.txt'].follows == true);
   
   T(sections[1].body == "this is 21 chars long");
   T(sections[2].body == "this is 18 chars l");
   
+  // now get attachments incrementally (only the attachments changes since
+  // a certain rev).
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/multipart?atts_since=[\"" + firstrev + "\"]",
+    {headers:{"accept": "multipart/related,*/*;"}});
+  
+  T(xhr.status == 200);
+  
+  var sections = parseMultipart(xhr);
+  
+  T(sections.length == 2);
+  
+  var doc = JSON.parse(sections[0].body);
+  
+  T(doc._attachments['foo.txt'].stub == true);
+  T(doc._attachments['bar.txt'].follows == true);
+  
+  T(sections[1].body == "this is 18 chars l");
+  
+  // try it with a rev that doesn't exist (should get all attachments)
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/multipart?atts_since=[\"1-2897589\"]",
+    {headers:{"accept": "multipart/related,*/*;"}});
+  
+  T(xhr.status == 200);
+  
+  var sections = parseMultipart(xhr);
+  
+  T(sections.length == 3);
+  
+  var doc = JSON.parse(sections[0].body);
+  
+  T(doc._attachments['foo.txt'].follows == true);
+  T(doc._attachments['bar.txt'].follows == true);
+  
+  T(sections[1].body == "this is 21 chars long");
+  T(sections[2].body == "this is 18 chars l");
+  
+  // try it with a rev that doesn't exist, and one that does
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/multipart?atts_since=[\"1-2897589\",\"" + firstrev + "\"]",
+    {headers:{"accept": "multipart/related,*/*;"}});
+  
+  T(xhr.status == 200);
+  
+  var sections = parseMultipart(xhr);
+  
+  T(sections.length == 2);
+  
+  var doc = JSON.parse(sections[0].body);
+  
+  T(doc._attachments['foo.txt'].stub == true);
+  T(doc._attachments['bar.txt'].follows == true);
+  
+  T(sections[1].body == "this is 18 chars l");
+  
 };

Modified: couchdb/trunk/src/couchdb/couch_db.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.erl?rev=836324&r1=836323&r2=836324&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_db.erl Sun Nov 15 04:11:11 2009
@@ -15,7 +15,7 @@
 
 -export([open/2,close/1,create/2,start_compact/1,get_db_info/1,get_design_docs/1]).
 -export([open_ref_counted/2,is_idle/1,monitor/1,count_changes_since/2]).
--export([update_doc/3,update_docs/4,update_docs/2,update_docs/3,delete_doc/3]).
+-export([update_doc/3,update_doc/4,update_docs/4,update_docs/2,update_docs/3,delete_doc/3]).
 -export([get_doc_info/2,open_doc/2,open_doc/3,open_doc_revs/4]).
 -export([set_revs_limit/2,get_revs_limit/1,register_update_notifier/3]).
 -export([get_missing_revs/2,name/1,doc_to_tree/1,get_update_seq/1,get_committed_update_seq/1]).
@@ -251,7 +251,10 @@
     Name.
 
 update_doc(Db, Doc, Options) ->
-    case update_docs(Db, [Doc], Options) of
+    update_doc(Db, Doc, Options, interactive_edit).
+
+update_doc(Db, Doc, Options, UpdateType) ->
+    case update_docs(Db, [Doc], Options, UpdateType) of
     {ok, [{ok, NewRev}]} ->
         {ok, NewRev};
     {ok, [Error]} ->

Modified: couchdb/trunk/src/couchdb/couch_doc.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_doc.erl?rev=836324&r1=836323&r2=836324&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_doc.erl (original)
+++ couchdb/trunk/src/couchdb/couch_doc.erl Sun Nov 15 04:11:11 2009
@@ -322,7 +322,7 @@
             {ok, #att{revpos=RevPos}=DiskAtt} ->
                 DiskAtt;
             _ ->
-                throw({missing_stub_on_target,
+                throw({missing_stub,
                         <<"id:", Id/binary, ", name:", Name/binary>>})
             end;
         (Att) ->

Modified: couchdb/trunk/src/couchdb/couch_httpd.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd.erl?rev=836324&r1=836323&r2=836324&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd.erl Sun Nov 15 04:11:11 2009
@@ -559,6 +559,8 @@
     {400, <<"illegal_database_name">>, <<"Only lowercase characters (a-z), "
         "digits (0-9), and any of the characters _, $, (, ), +, -, and / "
         "are allowed">>};
+error_info({missing_stub, Reason}) ->
+    {412, <<"missing_stub">>, Reason};
 error_info({Error, Reason}) ->
     {500, couch_util:to_binary(Error), couch_util:to_binary(Reason)};
 error_info(Error) ->

Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=836324&r1=836323&r2=836324&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Sun Nov 15 04:11:11 2009
@@ -30,6 +30,7 @@
     rev = nil,
     open_revs = [],
     show = nil,
+    update_type = interactive_edit,
     atts_since = nil
 }).
 
@@ -780,6 +781,9 @@
     ]});
 
 db_doc_req(#httpd{method='PUT'}=Req, Db, DocId) ->
+    #doc_query_args{
+        update_type = UpdateType
+    } = parse_doc_query(Req),
     couch_doc:validate_docid(DocId),
     
     Loc = absolute_uri(Req, "/" ++ ?b2l(Db#db.name) ++ "/" ++ ?b2l(DocId)),
@@ -789,7 +793,7 @@
         Doc0 = couch_doc:doc_from_multi_part_stream(ContentType,
                 fun() -> receive_request_data(Req) end),
         Doc = couch_doc_from_req(Req, DocId, Doc0),
-        update_doc(Req, Db, DocId, Doc, RespHeaders);
+        update_doc(Req, Db, DocId, Doc, RespHeaders, UpdateType);
     _ ->
         case couch_httpd:qs_value(Req, "batch") of
         "ok" ->
@@ -810,7 +814,7 @@
         _Normal ->
             % normal
             Doc = couch_doc_from_req(Req, DocId, couch_httpd:json_body(Req)),
-            update_doc(Req, Db, DocId, Doc, RespHeaders)
+            update_doc(Req, Db, DocId, Doc, RespHeaders, UpdateType)
         end
     end;
 
@@ -911,7 +915,10 @@
 update_doc(Req, Db, DocId, Doc) ->
     update_doc(Req, Db, DocId, Doc, []).
 
-update_doc(Req, Db, DocId, #doc{deleted=Deleted}=Doc, Headers) ->
+update_doc(Req, Db, DocId, Doc, Headers) ->
+    update_doc(Req, Db, DocId, Doc, Headers, interactive_edit).
+
+update_doc(Req, Db, DocId, #doc{deleted=Deleted}=Doc, Headers, UpdateType) ->
     case couch_httpd:header_value(Req, "X-Couch-Full-Commit") of
     "true" ->
         Options = [full_commit];
@@ -920,7 +927,7 @@
     _ ->
         Options = []
     end,
-    {ok, NewRev} = couch_db:update_doc(Db, Doc, Options),
+    {ok, NewRev} = couch_db:update_doc(Db, Doc, Options, UpdateType),
     NewRevStr = couch_doc:rev_to_str(NewRev),
     ResponseHeaders = [{"Etag", <<"\"", NewRevStr/binary, "\"">>}] ++ Headers,
     send_json(Req, if Deleted -> 200; true -> 201 end,
@@ -1133,6 +1140,10 @@
             Args#doc_query_args{atts_since = couch_doc:parse_revs(JsonArray)};
         {"show", FormatStr} ->
             Args#doc_query_args{show=parse_doc_format(FormatStr)};
+        {"new_edits", "false"} ->
+            Args#doc_query_args{update_type=replicated_changes};
+        {"new_edits", "true"} ->
+            Args#doc_query_args{update_type=interactive_edit};
         _Else -> % unknown key value pair, ignore.
             Args
         end