couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jch...@apache.org
Subject svn commit: r730016 - in /couchdb/trunk: ./ etc/couchdb/ etc/default/ share/server/ share/www/script/ src/couchdb/
Date Mon, 29 Dec 2008 23:43:11 GMT
Author: jchris
Date: Mon Dec 29 15:43:10 2008
New Revision: 730016

URL: http://svn.apache.org/viewvc?rev=730016&view=rev
Log:
merge form branch to trunk

Added:
    couchdb/trunk/src/couchdb/couch_httpd_form.erl
      - copied unchanged from r730015, couchdb/branches/form/src/couchdb/couch_httpd_form.erl
Modified:
    couchdb/trunk/   (props changed)
    couchdb/trunk/etc/couchdb/default.ini.tpl.in
    couchdb/trunk/etc/couchdb/local_dev.ini
    couchdb/trunk/etc/default/couchdb   (props changed)
    couchdb/trunk/share/server/main.js
    couchdb/trunk/share/www/script/couch_tests.js
    couchdb/trunk/src/couchdb/Makefile.am
    couchdb/trunk/src/couchdb/couch_external_manager.erl
    couchdb/trunk/src/couchdb/couch_external_server.erl
    couchdb/trunk/src/couchdb/couch_httpd_external.erl
    couchdb/trunk/src/couchdb/couch_httpd_misc_handlers.erl
    couchdb/trunk/src/couchdb/couch_query_servers.erl

Propchange: couchdb/trunk/
------------------------------------------------------------------------------
    svn:mergeinfo = /couchdb/branches/form:729440-730015

Modified: couchdb/trunk/etc/couchdb/default.ini.tpl.in
URL: http://svn.apache.org/viewvc/couchdb/trunk/etc/couchdb/default.ini.tpl.in?rev=730016&r1=730015&r2=730016&view=diff
==============================================================================
--- couchdb/trunk/etc/couchdb/default.ini.tpl.in (original)
+++ couchdb/trunk/etc/couchdb/default.ini.tpl.in Mon Dec 29 15:43:10 2008
@@ -47,6 +47,7 @@
 [httpd_db_handlers]
 _view = {couch_httpd_view, handle_view_req}
 _temp_view = {couch_httpd_view, handle_temp_view_req}
+_form = {couch_httpd_form, handle_form_req}
 
 ; The external module takes an optional argument allowing you to narrow it to a
 ; single script. Otherwise the script name is inferred from the first path section 

Modified: couchdb/trunk/etc/couchdb/local_dev.ini
URL: http://svn.apache.org/viewvc/couchdb/trunk/etc/couchdb/local_dev.ini?rev=730016&r1=730015&r2=730016&view=diff
==============================================================================
--- couchdb/trunk/etc/couchdb/local_dev.ini (original)
+++ couchdb/trunk/etc/couchdb/local_dev.ini Mon Dec 29 15:43:10 2008
@@ -17,3 +17,6 @@
 [update_notification]
 ;unique notifier name=/full/path/to/exe -with "cmd line arg"
 
+
+[test]
+foo = bar

Propchange: couchdb/trunk/etc/default/couchdb
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Dec 29 15:43:10 2008
@@ -0,0 +1,2 @@
+/couchdb/branches/form/etc/default/couchdb:729440-730015
+/incubator/couchdb/trunk/etc/default/couchdb:642419-694440

Modified: couchdb/trunk/share/server/main.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/server/main.js?rev=730016&r1=730015&r2=730016&view=diff
==============================================================================
--- couchdb/trunk/share/server/main.js [utf-8] (original)
+++ couchdb/trunk/share/server/main.js [utf-8] Mon Dec 29 15:43:10 2008
@@ -32,12 +32,181 @@
   print(toJSON({log: toJSON(message)}));  
 }
 
