Updated Branches: refs/heads/1684-feature-db-updates [created] 15c31508b
Import couch_dbupdates from rcouch. This creates a new top level API endpoint: `/_db_updates` that returns a line of JSON for each database event along with the database name. A database event is one of `created`, `updated`, `deleted`. The API endpoint supports a `?feed=` parameter with the options: `longpoll`, `continuous` and `eventsource`. A second parameter `timeout=` specifies when the server should close the connection. `longpoll` closes the connection after a single notification. It is the default option. `continuous` keeps a socket open until the specified `timeout` or 60 seconds by default. `eventsource` works like continuous, but sends the data in EventSource format. See http://dev.w3.org/html5/eventsource/ The parameters are modelled after the existing `/_changes` API endpoint. Note that `/_db_updates` does not support resuming of notifications via a sequence ID. This is a port of the existing DbUpdateNotification interface to the HTTP API. Functional changes compared to rcouch: - make _db_updates an admin-only resource Docs: - updated api/misc to include basic info on `/_db_updates` License: Apache 2 license, updated LICENSE. Notice: (c) 2012 Benoit Chesneau, updated NOTICE. Tests: - only manual testing of the various API differences due to complications with asynchronous HTTP requests in the JS test suite and total annoyance of overly complicated ibrowse/httpc modules for writing etap tests. Recommendation to ship this as EXPERIMENTAL until we have tests. Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/15c31508 Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/15c31508 Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/15c31508 Branch: refs/heads/1684-feature-db-updates Commit: 15c31508b95692677f7634be573724e11d274f8f Parents: 2b8539d Author: Jan Lehnardt <[email protected]> Authored: Mon Mar 4 12:02:24 2013 +0100 Committer: Jan Lehnardt <[email protected]> Committed: Mon Mar 4 14:25:12 2013 +0100 ---------------------------------------------------------------------- CHANGES | 6 ++ LICENSE | 6 ++ NEWS | 2 + NOTICE | 4 + configure.ac | 1 + etc/couchdb/default.ini.tpl.in | 1 + license.skip | 1 + share/doc/src/api/misc.rst | 62 ++++++++++++++ src/Makefile.am | 1 + src/couch_dbupdates/LICENSE | 22 +++++ src/couch_dbupdates/Makefile.am | 33 ++++++++ src/couch_dbupdates/NOTICE | 6 ++ src/couch_dbupdates/README.md | 32 +++++++ src/couch_dbupdates/src/couch_dbupdates.app.src | 11 +++ src/couch_dbupdates/src/couch_dbupdates.erl | 46 ++++++++++ src/couch_dbupdates/src/couch_dbupdates_httpd.erl | 69 ++++++++++++++++ 16 files changed, 303 insertions(+), 0 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/CHANGES ---------------------------------------------------------------------- diff --git a/CHANGES b/CHANGES index 184bc64..cd12973 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,12 @@ Apache CouchDB CHANGES # # This version has not been released yet. # +# HTTP Interface +# +# * Added /_db_update API endpoint to get notified about creating, +# updating nad deleting of databases. Supports `continuous`, +# `longpoll` and `eventsource` `?feed=` parameter. +# # Test Suite: # # * Split up replicator_db tests into multiple independent tests. http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/LICENSE ---------------------------------------------------------------------- diff --git a/LICENSE b/LICENSE index 7d9d0ce..36b8aca 100644 --- a/LICENSE +++ b/LICENSE @@ -603,3 +603,9 @@ For the share/server/coffee-script.js file WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For the src/couch_dbupdates component + + 2009-2012 (c) Benoît Chesneau <[email protected]> + + Apache 2 License, see above. http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/NEWS ---------------------------------------------------------------------- diff --git a/NEWS b/NEWS index 13d395d..9fb1af2 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,8 @@ Each release section notes when backwards incompatible changes have been made. # ------------- # # This version has not been released yet. +# * Added /_db_update API endpoint to get notified about creating, +# updating nad deleting of databases. Version 1.3.0 ------------- http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/NOTICE ---------------------------------------------------------------------- diff --git a/NOTICE b/NOTICE index 6e2090b..194505f 100644 --- a/NOTICE +++ b/NOTICE @@ -85,3 +85,7 @@ This product also includes the following third-party components: * Underscore.js 1.4.2 (http://underscorejs.org) Copyright 2012, Jeremy Ashkenas + + * couch_dbupdates + + Copyright 2012, Benoît Chesneau <[email protected]> http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/configure.ac ---------------------------------------------------------------------- diff --git a/configure.ac b/configure.ac index 5246830..8d38e2d 100644 --- a/configure.ac +++ b/configure.ac @@ -720,6 +720,7 @@ AC_CONFIG_FILES([share/Makefile]) AC_CONFIG_FILES([share/doc/Makefile]) AC_CONFIG_FILES([share/doc/build/Makefile]) AC_CONFIG_FILES([src/Makefile]) +AC_CONFIG_FILES([src/couch_dbupdates/Makefile]) AC_CONFIG_FILES([src/couch_index/Makefile]) AC_CONFIG_FILES([src/couch_mrview/Makefile]) AC_CONFIG_FILES([src/couch_replicator/Makefile]) http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/etc/couchdb/default.ini.tpl.in ---------------------------------------------------------------------- diff --git a/etc/couchdb/default.ini.tpl.in b/etc/couchdb/default.ini.tpl.in index 94bf2fe..a9b0e65 100644 --- a/etc/couchdb/default.ini.tpl.in +++ b/etc/couchdb/default.ini.tpl.in @@ -157,6 +157,7 @@ _stats = {couch_httpd_stats_handlers, handle_stats_req} _log = {couch_httpd_misc_handlers, handle_log_req} _session = {couch_httpd_auth, handle_session_req} _oauth = {couch_httpd_oauth, handle_oauth_req} +_db_updates = {couch_dbupdates_httpd, handle_req} [httpd_db_handlers] _all_docs = {couch_mrview_http, handle_all_docs_req} http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/license.skip ---------------------------------------------------------------------- diff --git a/license.skip b/license.skip index 89a6b05..24f702f 100644 --- a/license.skip +++ b/license.skip @@ -98,6 +98,7 @@ ^src/couchdb/priv/couchspawnkillable ^src/couchdb/priv/stat_descriptions.cfg ^src/erlang-oauth/.* +^src/couch_dbupdates ^src/ejson/.* ^src/etap/.* ^src/ibrowse/.* http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/share/doc/src/api/misc.rst ---------------------------------------------------------------------- diff --git a/share/doc/src/api/misc.rst b/share/doc/src/api/misc.rst index f9562ae..c33fdc1 100644 --- a/share/doc/src/api/misc.rst +++ b/share/doc/src/api/misc.rst @@ -33,6 +33,8 @@ A list of the available methods and URL paths are provided below: +--------+-------------------------+-------------------------------------------+ | GET | /_all_dbs | Get a list of all the DBs | +--------+-------------------------+-------------------------------------------+ +| GET | /_db_updates | A feed of database events | ++--------+-------------------------+-------------------------------------------+ | GET | /_log | Return the server log file | +--------+-------------------------+-------------------------------------------+ | POST | /_replicate | Set or cancel replication | @@ -153,6 +155,66 @@ The return is a JSON array: "locations" ] +``GET /_db_updates`` +==================== + +* **Method**: ``GET /_db_updates`` +* **Request**: None +* **Admin Privileges Required**: yes +* **Query ARguments**: + + * **Argument**: feed + + * **Descroption**: Format of the response feed + * **Optional**: yes + * **Type**: string + * **Default**: longpoll + * **Supported Values**: + + * **longpoll**: Closes the connection after the first event. + * **continuous**: Send a line of JSON per event. Keeps the socket open until ``timeout``. + * **eventsource**: Like, ``continuous``, but sends the events in EventSource format. See http://dev.w3.org/html5/eventsource/ for details, + + * **Argument**: timeout + + * **Descroption**: Number of seconds until CouchDB closes the connection. + * **Optional**: yes + * **Type**: numeric + * **Default**: 60 + + * **Argument**: heartbeat + + * **Descroption**: Number of seconds until CouchDB sends a sole newline ``\n`` character so the client know the connection is still open, there are just no events occuring. + * **Optional**: yes + * **Type**: boolean + * **Default**: true + +* **Return Codes**: + + * **200** + Request completed successfully. + +Returns a list of all database events in the CouchDB instance. + +A database event is one of `created`, `updated`, `deleted`. + +For example: + +.. code-block:: http + + GET http://couchdb:5984/_db_events?feed=continuous + Accept: application/json + +.. code-block:: javascript + + {"dbname":"my-database", "type":"created"} + {"dbname":"my-database", "type":"updated"} + {"dbname":"another-database", "type":"created"} + {"dbname":"my-database", "type":"deleted"} + {"dbname":"another-database", "type":"updated"} + + + ``GET /_log`` ============= http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/src/Makefile.am ---------------------------------------------------------------------- diff --git a/src/Makefile.am b/src/Makefile.am index fbd514c..7b11e15 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,6 +11,7 @@ ## the License. SUBDIRS = \ + couch_dbupdates \ couch_index \ couch_mrview \ couch_replicator \ http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/src/couch_dbupdates/LICENSE ---------------------------------------------------------------------- diff --git a/src/couch_dbupdates/LICENSE b/src/couch_dbupdates/LICENSE new file mode 100644 index 0000000..a089916 --- /dev/null +++ b/src/couch_dbupdates/LICENSE @@ -0,0 +1,22 @@ +2009-2012 (c) Benoît Chesneau <[email protected]> + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/src/couch_dbupdates/Makefile.am ---------------------------------------------------------------------- diff --git a/src/couch_dbupdates/Makefile.am b/src/couch_dbupdates/Makefile.am new file mode 100644 index 0000000..d131b9b --- /dev/null +++ b/src/couch_dbupdates/Makefile.am @@ -0,0 +1,33 @@ +## Licensed under the Apache License, Version 2.0 (the "License"); you may not +## use this file except in compliance with the License. You may obtain a copy of +## the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +## WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +## License for the specific language governing permissions and limitations under +## the License. + +couch_dbupdateslibdir = $(localerlanglibdir)/couch_dbupdates-0.1 +couch_dbupdatesebindir = $(couch_dbupdateslibdir)/ebin + +couch_dbupdatesebin_DATA = $(compiled_files) + +EXTRA_DIST = $(source_files) +CLEANFILES = $(compiled_files) + +source_files = \ + src/couch_dbupdates.erl \ + src/couch_dbupdates.app.src \ + src/couch_dbupdates_httpd.erl + +compiled_files = \ + ebin/couch_dbupdates.beam \ + ebin/couch_dbupdates_httpd.beam + +ebin/%.beam: src/%.erl + @mkdir -p ebin/ + $(ERLC) -I$(top_srcdir)/src/couchdb -o ebin/ $(ERLC_FLAGS) ${TEST} $<; + http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/src/couch_dbupdates/NOTICE ---------------------------------------------------------------------- diff --git a/src/couch_dbupdates/NOTICE b/src/couch_dbupdates/NOTICE new file mode 100644 index 0000000..ee00a85 --- /dev/null +++ b/src/couch_dbupdates/NOTICE @@ -0,0 +1,6 @@ +couch_dbupdates +--------------- + +2012 (c) Benoît Chesneau <[email protected]> + +couch_dbupdates is released under the Apache License 2. http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/src/couch_dbupdates/README.md ---------------------------------------------------------------------- diff --git a/src/couch_dbupdates/README.md b/src/couch_dbupdates/README.md new file mode 100644 index 0000000..c683102 --- /dev/null +++ b/src/couch_dbupdates/README.md @@ -0,0 +1,32 @@ +# couch_dbupdates + +`couch_dbupdates` is a simple couchdb modules to receive databases +events in couchdb node. + +It's actually **supported by all the [refuge](http://refuge.io) projects**: + +- [refuge](https://github.com/refuge/refuge) +- [rcouch](https://github.com/refuge/rcouch) +- [rcouch_template](https://github.com/refuge/rcouch_template) + + +## HTTP API + +To get db events, do a GET to `/_db_updates` . + +You can pass an optional query parameters: + +* `feed` The feed can be `longpoll` (default) for longpolling, `eventsource` + for event stream or `continuous` for continuous feed. +* `timeout`: timeout before the longpolling connection close or when the + heartbeat is emitted. +* `heartbeat`: true, or false. an empty line is emittend when the + timeout occurs to maintain the connection active. + + +## Example of usage + + $ curl http://127.0.0.1:5984/_db_updates?feed=continuous + {"type":"created","db_name":"testdb"} + {"type":"updated","db_name":"testdb"} + {"type":"deleted","db_name":"testdb"} http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/src/couch_dbupdates/src/couch_dbupdates.app.src ---------------------------------------------------------------------- diff --git a/src/couch_dbupdates/src/couch_dbupdates.app.src b/src/couch_dbupdates/src/couch_dbupdates.app.src new file mode 100644 index 0000000..c420283 --- /dev/null +++ b/src/couch_dbupdates/src/couch_dbupdates.app.src @@ -0,0 +1,11 @@ +{application, couch_dbupdates, + [ + {description, ""}, + {vsn, "@version@"}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {env, []} + ]}. http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/src/couch_dbupdates/src/couch_dbupdates.erl ---------------------------------------------------------------------- diff --git a/src/couch_dbupdates/src/couch_dbupdates.erl b/src/couch_dbupdates/src/couch_dbupdates.erl new file mode 100644 index 0000000..e37362f --- /dev/null +++ b/src/couch_dbupdates/src/couch_dbupdates.erl @@ -0,0 +1,46 @@ +-module(couch_dbupdates). + +-export([handle_dbupdates/3]). + + +handle_dbupdates(Fun, Acc, Options) -> + NotifierPid = db_update_notifier(), + try + loop(Fun, Acc, Options) + after + couch_db_update_notifier:stop(NotifierPid) + end. + + +loop(Fun, Acc, Options) -> + [{timeout, Timeout}, {heartbeat, Heartbeat}] = Options, + receive + {db_updated, Event} -> + case Fun(Event, Acc) of + {ok, Acc1} -> + loop(Fun, Acc1, Options); + stop -> + Fun(stop, Acc) + + end + after Timeout -> + case Heartbeat of + true -> + case Fun(heartbeat, Acc) of + {ok, Acc1} -> + loop(Fun, Acc1, Options); + stop -> + Fun(stop, Acc) + + end; + _ -> + Fun(stop, Acc) + end + end. + +db_update_notifier() -> + Self = self(), + {ok, Notifier} = couch_db_update_notifier:start_link(fun(Event) -> + Self ! {db_updated, Event} + end), + Notifier. http://git-wip-us.apache.org/repos/asf/couchdb/blob/15c31508/src/couch_dbupdates/src/couch_dbupdates_httpd.erl ---------------------------------------------------------------------- diff --git a/src/couch_dbupdates/src/couch_dbupdates_httpd.erl b/src/couch_dbupdates/src/couch_dbupdates_httpd.erl new file mode 100644 index 0000000..96058d3 --- /dev/null +++ b/src/couch_dbupdates/src/couch_dbupdates_httpd.erl @@ -0,0 +1,69 @@ +-module(couch_dbupdates_httpd). + +-export([handle_req/1]). + +-include_lib("couch_db.hrl"). + +-record(state, {resp, feed}). + +handle_req(#httpd{method='GET'}=Req) -> + ok = couch_httpd:verify_is_server_admin(Req), + Qs = couch_httpd:qs(Req), + Feed = proplists:get_value("feed", Qs, "longpoll"), + + Timeout = list_to_integer( + proplists:get_value("timeout", Qs, "60000") + ), + + Heartbeat0 = proplists:get_value("heartbeat", Qs), + Heartbeat = case {Feed, Heartbeat0} of + {"longpoll", _} -> false; + {_, "false"} -> false; + _ -> true + end, + + Options = [{timeout, Timeout}, {heartbeat, Heartbeat}], + + {ok, Resp} = case Feed of + "eventsource" -> + Headers = [ + {"Content-Type", "text/event-stream"}, + {"Cache-Control", "no-cache"} + ], + couch_httpd:start_json_response(Req, 200, Headers); + _ -> + couch_httpd:start_json_response(Req, 200) + end, + + State = #state{resp=Resp, feed=Feed}, + couch_dbupdates:handle_dbupdates(fun handle_update/2, + State, Options). + +handle_req(Req, _Db) -> + couch_httpd:send_method_not_allowed(Req, "GET"). + +handle_update(stop, #state{resp=Resp}) -> + couch_httpd:end_json_response(Resp); +handle_update(heartbeat, #state{resp=Resp}=State) -> + {ok, Resp1} = couch_httpd:send_chunk(Resp, "\n"), + {ok, State#state{resp=Resp1}}; +handle_update(Event, #state{resp=Resp, feed="eventsource"}=State) -> + EventObj = event_obj(Event), + {ok, Resp1} = couch_httpd:send_chunk(Resp, ["data: ", + ?JSON_ENCODE(EventObj), + "\n\n"]), + {ok, State#state{resp=Resp1}}; +handle_update(Event, #state{resp=Resp, feed="continuous"}=State) -> + EventObj = event_obj(Event), + {ok, Resp1} = couch_httpd:send_chunk(Resp, [?JSON_ENCODE(EventObj) | + "\n"]), + {ok, State#state{resp=Resp1}}; +handle_update(Event, #state{resp=Resp, feed="longpoll"}) -> + {Props} = event_obj(Event), + JsonObj = {[{<<"ok">>, true} | Props]}, + couch_httpd:send_chunk(Resp, ?JSON_ENCODE(JsonObj)), + stop. + +event_obj({Type, DbName}) -> + {[{<<"type">>, couch_util:to_binary(Type)}, + {<<"db_name">>, couch_util:to_binary(DbName)}]}.
