You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@couchdb.apache.org by Michael Hendricks <mi...@ndrix.org> on 2008/07/24 07:15:31 UTC

[PATCH] Expose UUID generation through the REST API

Because a POST request to create a new document is not idempotent, PUT
requests should be used when possible.  Unfortunately, generating a
sufficiently random document identifier from JavaScript is slightly
difficult.  To make the process easier, this patch exposes CouchDB's
functionality for generating UUIDs so that it's accessible with an
HTTP request.

This patch implements a new CouchDB URI _uuids which responds to
GET requests with a JSON object something like this:

    {
        "uuids" : [
            "405f021c2bdfee1cb0ead93b0864c2d2",
            "512d8fecc3f9a421a902f0815e5cbc3f",
            "fc5be6230fcffd061af50169ecb1647f"
        ],
    }

The number of UUIDs returned is specified with a "count" query
parameter.  The count parameter defaults to 1.

Signed-off-by: Michael Hendricks <mi...@ndrix.org>
---
 share/www/script/couch.js       |   11 +++++++++++
 share/www/script/couch_tests.js |   33 +++++++++++++++++++++++++++++++++
 src/couchdb/couch_httpd.erl     |   18 ++++++++++++++++++
 3 files changed, 62 insertions(+), 0 deletions(-)

diff --git a/share/www/script/couch.js b/share/www/script/couch.js
index 38c9cf3..fc6f8b0 100644
--- a/share/www/script/couch.js
+++ b/share/www/script/couch.js
@@ -160,6 +160,17 @@ function CouchDB(name) {
     return result;
   }
 
+  this.uuids = function(count) {
+    var uri = "/_uuids";
+    if (count) uri = uri + "?count=" + count;
+    var req = request("GET", uri);
+    var result = JSON.parse(req.responseText);
+    if (req.status != 200)
+      throw result;
+    return result;
+  }
+
+
   // Convert a options object to an url query string.
   // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"'
   function encodeOptions(options) {
diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js
index 852f9cf..711e3c1 100644
--- a/share/www/script/couch_tests.js
+++ b/share/www/script/couch_tests.js
@@ -1338,6 +1338,39 @@ var tests = {
     T(xhr.getResponseHeader("Content-Type") == "text/plain")
     T(db.info().doc_count == 1);
     T(db.info().disk_size < deletesize);
+  },
+
+  uuids: function(debug) {
+    var db = new CouchDB("test_suite_db");
+    db.deleteDb();
+    db.createDb();
+    if (debug) debugger;
+
+    // a single UUID without an explicit count
+    var result = db.uuids();
+    T(result.uuids.length == 1);
+    var first = result.uuids[0];
+
+    // a single UUID with an explicit count
+    result = db.uuids(1);
+    T( result.uuids.length == 1 );
+    var second = result.uuids[0];
+    T( first != second );
+
+    // no collisions with 1,000 UUIDs
+    result = db.uuids(1000);
+    T( result.uuids.length == 1000 );
+    var seen = {};
+    for( var i in result.uuids ) {
+        var id = result.uuids[i];
+        if ( seen[id] ) {
+            seen[id]++;
+            T( seen[id] == 1 );  // this will always fail
+        }
+        else {
+            seen[id] = 1;
+        }
+    }
   }
 };
 
diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
index af8d9b4..d41e1ac 100644
--- a/src/couchdb/couch_httpd.erl
+++ b/src/couchdb/couch_httpd.erl
@@ -97,6 +97,8 @@ handle_request0(Req, DocumentRoot, Method, Path) ->
             handle_replicate_request(Req, Method);
         "/_restart" ->
             handle_restart_request(Req, Method);