+// mimeparse.js
+// http://code.google.com/p/mimeparse/
+// Code with comments: http://mimeparse.googlecode.com/svn/trunk/mimeparse.js
+// Tests: http://mimeparse.googlecode.com/svn/trunk/mimeparse-js-test.html
+// Ported from version 0.1.2
+
+var Mimeparse = (function() {
+  function strip(string) {
+    return string.replace(/^\s+/, '').replace(/\s+$/, '')
+  };
+  function parseRanges(ranges) {
+    var parsedRanges = [], rangeParts = ranges.split(",");
+    for (var i=0; i < rangeParts.length; i++) {
+      parsedRanges.push(publicMethods.parseMediaRange(rangeParts[i]))
+    };
+    return parsedRanges;
+  };
+  var publicMethods = {
+    parseMimeType : function(mimeType) {
+      var fullType, typeParts, params = {}, parts = mimeType.split(';');
+      for (var i=0; i < parts.length; i++) {
+        var p = parts[i].split('=');
+        if (p.length == 2) {
+          params[strip(p[0])] = strip(p[1]);
+        }
+      };
+      fullType = parts[0].replace(/^\s+/, '').replace(/\s+$/, '');
+      if (fullType == '*') fullType = '*/*';
+      typeParts = fullType.split('/');
+      return [typeParts[0], typeParts[1], params];
+    },
+    parseMediaRange : function(range) {
+      var q, parsedType = this.parseMimeType(range);
+      if (!parsedType[2]['q']) {
+        parsedType[2]['q'] = '1';
+      } else {
+        q = parseFloat(parsedType[2]['q']);
+        if (isNaN(q)) {
+          parsedType[2]['q'] = '1';
+        } else if (q > 1 || q < 0) {
+          parsedType[2]['q'] = '1';
+        }
+      }
+      return parsedType;
+    },
+    fitnessAndQualityParsed : function(mimeType, parsedRanges) {
+      var bestFitness = -1, bestFitQ = 0, target = this.parseMediaRange(mimeType);
+      var targetType = target[0], targetSubtype = target[1], targetParams = target[2];
+
+      for (var i=0; i < parsedRanges.length; i++) {
+        var parsed = parsedRanges[i];
+        var type = parsed[0], subtype = parsed[1], params = parsed[2];
+        if ((type == targetType || type == "*" || targetType == "*") &&
+          (subtype == targetSubtype || subtype == "*" || targetSubtype == "*")) {
+          var matchCount = 0;
+          for (param in targetParams) {
+            if (param != 'q' && params[param] && params[param] == targetParams[param])
{
+              matchCount += 1;
+            }
+          }
+
+          var fitness = (type == targetType) ? 100 : 0;
+          fitness += (subtype == targetSubtype) ? 10 : 0;
+          fitness += matchCount;
+
+          if (fitness > bestFitness) {
+            bestFitness = fitness;
+            bestFitQ = params["q"];
+          }
+        }
+      };
+      return [bestFitness, parseFloat(bestFitQ)];
+    },
+    qualityParsed : function(mimeType, parsedRanges) {
+      return this.fitnessAndQualityParsed(mimeType, parsedRanges)[1];
+    },
+    quality : function(mimeType, ranges) {
+      return this.qualityParsed(mimeType, parseRanges(ranges));
+    },
+
+    // Takes a list of supported mime-types and finds the best
+    // match for all the media-ranges listed in header. The value of
+    // header must be a string that conforms to the format of the
+    // HTTP Accept: header. The value of 'supported' is a list of
+    // mime-types.
+    //
+    // >>> bestMatch(['application/xbel+xml', 'text/xml'], 'text/*;q=0.5,*/*; q=0.1')
+    // 'text/xml'
+    bestMatch : function(supported, header) {
+      var parsedHeader = parseRanges(header);
+      var weighted = [];
+      for (var i=0; i < supported.length; i++) {
+        weighted.push([publicMethods.fitnessAndQualityParsed(supported[i], parsedHeader),
supported[i]])
+      };
+      weighted.sort();
+      return weighted[weighted.length-1][0][1] ? weighted[weighted.length-1][1] : '';
+    }
+  }
+  return publicMethods;
+})();
+
+
+// this function provides a shortcut for managing responses by Accept header
+respondWith = function(req, responders) {
+  var accept = req.headers["Accept"];
+  if (accept) {
+    var provides = [];
+    for (key in responders) {
+      if (mimesByKey[key]) {
+        provides = provides.concat(mimesByKey[key]);        
+      }
+    }
+    var bestMime = Mimeparse.bestMatch(provides, accept);
+    var bestKey = keysByMime[bestMime];
+    var rFunc = responders[bestKey];
+    if (rFunc) {
+      var resp = rFunc();
+      resp["headers"] = resp["headers"] || {};
+      resp["headers"]["Content-Type"] = bestMime;
+      return resp;
+    }
+  } 
+  if (responders.default) {
+    return responders[responders.default]();
+  } 
+  throw({code:406, body:"Not Acceptable: "+accept});
+}
+
+// whoever registers last wins.
+mimesByKey = {};
+keysByMime = {};
+registerType = function() {
+  var mimes = [], key = arguments[0];
+  for (var i=1; i < arguments.length; i++) {
+    mimes.push(arguments[i]);
+  };
+  mimesByKey[key] = mimes;
+  for (var i=0; i < mimes.length; i++) {
+    keysByMime[mimes[i]] = key;
+  };
+};
+
+// Some default types
+// Ported from Ruby on Rails
+// Build list of Mime types for HTTP responses
+// http://www.iana.org/assignments/media-types/
+// http://dev.rubyonrails.org/svn/rails/trunk/actionpack/lib/action_controller/mime_types.rb
+
+registerType("all", "*/*");
+registerType("text", "text/plain", "txt");
+registerType("html", "text/html", "application/xhtml+xml", "xhtml");
+registerType("xml", "application/xml", "text/xml", "application/x-xml");
+// http://www.ietf.org/rfc/rfc4627.txt
+registerType("json", "application/json", "text/x-json");
+registerType("js", "text/javascript", "application/javascript", "application/x-javascript");
+registerType("css", "text/css");
+registerType("ics", "text/calendar");
+registerType("csv", "text/csv");
+registerType("rss", "application/rss+xml");
+registerType("atom", "application/atom+xml");
+registerType("yaml", "application/x-yaml", "text/yaml");
+// just like Rails
+registerType("multipart_form", "multipart/form-data");
+registerType("url_encoded_form", "application/x-www-form-urlencoded");
+
+// ok back to business.
+
 try {
   // if possible, use evalcx (not always available)
   sandbox = evalcx('');
   sandbox.emit = emit;
   sandbox.sum = sum;
   sandbox.log = log;
+  sandbox.respondWith = respondWith;
+  sandbox.registerType = registerType;
 } catch (e) {}
 
 // Commands are in the form of json arrays:
