diff --git a/Changelog.rst b/Changelog.rst index 202118d6..73da31cd 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -10,6 +10,7 @@ Changelog `retry_on_timeout=True`) * Implemented url parsing according to RFC-1738 * added support for proper SSL certificate handling + * Required parameters are now checked for non-empty values 1.2.0 (2014-08-03) ------------------ diff --git a/elasticsearch/client/__init__.py b/elasticsearch/client/__init__.py index 9554de44..c721d0b9 100644 --- a/elasticsearch/client/__init__.py +++ b/elasticsearch/client/__init__.py @@ -10,7 +10,7 @@ from .cluster import ClusterClient from .cat import CatClient from .nodes import NodesClient from .snapshot import SnapshotClient -from .utils import query_params, _make_path +from .utils import query_params, _make_path, SKIP_IN_PATH logger = logging.getLogger('elasticsearch') @@ -249,6 +249,9 @@ class Elasticsearch(object): :arg version: Explicit version number for concurrency control :arg version_type: Specific version type """ + for param in (index, doc_type, body): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('PUT' if id else 'POST', _make_path(index, doc_type, id), params=params, body=body) return data @@ -272,6 +275,9 @@ class Elasticsearch(object): performing the operation :arg routing: Specific routing value """ + for param in (index, doc_type, id): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") try: self.transport.perform_request('HEAD', _make_path(index, doc_type, id), params=params) except NotFoundError: @@ -308,6 +314,9 @@ class Elasticsearch(object): :arg version_type: Explicit version number for concurrency control """ + for param in (index, doc_type, id): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('GET', _make_path(index, doc_type, id), params=params) return data @@ -339,6 +348,9 @@ class Elasticsearch(object): :arg version: Explicit version number for concurrency control :arg version_type: Explicit version number for concurrency control """ + for param in (index, doc_type, id): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('GET', _make_path(index, doc_type, id, '_source'), params=params) return data @@ -369,6 +381,8 @@ class Elasticsearch(object): performing the operation :arg routing: Specific routing value """ + if body in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'body'.") _, data = self.transport.perform_request('GET', _make_path(index, doc_type, '_mget'), params=params, body=body) return data @@ -401,6 +415,9 @@ class Elasticsearch(object): :arg version: Explicit version number for concurrency control :arg version_type: Explicit version number for concurrency control """ + for param in (index, doc_type, id): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('POST', _make_path(index, doc_type, id, '_update'), params=params, body=body) return data @@ -578,6 +595,9 @@ class Elasticsearch(object): :arg source: The URL-encoded query definition (instead of using the request body) """ + for param in (index, doc_type, id): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('GET', _make_path(index, doc_type, id, '_explain'), params=params, body=body) return data @@ -631,6 +651,9 @@ class Elasticsearch(object): :arg version: Explicit version number for concurrency control :arg version_type: Specific version type """ + for param in (index, doc_type, id): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('DELETE', _make_path(index, doc_type, id), params=params) return data @@ -682,6 +705,8 @@ class Elasticsearch(object): :arg replication: Explicitly set the replication type (default: sync) :arg timeout: Explicit operation timeout """ + if body in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'body'.") _, data = self.transport.perform_request('POST', _make_path(index, doc_type, '_bulk'), params=params, body=self._bulk_body(body)) return data @@ -699,6 +724,8 @@ class Elasticsearch(object): :arg doc_type: A comma-separated list of document types to use as default :arg search_type: Search operation type """ + if body in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'body'.") _, data = self.transport.perform_request('GET', _make_path(index, doc_type, '_msearch'), params=params, body=self._bulk_body(body)) return data @@ -736,6 +763,8 @@ class Elasticsearch(object): request body) :arg timeout: Explicit operation timeout """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'index'.") _, data = self.transport.perform_request('DELETE', _make_path(index, doc_type, '_query'), params=params, body=body) return data @@ -763,6 +792,8 @@ class Elasticsearch(object): :arg routing: Specific routing value :arg source: The URL-encoded request definition (instead of using request body) """ + if body in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'body'.") _, data = self.transport.perform_request('POST', _make_path(index, '_suggest'), params=params, body=body) return data @@ -803,6 +834,9 @@ class Elasticsearch(object): :arg version: Explicit version number for concurrency control :arg version_type: Specific version type """ + for param in (index, doc_type): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('GET', _make_path(index, doc_type, id, '_percolate'), params=params, body=body) return data @@ -829,6 +863,8 @@ class Elasticsearch(object): :arg ignore_unavailable: Whether specified concrete indices should be ignored when unavailable (missing or closed) """ + if body in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'body'.") _, data = self.transport.perform_request('GET', _make_path(index, doc_type, '_mpercolate'), params=params, body=self._bulk_body(body)) return data @@ -868,6 +904,9 @@ class Elasticsearch(object): :arg version: Explicit version number for concurrency control :arg version_type: Specific version type """ + for param in (index, doc_type): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('GET', _make_path(index, doc_type, id, '_percolate', 'count'), params=params, body=body) return data @@ -915,6 +954,9 @@ class Elasticsearch(object): against (default: the same type as the document) :arg stop_words: A list of stop words to be ignored """ + for param in (index, doc_type, id): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('GET', _make_path(index, doc_type, id, '_mlt'), params=params, body=body) return data @@ -953,6 +995,9 @@ class Elasticsearch(object): :arg term_statistics: Specifies if total term frequency and document frequency should be returned., default False """ + for param in (index, doc_type, id): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('GET', _make_path(index, doc_type, id, '_termvectors'), params=params, body=body) return data @@ -960,6 +1005,9 @@ class Elasticsearch(object): @query_params('field_statistics', 'fields', 'offsets', 'parent', 'payloads', 'positions', 'preference', 'realtime', 'routing', 'term_statistics') def termvector(self, index, doc_type, id, body=None, params=None): + for param in (index, doc_type, id): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('GET', _make_path(index, doc_type, id, '_termvector'), params=params, body=body) return data @@ -1069,6 +1117,9 @@ class Elasticsearch(object): :arg version: Explicit version number for concurrency control :arg version_type: Specific version type """ + for param in (lang, id, body): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('PUT', _make_path('_scripts', lang, id), params=params, body=body) return data @@ -1084,6 +1135,9 @@ class Elasticsearch(object): :arg version: Explicit version number for concurrency control :arg version_type: Specific version type """ + for param in (lang, id): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('GET', _make_path('_scripts', lang, id), params=params) return data @@ -1099,6 +1153,9 @@ class Elasticsearch(object): :arg version: Explicit version number for concurrency control :arg version_type: Specific version type """ + for param in (lang, id): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('DELETE', _make_path('_scripts', lang, id), params=params) return data @@ -1112,6 +1169,9 @@ class Elasticsearch(object): :arg id: Template ID :arg body: The document """ + for param in (id, body): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('PUT', _make_path('_search', 'template', id), params=params, body=body) return data @@ -1125,6 +1185,8 @@ class Elasticsearch(object): :arg id: Template ID :arg body: The document """ + if id in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'id'.") _, data = self.transport.perform_request('GET', _make_path('_search', 'template', id), params=params, body=body) return data diff --git a/elasticsearch/client/indices.py b/elasticsearch/client/indices.py index 5c6209f4..c5d1261b 100644 --- a/elasticsearch/client/indices.py +++ b/elasticsearch/client/indices.py @@ -1,4 +1,4 @@ -from .utils import NamespacedClient, query_params, _make_path +from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH from ..exceptions import NotFoundError class IndicesClient(NamespacedClient): @@ -92,6 +92,8 @@ class IndicesClient(NamespacedClient): :arg master_timeout: Specify timeout for connection to master :arg timeout: Explicit operation timeout """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'index'.") _, data = self.transport.perform_request('PUT', _make_path(index), params=params, body=body) return data @@ -112,6 +114,8 @@ class IndicesClient(NamespacedClient): :arg local: Return local information, do not retrieve the state from master node (default: false) """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'index'.") _, data = self.transport.perform_request('GET', _make_path(index, feature), params=params) return data @@ -134,6 +138,8 @@ class IndicesClient(NamespacedClient): :arg ignore_unavailable: Whether specified concrete indices should be ignored when unavailable (missing or closed) """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'index'.") _, data = self.transport.perform_request('POST', _make_path(index, '_open'), params=params) return data @@ -158,6 +164,8 @@ class IndicesClient(NamespacedClient): :arg master_timeout: Specify timeout for connection to master :arg timeout: Explicit operation timeout """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'index'.") _, data = self.transport.perform_request('POST', _make_path(index, '_close'), params=params) return data @@ -173,6 +181,8 @@ class IndicesClient(NamespacedClient): :arg master_timeout: Specify timeout for connection to master :arg timeout: Explicit operation timeout """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'index'.") _, data = self.transport.perform_request('DELETE', _make_path(index), params=params) return data @@ -195,6 +205,8 @@ class IndicesClient(NamespacedClient): :arg local: Return local information, do not retrieve the state from master node (default: false) """ + if index in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'index'.") try: self.transport.perform_request('HEAD', _make_path(index), params=params) except NotFoundError: @@ -223,6 +235,9 @@ class IndicesClient(NamespacedClient): :arg local: Return local information, do not retrieve the state from master node (default: false) """ + for param in (index, doc_type): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") try: self.transport.perform_request('HEAD', _make_path(index, doc_type), params=params) except NotFoundError: @@ -253,6 +268,9 @@ class IndicesClient(NamespacedClient): :arg master_timeout: Specify timeout for connection to master :arg timeout: Explicit operation timeout """ + for param in (doc_type, body): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('PUT', _make_path(index, '_mapping', doc_type), params=params, body=body) return data @@ -303,6 +321,8 @@ class IndicesClient(NamespacedClient): :arg local: Return local information, do not retrieve the state from master node (default: false) """ + if field in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'field'.") _, data = self.transport.perform_request('GET', _make_path(index, '_mapping', doc_type, 'field', field), params=params) return data @@ -320,6 +340,9 @@ class IndicesClient(NamespacedClient): specified indices. :arg master_timeout: Specify timeout for connection to master """ + for param in (index, doc_type): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('DELETE', _make_path(index, '_mapping', doc_type), params=params) return data @@ -338,6 +361,8 @@ class IndicesClient(NamespacedClient): :arg master_timeout: Specify timeout for connection to master :arg timeout: Explicit timestamp for the document """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'name'.") _, data = self.transport.perform_request('PUT', _make_path(index, '_alias', name), params=params, body=body) return data @@ -420,6 +445,8 @@ class IndicesClient(NamespacedClient): :arg master_timeout: Specify timeout for connection to master :arg timeout: Request timeout """ + if body in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'body'.") _, data = self.transport.perform_request('POST', '/_aliases', params=params, body=body) return data @@ -437,6 +464,9 @@ class IndicesClient(NamespacedClient): :arg master_timeout: Specify timeout for connection to master :arg timeout: Explicit timestamp for the document """ + for param in (index, name): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('DELETE', _make_path(index, '_alias', name), params=params) return data @@ -458,6 +488,9 @@ class IndicesClient(NamespacedClient): :arg timeout: Explicit operation timeout :arg flat_settings: Return settings in flat format (default: false) """ + for param in (name, body): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('PUT', _make_path('_template', name), params=params, body=body) return data @@ -472,6 +505,8 @@ class IndicesClient(NamespacedClient): :arg local: Return local information, do not retrieve the state from master node (default: false) """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'name'.") try: self.transport.perform_request('HEAD', _make_path('_template', name), params=params) @@ -504,6 +539,8 @@ class IndicesClient(NamespacedClient): :arg master_timeout: Specify timeout for connection to master :arg timeout: Explicit operation timeout """ + if name in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'name'.") _, data = self.transport.perform_request('DELETE', _make_path('_template', name), params=params) return data @@ -552,6 +589,8 @@ class IndicesClient(NamespacedClient): ignored when unavailable (missing or closed) :arg master_timeout: Specify timeout for connection to master """ + if body in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'body'.") _, data = self.transport.perform_request('PUT', _make_path(index, '_settings'), params=params, body=body) return data @@ -583,6 +622,9 @@ class IndicesClient(NamespacedClient): to warm :arg master_timeout: Specify timeout for connection to master """ + for param in (name, body): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") if doc_type and not index: index = '_all' _, data = self.transport.perform_request('PUT', _make_path(index, doc_type, '_warmer', name), @@ -627,6 +669,9 @@ class IndicesClient(NamespacedClient): wildcards); use `_all` to delete all warmers in the specified indices. :arg master_timeout: Specify timeout for connection to master """ + for param in (index, name): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('DELETE', _make_path(index, '_warmer', name), params=params) return data diff --git a/elasticsearch/client/snapshot.py b/elasticsearch/client/snapshot.py index b53f1629..7c2b8155 100644 --- a/elasticsearch/client/snapshot.py +++ b/elasticsearch/client/snapshot.py @@ -1,4 +1,4 @@ -from .utils import NamespacedClient, query_params, _make_path +from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH class SnapshotClient(NamespacedClient): @query_params('master_timeout', 'wait_for_completion') @@ -15,6 +15,9 @@ class SnapshotClient(NamespacedClient): :arg wait_for_completion: Should this request wait until the operation has completed before returning, default False """ + for param in (repository, snapshot): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('PUT', _make_path('_snapshot', repository, snapshot), params=params, body=body) return data @@ -30,6 +33,9 @@ class SnapshotClient(NamespacedClient): :arg master_timeout: Explicit operation timeout for connection to master node """ + for param in (repository, snapshot): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('DELETE', _make_path('_snapshot', repository, snapshot), params=params) return data @@ -45,6 +51,9 @@ class SnapshotClient(NamespacedClient): :arg master_timeout: Explicit operation timeout for connection to master node """ + for param in (repository, snapshot): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('GET', _make_path('_snapshot', repository, snapshot), params=params) return data @@ -60,6 +69,8 @@ class SnapshotClient(NamespacedClient): node :arg timeout: Explicit operation timeout """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'repository'.") _, data = self.transport.perform_request('DELETE', _make_path('_snapshot', repository), params=params) return data @@ -92,6 +103,9 @@ class SnapshotClient(NamespacedClient): node :arg timeout: Explicit operation timeout """ + for param in (repository, body): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('PUT', _make_path('_snapshot', repository), params=params, body=body) return data @@ -110,6 +124,9 @@ class SnapshotClient(NamespacedClient): :arg wait_for_completion: Should this request wait until the operation has completed before returning, default False """ + for param in (repository, snapshot): + if param in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument.") _, data = self.transport.perform_request('POST', _make_path('_snapshot', repository, snapshot, '_restore'), params=params, body=body) return data @@ -143,6 +160,8 @@ class SnapshotClient(NamespacedClient): node :arg timeout: Explicit operation timeout """ + if repository in SKIP_IN_PATH: + raise ValueError("Empty value passed for a required argument 'repository'.") _, data = self.transport.perform_request('POST', _make_path('_snapshot', repository, '_verify'), params=params) return data diff --git a/elasticsearch/client/utils.py b/elasticsearch/client/utils.py index 54a73e2c..360208aa 100644 --- a/elasticsearch/client/utils.py +++ b/elasticsearch/client/utils.py @@ -5,7 +5,7 @@ from functools import wraps from ..compat import string_types, quote_plus # parts of URL to be omitted -SKIP_IN_PATH = (None, b'', [], ()) +SKIP_IN_PATH = (None, '', b'', [], ()) def _escape(value): """ diff --git a/test_elasticsearch/test_client/test_indices.py b/test_elasticsearch/test_client/test_indices.py index 9ac93f35..fbb8c23c 100644 --- a/test_elasticsearch/test_client/test_indices.py +++ b/test_elasticsearch/test_client/test_indices.py @@ -12,3 +12,8 @@ class TestIndices(ElasticsearchTestCase): def test_exists_index(self): self.client.indices.exists('second.index,third/index') self.assert_url_called('HEAD', '/second.index,third%2Findex') + + def test_passing_empty_value_for_required_param_raises_exception(self): + self.assertRaises(ValueError, self.client.indices.exists, index=None) + self.assertRaises(ValueError, self.client.indices.exists, index=[]) + self.assertRaises(ValueError, self.client.indices.exists, index='')