summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/docs/src/api/database/find.rst77
-rw-r--r--src/mango/src/mango_httpd.erl44
-rw-r--r--src/mango/test/01-index-crud-test.py51
3 files changed, 151 insertions, 21 deletions
diff --git a/src/docs/src/api/database/find.rst b/src/docs/src/api/database/find.rst
index ede5598c9..aed695543 100644
--- a/src/docs/src/api/database/find.rst
+++ b/src/docs/src/api/database/find.rst
@@ -1261,7 +1261,8 @@ it easier to take advantage of future improvements to query planning
:synopsis: Delete an index.
:param db: Database name.
- :param design_doc: Design document name.
+ :param design_doc: Design document name. The ``_design/`` prefix
+ is not required.
:param name: Index name.
:>header Content-Type: - :mimetype:`application/json`
@@ -1297,6 +1298,80 @@ it easier to take advantage of future improvements to query planning
"ok": true
}
+.. _api/db/find/index-bulk-delete:
+
+.. http:post:: /{db}/_index/_bulk_delete
+ :synopsis: Delete indexes in bulk.
+
+ :param db: Database name
+
+ :<header Content-Type: - :mimetype:`application/json`
+
+ :<json array docids: List of names for indexes to be deleted.
+ :<json number w: Write quorum for each of the deletions. Default
+ is ``2``. *Optional*
+
+ :>header Content-Type: - :mimetype:`application/json`
+
+ :>json array success: An array of objects that represent successful
+ deletions per index. The ``id`` key contains the name of the
+ index, and ``ok`` reports if the operation has completed
+ :>json array fail: An array of object that describe failed
+ deletions per index. The ``id`` key names the corresponding
+ index, and ``error`` describes the reason for the failure
+
+ :code 200: Success
+ :code 400: Invalid request
+ :code 404: Requested database not found
+ :code 500: Execution error
+
+ **Request**:
+
+ .. code-block:: http
+
+ POST /db/_index/_bulk_delete HTTP/1.1
+ Accept: application/json
+ Content-Type: application/json
+ Host: localhost:5984
+
+ {
+ "docids": [
+ "_design/example-ddoc",
+ "foo-index",
+ "nonexistent-index"
+ ]
+ }
+
+ **Response**:
+
+ .. code-block:: http
+
+ HTTP/1.1 200 OK
+ Cache-Control: must-revalidate
+ Content-Length: 94
+ Content-Type: application/json
+ Date: Thu, 01 Sep 2016 19:26:59 GMT
+ Server: CouchDB (Erlang OTP/18)
+
+ {
+ "success": [
+ {
+ "id": "_design/example-ddoc",
+ "ok": true
+ },
+ {
+ "id": "foo-index",
+ "ok": true
+ }
+ ],
+ "fail": [
+ {
+ "id": "nonexistent-index",
+ "error": "not_found"
+ }
+ ]
+ }
+
.. _api/db/find/explain:
==================
diff --git a/src/mango/src/mango_httpd.erl b/src/mango/src/mango_httpd.erl
index 3e58288da..ed797c517 100644
--- a/src/mango/src/mango_httpd.erl
+++ b/src/mango/src/mango_httpd.erl
@@ -157,7 +157,6 @@ handle_index_req(
chttpd:send_method_not_allowed(Req, "POST");
handle_index_req(
#httpd{
- method = 'DELETE',
path_parts = [A, B, <<"_design">>, DDocId0, Type, Name]
} = Req,
Db
@@ -166,30 +165,35 @@ handle_index_req(
handle_index_req(Req#httpd{path_parts = PathParts}, Db);
handle_index_req(
#httpd{
- method = 'DELETE',
+ method = Method,
path_parts = [_, _, DDocId0, Type, Name]
} = Req,
Db
) ->
- Idxs = mango_idx:list(Db),
- DDocId = convert_to_design_id(DDocId0),
- DelOpts = get_idx_del_opts(Req),
- Filt = fun(Idx) ->
- IsDDoc = mango_idx:ddoc(Idx) == DDocId,
- IsType = mango_idx:type(Idx) == Type,
- IsName = mango_idx:name(Idx) == Name,
- IsDDoc andalso IsType andalso IsName
- end,
- case mango_idx:delete(Filt, Db, Idxs, DelOpts) of
- {ok, true} ->
- chttpd:send_json(Req, {[{ok, true}]});
- {error, not_found} ->
- throw({not_found, missing});
- {error, Error} ->
- ?MANGO_ERROR({error_saving_ddoc, Error})
+ case Method of
+ 'DELETE' ->
+ Idxs = mango_idx:list(Db),
+ DDocId = convert_to_design_id(DDocId0),
+ DelOpts = get_idx_del_opts(Req),
+ Filt = fun(Idx) ->
+ IsDDoc = mango_idx:ddoc(Idx) == DDocId,
+ IsType = mango_idx:type(Idx) == Type,
+ IsName = mango_idx:name(Idx) == Name,
+ IsDDoc andalso IsType andalso IsName
+ end,
+ case mango_idx:delete(Filt, Db, Idxs, DelOpts) of
+ {ok, true} ->
+ chttpd:send_json(Req, {[{ok, true}]});
+ {error, not_found} ->
+ throw({not_found, missing});
+ {error, Error} ->
+ ?MANGO_ERROR({error_saving_ddoc, Error})
+ end;
+ _ ->
+ chttpd:send_method_not_allowed(Req, "DELETE")
end;
-handle_index_req(#httpd{path_parts = [_, _, _DDocId0, _Type, _Name]} = Req, _Db) ->
- chttpd:send_method_not_allowed(Req, "DELETE").
+handle_index_req(Req, _Db) ->
+ chttpd:send_error(Req, not_found).
handle_explain_req(#httpd{method = 'POST'} = Req, Db) ->
chttpd:validate_ctype(Req, "application/json"),
diff --git a/src/mango/test/01-index-crud-test.py b/src/mango/test/01-index-crud-test.py
index dd70e7eea..24409b49a 100644
--- a/src/mango/test/01-index-crud-test.py
+++ b/src/mango/test/01-index-crud-test.py
@@ -11,6 +11,7 @@
# the License.
import random
+from functools import partial
import mango
import copy
@@ -88,6 +89,56 @@ class IndexCrudTests(mango.DbPerClass):
else:
raise AssertionError("bad create index")
+ def test_bad_urls(self):
+ # These are only the negative test cases because ideally the
+ # positive ones are implicitly tested by other ones.
+
+ all_methods = [
+ ("PUT", self.db.sess.put),
+ ("GET", self.db.sess.get),
+ ("POST", self.db.sess.post),
+ ("PATCH", self.db.sess.get),
+ ("DELETE", self.db.sess.delete),
+ ("HEAD", self.db.sess.head),
+ ("COPY", partial(self.db.sess.request, "COPY")),
+ ("OPTIONS", partial(self.db.sess.request, "OPTIONS")),
+ ("TRACE", partial(self.db.sess.request, "TRACE")),
+ ("CONNECT", partial(self.db.sess.request, "CONNECT")),
+ ]
+
+ def all_but(method):
+ return list(filter(lambda x: x[0] != method, all_methods))
+
+ # Three-element subpaths are used as a shorthand to delete
+ # indexes via design documents, see below.
+ for subpath in ["a", "a/b", "a/b/c/d", "a/b/c/d/e", "a/b/c/d/e/f"]:
+ path = self.db.path("_index/{}".format(subpath))
+ for method_name, method in all_methods:
+ with self.subTest(path=path, method=method_name):
+ r = method(path)
+ self.assertEqual(r.status_code, 404)
+
+ for method_name, method in all_but("POST"):
+ path = self.db.path("_index/_bulk_delete")
+ with self.subTest(path=path, method=method_name):
+ r = method(path)
+ self.assertEqual(r.status_code, 405)
+
+ fields = ["foo", "bar"]
+ ddoc = "dd"
+ idx = "idx_01"
+ ret = self.db.create_index(fields, name=idx, ddoc=ddoc)
+ assert ret is True
+ for subpath in [
+ "{}/json/{}".format(ddoc, idx),
+ "_design/{}/json/{}".format(ddoc, idx),
+ ]:
+ path = self.db.path("_index/{}".format(subpath))
+ for method_name, method in all_but("DELETE"):
+ r = method(path)
+ with self.subTest(path=path, method=method_name):
+ self.assertEqual(r.status_code, 405)
+
def test_create_idx_01(self):
fields = ["foo", "bar"]
ret = self.db.create_index(fields, name="idx_01")