@@ -167,6 +336,19 @@
           print(toJSON(error));
         }
         break;
+      case "form":
+        var funSrc = cmd[1];
+        var doc = cmd[2];
+        var req = cmd[3];
+        try {
+          var formFun = compileFunction(funSrc);
+          var rendered = formFun(doc, req);
+          print(toJSON(rendered));
+        } catch (error) {
+          log({error:(error||"undefined error")});
+          print(toJSON(error));
+        }
+        break;
       default:
         print(toJSON({error: "query_server_error",
             reason: "unknown command '" + cmd[0] + "'"}));

Modified: couchdb/trunk/share/www/script/couch_tests.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch_tests.js?rev=730016&r1=730015&r2=730016&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/couch_tests.js [utf-8] (original)
+++ couchdb/trunk/share/www/script/couch_tests.js [utf-8] Mon Dec 29 15:43:10 2008
@@ -2029,6 +2029,259 @@
     T(xhr.status == 200)
   },
 
+   forms: function(debug) {
+     var db = new CouchDB("test_suite_db");
+     db.deleteDb();
+     db.createDb();
+     if (debug) debugger;
+         
+     var designDoc = {
+       _id:"_design/template",
+       language: "javascript",
+       forms: {
+         "hello" : (function() { 
+           return {
+             body : "Hello World"
+           };
+         }).toString(),
+         "just-name" : (function(doc, req) {
+           return {
+             body : "Just " + doc.name
+           };
+         }).toString(),
+         "req-info" : (function(doc, req) {
+           return {
+             json : req
+           }
+         }).toString(),
+         "xml-type" : (function(doc, req) {
+           return {
+             headers : {
+               "Content-Type" : "application/xml"
+             },
+             "body" : <xml><node foo="bar"/></xml>
+           }
+         }).toString(),
+         "no-set-etag" : (function(doc, req) {
+           return {
+             headers : {
+               "Etag" : "skipped"
+             },
+             "body" : "something"
+           }
+         }).toString(),
+         "accept-switch" : (function(doc, req) {
+           if (req.headers["Accept"].match(/image/)) {
+             return {
+               // a 16x16 px version of the CouchDB logo
+               "base64" : ["iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAsV",
+               "BMVEUAAAD////////////////////////5ur3rEBn////////////////wDBL/",
+               "AADuBAe9EB3IEBz/7+//X1/qBQn2AgP/f3/ilpzsDxfpChDtDhXeCA76AQH/v7",
+               "/84eLyWV/uc3bJPEf/Dw/uw8bRWmP1h4zxSlD6YGHuQ0f6g4XyQkXvCA36MDH6",
+               "wMH/z8/yAwX64ODeh47BHiv/Ly/20dLQLTj98PDXWmP/Pz//39/wGyJ7Iy9JAA",
+               "AADHRSTlMAbw8vf08/bz+Pv19jK/W3AAAAg0lEQVR4Xp3LRQ4DQRBD0QqTm4Y5",
+               "zMxw/4OleiJlHeUtv2X6RbNO1Uqj9g0RMCuQO0vBIg4vMFeOpCWIWmDOw82fZx",
+               "vaND1c8OG4vrdOqD8YwgpDYDxRgkSm5rwu0nQVBJuMg++pLXZyr5jnc1BaH4GT",
+               "LvEliY253nA3pVhQqdPt0f/erJkMGMB8xucAAAAASUVORK5CYII="].join(''),
+               headers : {
+                 "Content-Type" : "image/png",
+                 "Vary" : "Accept" // we set this for proxy caches
+               }
+             };
+           } else {
+             return {
+               "body" : "accepting text requests",
+               headers : {
+                 "Content-Type" : "text/html",
+                 "Vary" : "Accept"
+               }
+             };
+           }
+         }).toString(),
+         "respondWith" : (function(doc, req) {
+           registerType("foo", "application/foo","application/x-foo");
+           return respondWith(req, {
+             html : function() {
+               return {
+                 body:"Ha ha, you said \"" + doc.word + "\"."
+               };
+             },
+             xml : function() {
+               return {
+                 body: <xml><node foo={doc.word}/></xml>
+               };
+             },
+             foo : function() {
+               return {
+                 body: "foofoo"
+               };
+             },
+             default : "html"
+           });
+         }).toString()
+       }
+     };
+     T(db.save(designDoc).ok);
+     
+     var doc = {"word":"plankton", "name":"Rusty"}
+     var resp = db.save(doc);
+     T(resp.ok);
+     var docid = resp.id;
+ 
+     // form error
+     var xhr = CouchDB.request("GET", "/test_suite_db/_form/");
+     T(xhr.status == 404);
+     T(JSON.parse(xhr.responseText).reason == "Invalid path.");
+ 
+     // hello template world
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/hello/"+docid);
+     T(xhr.responseText == "Hello World");
+     
+     // form with doc
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/just-name/"+docid);
+     T(xhr.responseText == "Just Rusty");
+     
+     // form with missing doc
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/just-name/missingdoc");
+     T(xhr.status == 404);
+     var resp = JSON.parse(xhr.responseText);
+     T(resp.error == "not_found");
+     T(resp.reason == "missing");
+     
+     // missing design doc
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/missingdoc/just-name/"+docid);
+     T(xhr.status == 404);
+     var resp = JSON.parse(xhr.responseText);
+     T(resp.error == "not_found");
+     T(resp.reason == "missing_design_doc");
+     
+     // query parameters
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/req-info/"+docid+"?foo=bar",
{
+       headers: {
+         "Accept": "text/html;text/plain;*/*",
+         "X-Foo" : "bar"
+       }
+     });
+     var resp = JSON.parse(xhr.responseText);
+     T(equals(resp.headers["X-Foo"], "bar"));
+     T(equals(resp.query, {foo:"bar"}));
+     T(equals(resp.verb, "GET"));
+     T(equals(resp.path[4], docid));
+     T(equals(resp.info.db_name, "test_suite_db"));
+     
+     // returning a content-type
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/xml-type/"+docid);
+     T("application/xml" == xhr.getResponseHeader("Content-Type"));
+     T("Accept" == xhr.getResponseHeader("Vary"));
+ 
+     // accept header switching
+     // different mime has different etag
+     
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/accept-switch/"+docid, {
+       headers: {"Accept": "text/html;text/plain;*/*"}
+     });
+     T("text/html" == xhr.getResponseHeader("Content-Type"));
+     T("Accept" == xhr.getResponseHeader("Vary"));
+     var etag = xhr.getResponseHeader("etag");
+
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/accept-switch/"+docid, {
+       headers: {"Accept": "image/png;*/*"}
+     });
+     T(xhr.responseText.match(/PNG/))
+     T("image/png" == xhr.getResponseHeader("Content-Type"));
+     var etag2 = xhr.getResponseHeader("etag");
+     T(etag2 != etag);
+ 
+     // proper etags
+     // form with doc
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/just-name/"+docid);
+     // extract the ETag header values
+     etag = xhr.getResponseHeader("etag");
+     // get again with etag in request
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/just-name/"+docid, {
+       headers: {"if-none-match": etag}
+     });
+     // should be 304
+     T(xhr.status == 304);    
+ 
+     // update the doc
+     doc.name = "Crusty";
+     resp = db.save(doc);
+     T(resp.ok);
+     // req with same etag
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/just-name/"+docid, {
+       headers: {"if-none-match": etag}
+     });
+     // status is 200    
+     T(xhr.status == 200);
+ 
+     // get new etag and request again
+     etag = xhr.getResponseHeader("etag");
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/just-name/"+docid, {
+       headers: {"if-none-match": etag}
+     });
+     // should be 304
+     T(xhr.status == 304);
+ 
+     // update design doc (but not function)
+     designDoc.isChanged = true;
+     T(db.save(designDoc).ok);
+     
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/just-name/"+docid, {
+       headers: {"if-none-match": etag}
+     });
+     // should be 304
+     T(xhr.status == 304);
+     
+     // update design doc function
+     designDoc.forms["just-name"] = (function(doc, req) {
+       return {
+         body : "Just old " + doc.name
+       };
+     }).toString();
+     T(db.save(designDoc).ok);
+ 
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/just-name/"+docid, {
+       headers: {"if-none-match": etag}
+     });
+     // status is 200    
+     T(xhr.status == 200);
+     
+     
+     // JS can't set etag
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/no-set-etag/"+docid);
+     // extract the ETag header values
+     etag = xhr.getResponseHeader("etag");
+     T(etag != "skipped")
+ 
+     // test the respondWith mime matcher
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/respondWith/"+docid, {
+       headers: {
+         "Accept": 'text/html,application/atom+xml; q=0.9'
+       }
+     });
+     T(xhr.getResponseHeader("Content-Type") == "text/html");
+     T(xhr.responseText == "Ha ha, you said \"plankton\".");
+ 
+     // now with xml
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/respondWith/"+docid, {
+       headers: {
+         "Accept": 'application/xml'
+       }
+     });
+     T(xhr.getResponseHeader("Content-Type") == "application/xml");
+     T(xhr.responseText.match(/plankton/));
+     
+     // registering types works
+     xhr = CouchDB.request("GET", "/test_suite_db/_form/template/respondWith/"+docid, {
+       headers: {
+         "Accept": "application/x-foo"
+       }
+     });
+     T(xhr.getResponseHeader("Content-Type") == "application/x-foo");
+     T(xhr.responseText.match(/foofoo/));
+   },
+
   compact: function(debug) {
     var db = new CouchDB("test_suite_db");
     db.deleteDb();

Modified: couchdb/trunk/src/couchdb/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/Makefile.am?rev=730016&r1=730015&r2=730016&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/Makefile.am (original)
+++ couchdb/trunk/src/couchdb/Makefile.am Mon Dec 29 15:43:10 2008
@@ -55,6 +55,7 @@
     couch_httpd.erl \
     couch_httpd_db.erl \
     couch_httpd_external.erl \
+    couch_httpd_form.erl \
     couch_httpd_view.erl \
     couch_httpd_misc_handlers.erl \
     couch_key_tree.erl \
@@ -90,6 +91,7 @@
     couch_httpd.beam \
     couch_httpd_db.beam \
     couch_httpd_external.beam \
+    couch_httpd_form.beam \
     couch_httpd_view.beam \
     couch_httpd_misc_handlers.beam \
     couch_key_tree.beam \

Modified: couchdb/trunk/src/couchdb/couch_external_manager.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_external_manager.erl?rev=730016&r1=730015&r2=730016&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_external_manager.erl (original)
+++ couchdb/trunk/src/couchdb/couch_external_manager.erl Mon Dec 29 15:43:10 2008
@@ -13,7 +13,7 @@
 -module(couch_external_manager).
 -behaviour(gen_server).
 
--export([start_link/0, execute/8, config_change/2]).
+-export([start_link/0, execute/2, config_change/2]).
 -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]).
 
 -include("couch_db.hrl").
