diff --git a/CHANGELOG.md b/CHANGELOG.md index 0791c2ea..c6f8761b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Finally add a CHANGELOG.md to communicate breaking changes (#598) - Test multiple versions of InfluxDB in travis ### Changed +- Update POST/GET requests to follow verb guidelines from InfluxDB documentation - Update test suite to support InfluxDB v1.3.9, v1.4.2, and v1.5.4 - Fix performance degradation when removing NaN values via line protocol (#592) ### Removed diff --git a/examples/tutorial_sine_wave.py b/examples/tutorial_sine_wave.py index 99b3d388..5dfebf3c 100644 --- a/examples/tutorial_sine_wave.py +++ b/examples/tutorial_sine_wave.py @@ -43,7 +43,7 @@ def main(host='localhost', port=8086): time.sleep(3) query = 'SELECT * FROM foobar' - print("Queying data: " + query) + print("Querying data: " + query) result = client.query(query, database=DBNAME) print("Result: {0}".format(result)) diff --git a/influxdb/client.py b/influxdb/client.py index 62d5a025..e3299fe8 100644 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -350,7 +350,8 @@ def query(self, database=None, raise_errors=True, chunked=False, - chunk_size=0): + chunk_size=0, + method="GET"): """Send a query to InfluxDB. :param query: the actual query string @@ -384,6 +385,9 @@ def query(self, :param chunk_size: Size of each chunk to tell InfluxDB to use. :type chunk_size: int + :param method: the HTTP method for the request, defaults to GET + :type method: str + :returns: the queried data :rtype: :class:`~.ResultSet` """ @@ -401,9 +405,12 @@ def query(self, if chunk_size > 0: params['chunk_size'] = chunk_size + if query.lower().startswith("select ") and " into " in query.lower(): + method = "POST" + response = self.request( url="query", - method='GET', + method=method, params=params, data=None, expected_response_code=expected_response_code @@ -568,7 +575,8 @@ def create_database(self, dbname): :param dbname: the name of the database to create :type dbname: str """ - self.query("CREATE DATABASE {0}".format(quote_ident(dbname))) + self.query("CREATE DATABASE {0}".format(quote_ident(dbname)), + method="POST") def drop_database(self, dbname): """Drop a database from InfluxDB. @@ -576,7 +584,8 @@ def drop_database(self, dbname): :param dbname: the name of the database to drop :type dbname: str """ - self.query("DROP DATABASE {0}".format(quote_ident(dbname))) + self.query("DROP DATABASE {0}".format(quote_ident(dbname)), + method="POST") def get_list_measurements(self): """Get the list of measurements in InfluxDB. @@ -602,7 +611,8 @@ def drop_measurement(self, measurement): :param measurement: the name of the measurement to drop :type measurement: str """ - self.query("DROP MEASUREMENT {0}".format(quote_ident(measurement))) + self.query("DROP MEASUREMENT {0}".format(quote_ident(measurement)), + method="POST") def create_retention_policy(self, name, duration, replication, database=None, default=False): @@ -634,11 +644,11 @@ def create_retention_policy(self, name, duration, replication, if default is True: query_string += " DEFAULT" - self.query(query_string) + self.query(query_string, method="POST") def alter_retention_policy(self, name, database=None, duration=None, replication=None, default=None): - """Mofidy an existing retention policy for a database. + """Modify an existing retention policy for a database. :param name: the name of the retention policy to modify :type name: str @@ -671,7 +681,7 @@ def alter_retention_policy(self, name, database=None, if default is True: query_string += " DEFAULT" - self.query(query_string) + self.query(query_string, method="POST") def drop_retention_policy(self, name, database=None): """Drop an existing retention policy for a database. @@ -685,7 +695,7 @@ def drop_retention_policy(self, name, database=None): query_string = ( "DROP RETENTION POLICY {0} ON {1}" ).format(quote_ident(name), quote_ident(database or self._database)) - self.query(query_string) + self.query(query_string, method="POST") def get_list_retention_policies(self, database=None): """Get the list of retention policies for a database. @@ -751,7 +761,7 @@ def create_user(self, username, password, admin=False): quote_ident(username), quote_literal(password)) if admin: text += ' WITH ALL PRIVILEGES' - self.query(text) + self.query(text, method="POST") def drop_user(self, username): """Drop a user from InfluxDB. @@ -759,8 +769,8 @@ def drop_user(self, username): :param username: the username to drop :type username: str """ - text = "DROP USER {0}".format(quote_ident(username)) - self.query(text) + text = "DROP USER {0}".format(quote_ident(username), method="POST") + self.query(text, method="POST") def set_user_password(self, username, password): """Change the password of an existing user. @@ -796,7 +806,7 @@ def delete_series(self, database=None, measurement=None, tags=None): tag_eq_list = ["{0}={1}".format(quote_ident(k), quote_literal(v)) for k, v in tags.items()] query_str += ' WHERE ' + ' AND '.join(tag_eq_list) - self.query(query_str, database=database) + self.query(query_str, database=database, method="POST") def grant_admin_privileges(self, username): """Grant cluster administration privileges to a user. @@ -808,7 +818,7 @@ def grant_admin_privileges(self, username): and manage users. """ text = "GRANT ALL PRIVILEGES TO {0}".format(quote_ident(username)) - self.query(text) + self.query(text, method="POST") def revoke_admin_privileges(self, username): """Revoke cluster administration privileges from a user. @@ -820,7 +830,7 @@ def revoke_admin_privileges(self, username): and manage users. """ text = "REVOKE ALL PRIVILEGES FROM {0}".format(quote_ident(username)) - self.query(text) + self.query(text, method="POST") def grant_privilege(self, privilege, database, username): """Grant a privilege on a database to a user. @@ -836,7 +846,7 @@ def grant_privilege(self, privilege, database, username): text = "GRANT {0} ON {1} TO {2}".format(privilege, quote_ident(database), quote_ident(username)) - self.query(text) + self.query(text, method="POST") def revoke_privilege(self, privilege, database, username): """Revoke a privilege on a database from a user. @@ -852,7 +862,7 @@ def revoke_privilege(self, privilege, database, username): text = "REVOKE {0} ON {1} FROM {2}".format(privilege, quote_ident(database), quote_ident(username)) - self.query(text) + self.query(text, method="POST") def get_list_privileges(self, username): """Get the list of all privileges granted to given user. diff --git a/influxdb/tests/client_test.py b/influxdb/tests/client_test.py index efdfb770..859e8bc9 100644 --- a/influxdb/tests/client_test.py +++ b/influxdb/tests/client_test.py @@ -439,6 +439,29 @@ def test_query(self): [{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}] ) + def test_select_into_post(self): + """Test SELECT.*INTO is POSTed.""" + example_response = ( + '{"results": [{"series": [{"measurement": "sdfsdfsdf", ' + '"columns": ["time", "value"], "values": ' + '[["2009-11-10T23:00:00Z", 0.64]]}]}, {"series": ' + '[{"measurement": "cpu_load_short", "columns": ["time", "value"], ' + '"values": [["2009-11-10T23:00:00Z", 0.64]]}]}]}' + ) + + with requests_mock.Mocker() as m: + m.register_uri( + requests_mock.POST, + "http://localhost:8086/query", + text=example_response + ) + rs = self.cli.query('select * INTO newmeas from foo') + + self.assertListEqual( + list(rs[0].get_points()), + [{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}] + ) + @unittest.skip('Not implemented for 0.9') def test_query_chunked(self): """Test chunked query for TestInfluxDBClient object.""" @@ -495,7 +518,7 @@ def test_create_database(self): """Test create database for TestInfluxDBClient object.""" with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text='{"results":[{}]}' ) @@ -509,7 +532,7 @@ def test_create_numeric_named_database(self): """Test create db w/numeric name for TestInfluxDBClient object.""" with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text='{"results":[{}]}' ) @@ -529,7 +552,7 @@ def test_drop_database(self): """Test drop database for TestInfluxDBClient object.""" with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text='{"results":[{}]}' ) @@ -543,7 +566,7 @@ def test_drop_measurement(self): """Test drop measurement for TestInfluxDBClient object.""" with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text='{"results":[{}]}' ) @@ -557,7 +580,7 @@ def test_drop_numeric_named_database(self): """Test drop numeric db for TestInfluxDBClient object.""" with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text='{"results":[{}]}' ) @@ -615,7 +638,7 @@ def test_create_retention_policy_default(self): with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text=example_response ) @@ -635,7 +658,7 @@ def test_create_retention_policy(self): with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text=example_response ) @@ -655,7 +678,7 @@ def test_alter_retention_policy(self): with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text=example_response ) @@ -695,7 +718,7 @@ def test_drop_retention_policy(self): with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text=example_response ) @@ -879,7 +902,7 @@ def test_grant_admin_privileges(self): with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text=example_response ) @@ -903,7 +926,7 @@ def test_revoke_admin_privileges(self): with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text=example_response ) @@ -927,7 +950,7 @@ def test_grant_privilege(self): with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text=example_response ) @@ -951,7 +974,7 @@ def test_revoke_privilege(self): with requests_mock.Mocker() as m: m.register_uri( - requests_mock.GET, + requests_mock.POST, "http://localhost:8086/query", text=example_response ) diff --git a/influxdb/tests/server_tests/client_test_with_server.py b/influxdb/tests/server_tests/client_test_with_server.py index 701f72ac..d2370e63 100644 --- a/influxdb/tests/server_tests/client_test_with_server.py +++ b/influxdb/tests/server_tests/client_test_with_server.py @@ -211,7 +211,7 @@ def test_drop_user(self): self.assertEqual(users, []) def test_drop_user_nonexisting(self): - """Test dropping a nonexistant user.""" + """Test dropping a nonexistent user.""" with self.assertRaises(InfluxDBClientError) as ctx: self.cli.drop_user('test') self.assertIn('user not found', @@ -383,6 +383,24 @@ def test_write_multiple_points_different_series(self): ]] ) + def test_select_into_as_post(self): + """Test SELECT INTO is POSTed.""" + self.assertIs(True, self.cli.write_points(dummy_points)) + time.sleep(1) + rsp = self.cli.query('SELECT * INTO "newmeas" FROM "memory"') + rsp = self.cli.query('SELECT * FROM "newmeas"') + lrsp = list(rsp) + + self.assertEqual( + lrsp, + [[ + {'value': 33, + 'time': '2009-11-10T23:01:35Z', + "host": "server01", + "region": "us-west"} + ]] + ) + @unittest.skip("Broken as of 0.9.0") def test_write_multiple_points_different_series_DF(self): """Test write multiple points using dataframe to different series."""