+        "/_uuids" ->
+            handle_uuids_request(Req, Method);
         "/_utils" ->
             {ok, Req:respond({301, [
                 {"Location", "/_utils/"}
@@ -147,6 +149,22 @@ handle_restart_request(Req, 'POST') ->
 handle_restart_request(_Req, _Method) ->
     throw({method_not_allowed, "POST"}).
 
+handle_uuids_request(Req, 'GET') ->
+    Count = list_to_integer(proplists:get_value("count", Req:parse_qs(), "1")),
+    % generate the uuids
+    UUIDs = lists:map(
+        fun (_) -> couch_util:new_uuid() end,
+        lists:seq(1,Count)
+    ),
+    % send a JSON response
+    send_json(Req, {obj, [
+        {"uuids", list_to_tuple(UUIDs)}
+    ]});
+
+handle_uuids_request(_Req, _Method) ->
+    throw({method_not_allowed, "GET"}).
+
+
 % Database request handlers
 
 handle_db_request(Req, Method, {Path}) ->
-- 
1.5.6


Re: [PATCH] Expose UUID generation through the REST API

Posted by Kevin Jackson <fo...@gmail.com>.
Hi,

> This looks good. I'll see about checking this in later.
>
> Anyone know if Micheal need to sign a ICLA or anything?

As far as I know, ICLAs are for when people are becoming committers.
If this is a patch that you (as a committer) accept and add to the src
tree I think your ICLA is used. IANAL etc etc, but I've sent in
patches that were applied by committers (other apache projects) and I
didn't need to sign an ICLA until I was invited to become a committer.

Kev

Re: [PATCH] Expose UUID generation through the REST API

Posted by Damien Katz <da...@apache.org>.
This looks good. I'll see about checking this in later.

Anyone know if Micheal need to sign a ICLA or anything?

On Jul 24, 2008, at 1:15 AM, Michael Hendricks wrote:

> Because a POST request to create a new document is not idempotent, PUT
> requests should be used when possible.  Unfortunately, generating a
> sufficiently random document identifier from JavaScript is slightly
> difficult.  To make the process easier, this patch exposes CouchDB's
> functionality for generating UUIDs so that it's accessible with an
> HTTP request.
>
> This patch implements a new CouchDB URI _uuids which responds to
> GET requests with a JSON object something like this:
>
>    {
>        "uuids" : [
>            "405f021c2bdfee1cb0ead93b0864c2d2",
>            "512d8fecc3f9a421a902f0815e5cbc3f",
>            "fc5be6230fcffd061af50169ecb1647f"
>        ],
>    }
>
> The number of UUIDs returned is specified with a "count" query
> parameter.  The count parameter defaults to 1.
>
> Signed-off-by: Michael Hendricks <mi...@ndrix.org>
> ---
> share/www/script/couch.js       |   11 +++++++++++
> share/www/script/couch_tests.js |   33 ++++++++++++++++++++++++++++++ 
> +++
> src/couchdb/couch_httpd.erl     |   18 ++++++++++++++++++
> 3 files changed, 62 insertions(+), 0 deletions(-)
>
> diff --git a/share/www/script/couch.js b/share/www/script/couch.js
> index 38c9cf3..fc6f8b0 100644
> --- a/share/www/script/couch.js
> +++ b/share/www/script/couch.js
> @@ -160,6 +160,17 @@ function CouchDB(name) {
>     return result;
>   }
>
> +  this.uuids = function(count) {
> +    var uri = "/_uuids";
> +    if (count) uri = uri + "?count=" + count;
> +    var req = request("GET", uri);
> +    var result = JSON.parse(req.responseText);
> +    if (req.status != 200)
> +      throw result;
> +    return result;
> +  }
> +
> +
>   // Convert a options object to an url query string.
>   // ex: {key:'value',key2:'value2'} becomes '? 
> key="value"&key2="value2"'
>   function encodeOptions(options) {
> diff --git a/share/www/script/couch_tests.js b/share/www/script/ 
> couch_tests.js
> index 852f9cf..711e3c1 100644
> --- a/share/www/script/couch_tests.js
> +++ b/share/www/script/couch_tests.js
> @@ -1338,6 +1338,39 @@ var tests = {
>     T(xhr.getResponseHeader("Content-Type") == "text/plain")
>     T(db.info().doc_count == 1);
>     T(db.info().disk_size < deletesize);
> +  },
> +
> +  uuids: function(debug) {
> +    var db = new CouchDB("test_suite_db");
> +    db.deleteDb();
> +    db.createDb();
> +    if (debug) debugger;
> +
> +    // a single UUID without an explicit count
> +    var result = db.uuids();
> +    T(result.uuids.length == 1);
> +    var first = result.uuids[0];
> +
> +    // a single UUID with an explicit count
> +    result = db.uuids(1);
> +    T( result.uuids.length == 1 );
> +    var second = result.uuids[0];
> +    T( first != second );
> +
> +    // no collisions with 1,000 UUIDs
> +    result = db.uuids(1000);
> +    T( result.uuids.length == 1000 );
> +    var seen = {};
> +    for( var i in result.uuids ) {
> +        var id = result.uuids[i];
> +        if ( seen[id] ) {
> +            seen[id]++;
> +            T( seen[id] == 1 );  // this will always fail
> +        }
> +        else {
> +            seen[id] = 1;
> +        }
> +    }
>   }
> };
>
> diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
> index af8d9b4..d41e1ac 100644
> --- a/src/couchdb/couch_httpd.erl
> +++ b/src/couchdb/couch_httpd.erl
> @@ -97,6 +97,8 @@ handle_request0(Req, DocumentRoot, Method, Path) ->
>             handle_replicate_request(Req, Method);
>         "/_restart" ->
>             handle_restart_request(Req, Method);
> +        "/_uuids" ->
> +            handle_uuids_request(Req, Method);
>         "/_utils" ->
>             {ok, Req:respond({301, [
>                 {"Location", "/_utils/"}
> @@ -147,6 +149,22 @@ handle_restart_request(Req, 'POST') ->
> handle_restart_request(_Req, _Method) ->
>     throw({method_not_allowed, "POST"}).
>
> +handle_uuids_request(Req, 'GET') ->
> +    Count = list_to_integer(proplists:get_value("count",  
> Req:parse_qs(), "1")),
> +    % generate the uuids
> +    UUIDs = lists:map(
> +        fun (_) -> couch_util:new_uuid() end,
> +        lists:seq(1,Count)
> +    ),
> +    % send a JSON response
> +    send_json(Req, {obj, [
> +        {"uuids", list_to_tuple(UUIDs)}
> +    ]});
> +
> +handle_uuids_request(_Req, _Method) ->
> +    throw({method_not_allowed, "GET"}).
> +
> +
> % Database request handlers
>
> handle_db_request(Req, Method, {Path}) ->
> -- 
> 1.5.6
>


Re: [PATCH] Expose UUID generation through the REST API

Posted by Damien Katz <da...@apache.org>.
Before I check this in, can someone add to the patch support for this  
in the couch.js library too?

That is, when creating a new document, library either generates a UUID  
locally (if the browser supports it) or via a GET to CouchDB. And  
ideally it would cache a bunch in memory so it's not necessary to get  
a new one for each document save. This work needs to be done both for  
single document creation and documents created via in a bulk post.

Once we have that in the patch, I'll check it in. We also need to  
update the wiki about the proper way to create a document.

-Damien


On Jul 24, 2008, at 1:15 AM, Michael Hendricks wrote:

> Because a POST request to create a new document is not idempotent, PUT
> requests should be used when possible.  Unfortunately, generating a
> sufficiently random document identifier from JavaScript is slightly
> difficult.  To make the process easier, this patch exposes CouchDB's
> functionality for generating UUIDs so that it's accessible with an
> HTTP request.
>
> This patch implements a new CouchDB URI _uuids which responds to
> GET requests with a JSON object something like this:
>
>    {
>        "uuids" : [
>            "405f021c2bdfee1cb0ead93b0864c2d2",
>            "512d8fecc3f9a421a902f0815e5cbc3f",
>            "fc5be6230fcffd061af50169ecb1647f"
>        ],
>    }
>
> The number of UUIDs returned is specified with a "count" query
> parameter.  The count parameter defaults to 1.
>
> Signed-off-by: Michael Hendricks <mi...@ndrix.org>
> ---
> share/www/script/couch.js       |   11 +++++++++++
> share/www/script/couch_tests.js |   33 ++++++++++++++++++++++++++++++ 
> +++
> src/couchdb/couch_httpd.erl     |   18 ++++++++++++++++++
> 3 files changed, 62 insertions(+), 0 deletions(-)
>
> diff --git a/share/www/script/couch.js b/share/www/script/couch.js
> index 38c9cf3..fc6f8b0 100644
> --- a/share/www/script/couch.js
> +++ b/share/www/script/couch.js
> @@ -160,6 +160,17 @@ function CouchDB(name) {
>     return result;
>   }
>
> +  this.uuids = function(count) {
> +    var uri = "/_uuids";
> +    if (count) uri = uri + "?count=" + count;
> +    var req = request("GET", uri);
> +    var result = JSON.parse(req.responseText);
> +    if (req.status != 200)
> +      throw result;
> +    return result;
> +  }
> +
> +
>   // Convert a options object to an url query string.
>   // ex: {key:'value',key2:'value2'} becomes '? 
> key="value"&key2="value2"'
>   function encodeOptions(options) {
> diff --git a/share/www/script/couch_tests.js b/share/www/script/ 
> couch_tests.js
> index 852f9cf..711e3c1 100644
> --- a/share/www/script/couch_tests.js
> +++ b/share/www/script/couch_tests.js
> @@ -1338,6 +1338,39 @@ var tests = {
>     T(xhr.getResponseHeader("Content-Type") == "text/plain")
>     T(db.info().doc_count == 1);
>     T(db.info().disk_size < deletesize);
> +  },
> +
> +  uuids: function(debug) {
> +    var db = new CouchDB("test_suite_db");
> +    db.deleteDb();
> +    db.createDb();
> +    if (debug) debugger;
> +
> +    // a single UUID without an explicit count
> +    var result = db.uuids();
> +    T(result.uuids.length == 1);
> +    var first = result.uuids[0];
> +
> +    // a single UUID with an explicit count
> +    result = db.uuids(1);
> +    T( result.uuids.length == 1 );
> +    var second = result.uuids[0];
> +    T( first != second );
> +
> +    // no collisions with 1,000 UUIDs
> +    result = db.uuids(1000);
> +    T( result.uuids.length == 1000 );
> +    var seen = {};
> +    for( var i in result.uuids ) {
> +        var id = result.uuids[i];
> +        if ( seen[id] ) {
> +            seen[id]++;
> +            T( seen[id] == 1 );  // this will always fail
> +        }
> +        else {
> +            seen[id] = 1;
> +        }
> +    }
>   }
> };
>
> diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
> index af8d9b4..d41e1ac 100644
> --- a/src/couchdb/couch_httpd.erl
> +++ b/src/couchdb/couch_httpd.erl
> @@ -97,6 +97,8 @@ handle_request0(Req, DocumentRoot, Method, Path) ->
>             handle_replicate_request(Req, Method);
>         "/_restart" ->
>             handle_restart_request(Req, Method);
> +        "/_uuids" ->
> +            handle_uuids_request(Req, Method);
>         "/_utils" ->
>             {ok, Req:respond({301, [
>                 {"Location", "/_utils/"}
> @@ -147,6 +149,22 @@ handle_restart_request(Req, 'POST') ->
> handle_restart_request(_Req, _Method) ->
>     throw({method_not_allowed, "POST"}).
>
> +handle_uuids_request(Req, 'GET') ->
> +    Count = list_to_integer(proplists:get_value("count",  
> Req:parse_qs(), "1")),
> +    % generate the uuids
> +    UUIDs = lists:map(
> +        fun (_) -> couch_util:new_uuid() end,
> +        lists:seq(1,Count)
> +    ),
> +    % send a JSON response
> +    send_json(Req, {obj, [
> +        {"uuids", list_to_tuple(UUIDs)}
> +    ]});
> +
> +handle_uuids_request(_Req, _Method) ->
> +    throw({method_not_allowed, "GET"}).
> +
> +
> % Database request handlers
>
> handle_db_request(Req, Method, {Path}) ->
> -- 
> 1.5.6
>