@@ -21,13 +21,13 @@
 start_link() ->
     gen_server:start_link({local, couch_external_manager}, couch_external_manager, [], []).
 
-execute(UrlName, Db, Verb, Path, Query, Body, Post, Cookie) ->
+execute(UrlName, JsonReq) ->
     Pid = gen_server:call(couch_external_manager, {get, UrlName}),
     case Pid of
     {error, Reason} ->
         Reason;
     _ ->
-        couch_external_server:execute(Pid, Db, Verb, Path, Query, Body, Post, Cookie)
+        couch_external_server:execute(Pid, JsonReq)
     end.
 
 config_change("external", UrlName) ->

Modified: couchdb/trunk/src/couchdb/couch_external_server.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_external_server.erl?rev=730016&r1=730015&r2=730016&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_external_server.erl (original)
+++ couchdb/trunk/src/couchdb/couch_external_server.erl Mon Dec 29 15:43:10 2008
@@ -13,7 +13,7 @@
 -module(couch_external_server).
 -behaviour(gen_server).
 
--export([start_link/2, stop/1, execute/8]).
+-export([start_link/2, stop/1, execute/2]).
 -export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2, code_change/3]).

 
 -define(TIMEOUT, 5000).
@@ -28,8 +28,8 @@
 stop(Pid) ->
     gen_server:cast(Pid, stop).
 
-execute(Pid, Db, Verb, Path, Query, Body, Post, Cookie) ->
-    gen_server:call(Pid, {execute, Db, Verb, Path, Query, Body, Post, Cookie}).
+execute(Pid, JsonReq) ->
+    gen_server:call(Pid, {execute, JsonReq}).
 
 % Gen Server Handlers
 
@@ -43,18 +43,8 @@
     couch_os_process:stop(Pid),
     ok.
 
-handle_call({execute, Db, Verb, Path, Query, Body, Post, Cookie}, _From, {Name, Command,
Pid}) ->
-    ?LOG_DEBUG("Query Params ~p",[Query]),
-    {ok, Info} = couch_db:get_db_info(Db),
-    Json = {[
-        {<<"info">>, {Info}},
-        {<<"verb">>, Verb},
-        {<<"path">>, Path},
-        {<<"query">>, to_json_terms(Query)},
-        {<<"body">>, Body},
-        {<<"form">>, to_json_terms(Post)},
-        {<<"cookie">>, to_json_terms(Cookie)}]},
-    {reply, couch_os_process:prompt(Pid, Json), {Name, Command, Pid}}.
+handle_call({execute, JsonReq}, _From, {Name, Command, Pid}) ->
+    {reply, couch_os_process:prompt(Pid, JsonReq), {Name, Command, Pid}}.
 
 handle_info({'EXIT', Pid, Reason}, {Name, Command, Pid}) ->
     ?LOG_INFO("EXTERNAL: Restarting process for ~s (reason: ~w)", [Name, Reason]),
@@ -69,12 +59,4 @@
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-% Internal API
-
-to_json_terms(Data) ->
-    to_json_terms(Data, []).
-to_json_terms([], Acc) ->
-    {lists:reverse(Acc)};
-to_json_terms([{Key, Value} | Rest], Acc) ->
-    to_json_terms(Rest, [{list_to_binary(Key), list_to_binary(Value)} | Acc]).
 

Modified: couchdb/trunk/src/couchdb/couch_httpd_external.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_external.erl?rev=730016&r1=730015&r2=730016&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_external.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_external.erl Mon Dec 29 15:43:10 2008
@@ -13,6 +13,7 @@
 -module(couch_httpd_external).
 
 -export([handle_external_req/2, handle_external_req/3]).
+-export([send_external_response/2, json_req_obj/2]).
 
 -import(couch_httpd,[send_error/4]).
 
@@ -25,50 +26,82 @@
     headers = []
 }).
 
-process_external_req(#httpd{mochi_req=Req, 
-                        method=Verb
-                    }=HttpReq, Db, Name, Path) ->
-    ReqBody = Req:recv_body(),
-    ParsedForm = case Req:get_primary_header_value("content-type") of
-        "application/x-www-form-urlencoded" ++ _ ->
-            mochiweb_util:parse_qs(ReqBody);
-        _ ->
-            []
-    end,
+% handle_external_req/2
+% for the old type of config usage:
+% _external = {couch_httpd_external, handle_external_req}
+% with urls like
+% /db/_external/action/design/name
+handle_external_req(#httpd{
+                        path_parts=[_DbName, _External, UrlName | _Path]
+                    }=HttpReq, Db) ->
+    process_external_req(HttpReq, Db, UrlName);
+handle_external_req(#httpd{path_parts=[_, _]}=Req, _Db) ->
+    send_error(Req, 404, <<"external_server_error">>, <<"No server name
specified.">>);
+handle_external_req(Req, _) ->
+    send_error(Req, 404, <<"external_server_error">>, <<"Broken assumption">>).
+
+% handle_external_req/3 
+% for this type of config usage:
+% _action = {couch_httpd_external, handle_external_req, <<"action">>}
+% with urls like
+% /db/_action/design/name
+handle_external_req(HttpReq, Db, Name) ->
+    process_external_req(HttpReq, Db, Name).
+
+process_external_req(HttpReq, Db, Name) ->
+
     Response = couch_external_manager:execute(binary_to_list(Name), 
-        Db, Verb, Path, Req:parse_qs(), ReqBody, ParsedForm,
-        Req:parse_cookie()),
-        
+        json_req_obj(HttpReq, Db)),
+
     case Response of
     {unknown_external_server, Msg} ->
         send_error(HttpReq, 404, <<"external_server_error">>, Msg);
     _ ->
-        send_external_response(Req, Response)
+        send_external_response(HttpReq, Response)
     end.
 
-handle_external_req(#httpd{
-                        path_parts=[_DbName, _External, UrlName | Path]
-                    }=HttpReq, Db) ->
-    process_external_req(HttpReq, Db, UrlName, Path);
-handle_external_req(#httpd{path_parts=[_, _]}=Req, _Db) ->
-    send_error(Req, 404, <<"external_server_error">>, <<"No server name
specified.">>);
-handle_external_req(Req, _) ->
-    send_error(Req, 404, <<"external_server_error">>, <<"Broken assumption">>).
+json_req_obj(#httpd{mochi_req=Req, 
+               method=Verb,
+               path_parts=Path
+            }, Db) ->
+    ReqBody = Req:recv_body(),
+    ParsedForm = case Req:get_primary_header_value("content-type") of
+        "application/x-www-form-urlencoded" ++ _ ->
+            mochiweb_util:parse_qs(ReqBody);
+        _ ->
+            []
+    end,
+    Headers = Req:get(headers),
+    Hlist = mochiweb_headers:to_list(Headers),
+    {ok, Info} = couch_db:get_db_info(Db),
+    % add headers...
+    {[{<<"info">>, {Info}},
+        {<<"verb">>, Verb},
+        {<<"path">>, Path},
+        {<<"query">>, to_json_terms(Req:parse_qs())},
+        {<<"headers">>, to_json_terms(Hlist)},
+        {<<"body">>, ReqBody},
+        {<<"form">>, to_json_terms(ParsedForm)},
+        {<<"cookie">>, to_json_terms(Req:parse_cookie())}]}.
+
+to_json_terms(Data) ->
+    to_json_terms(Data, []).
+to_json_terms([], Acc) ->
+    {lists:reverse(Acc)};
+to_json_terms([{Key, Value} | Rest], Acc) when is_atom(Key) ->
+    to_json_terms(Rest, [{list_to_binary(atom_to_list(Key)), list_to_binary(Value)} | Acc]);
+to_json_terms([{Key, Value} | Rest], Acc) ->
+    to_json_terms(Rest, [{list_to_binary(Key), list_to_binary(Value)} | Acc]).
 
-handle_external_req(#httpd{
-                        path_parts=[_DbName, _External | Path]
-                    }=HttpReq, Db, Name) ->
-    process_external_req(HttpReq, Db, Name, Path).
 
-send_external_response(Req, Response) ->
+send_external_response(#httpd{mochi_req=MochiReq}, Response) ->
     #extern_resp_args{
         code = Code,
         data = Data,
         ctype = CType,
         headers = Headers
     } = parse_external_response(Response),
-    ?LOG_DEBUG("External Response ~p",[Response]),
-    Resp = Req:respond({Code, 
+    Resp = MochiReq:respond({Code, 
         default_or_content_type(CType, Headers), chunked}),
     Resp:write_chunk(Data),
     Resp:write_chunk(""),
@@ -87,13 +120,15 @@
                     ctype="application/json"};
             {<<"body">>, Value} ->
                 Args#extern_resp_args{data=Value, ctype="text/html"};
+            {<<"base64">>, Value} ->
+                Args#extern_resp_args{data=couch_util:decodeBase64(Value), ctype="application/binary"};
             {<<"headers">>, {Headers}} ->
                 NewHeaders = lists:map(fun({Header, HVal}) ->
                     {binary_to_list(Header), binary_to_list(HVal)}
                 end, Headers),
                 Args#extern_resp_args{headers=NewHeaders};
             _ -> % unknown key
-                Msg = lists:flatten(io_lib:format("Invalid data from external server: ~s
= ~p", [Key, Value])),
+                Msg = lists:flatten(io_lib:format("Invalid data from external server: ~p",
[{Key, Value}])),
                 throw({external_response_error, Msg})
             end
         end, #extern_resp_args{}, Response).

Modified: couchdb/trunk/src/couchdb/couch_httpd_misc_handlers.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_misc_handlers.erl?rev=730016&r1=730015&r2=730016&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_misc_handlers.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_misc_handlers.erl Mon Dec 29 15:43:10 2008
@@ -24,7 +24,7 @@
 -import(couch_httpd,
     [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,
     start_json_response/2,send_chunk/2,end_json_response/1,
-    start_chunked_response/3]).
+    start_chunked_response/3, send_error/4]).
 
 % httpd global handlers
 
@@ -173,5 +173,5 @@
         {update_seq, NewSeq}
     ]});
 increment_update_seq_req(Req, _Db) ->
-    send_method_not_allowed(Req, "GET,PUT,DELETE").
+    send_method_not_allowed(Req, "POST").
 

Modified: couchdb/trunk/src/couchdb/couch_query_servers.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_query_servers.erl?rev=730016&r1=730015&r2=730016&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_query_servers.erl (original)
+++ couchdb/trunk/src/couchdb/couch_query_servers.erl Mon Dec 29 15:43:10 2008
@@ -17,7 +17,7 @@
 
 -export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2,code_change/3,stop/0]).
 -export([start_doc_map/2, map_docs/2, stop_doc_map/1]).
--export([reduce/3, rereduce/3,validate_doc_update/5]).
+-export([reduce/3, rereduce/3,validate_doc_update/5,render_doc_form/5]).
 % -export([test/0]).
 
 -include("couch_db.hrl").
@@ -122,6 +122,17 @@
         ok = ret_os_process(Lang, Pid)
     end.
 
+render_doc_form(Lang, FormSrc, Doc, Req, Db) ->
+    Pid = get_os_process(Lang),
+    JsonDoc = couch_doc:to_json_obj(Doc, [revs]),
+    JsonReq = couch_httpd_external:json_req_obj(Req, Db),
+    try couch_os_process:prompt(Pid, [<<"form">>, FormSrc, JsonDoc, JsonReq])
of
+    FormResp ->
+        FormResp
+    after
+        ok = ret_os_process(Lang, Pid)
+    end.
+
 init([]) ->
     
     % read config and register for configuration changes



Mime
View raw message