From 6c77ee53f61e8552eef3200174c0ad0434b91614 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Wed, 12 Nov 2025 18:24:17 +0700 Subject: [PATCH 01/16] feat: handle URL path prefix in InfluxDB --- influxdb_client_3/__init__.py | 6 +++++- tests/test_influxdb_client_3.py | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/influxdb_client_3/__init__.py b/influxdb_client_3/__init__.py index bbfa89b..645b03d 100644 --- a/influxdb_client_3/__init__.py +++ b/influxdb_client_3/__init__.py @@ -266,12 +266,16 @@ def __init__( hostname = parsed_url.hostname if parsed_url.hostname else host port = parsed_url.port if parsed_url.port else 443 + path = "" + if parsed_url.scheme is not None and parsed_url.hostname is not None and parsed_url.port is not None: + path = parsed_url.path + # Construct the clients using the parsed values if write_port_overwrite is not None: port = write_port_overwrite self._client = _InfluxDBClient( - url=f"{scheme}://{hostname}:{port}", + url=f"{scheme}://{hostname}:{port}{path}", token=self._token, org=self._org, timeout=write_timeout, diff --git a/tests/test_influxdb_client_3.py b/tests/test_influxdb_client_3.py index 7bab679..ca01585 100644 --- a/tests/test_influxdb_client_3.py +++ b/tests/test_influxdb_client_3.py @@ -352,6 +352,33 @@ def test_get_version_fail(self): host=f'http://{server.host}:{server.port}', org="ORG", database="DB", token="TOKEN" ).get_server_version() + def test_url_with_path_prefix(self): + server = self.http_server + server.expect_request('/prefix/ping').respond_with_json( + response_json={"version": "3.0"}, + ) + version = InfluxDBClient3( + host=f'http://{server.host}:{server.port}/prefix', + org="ORG", + database="DB", + token="TOKEN" + ).get_server_version() + assert version == "3.0" + + def test_url_lack_path_prefix(self): + server = self.http_server + server.expect_request('/prefix/ping').respond_with_json( + response_json={"version": "3.0"}, + ) + try: + InfluxDBClient3( + host=f'http://{server.host}:{server.port}', + org="ORG", + database="DB", + token="TOKEN" + ).get_server_version() + except ApiException: + self.assertRaises(ApiException) if __name__ == '__main__': unittest.main() From 8542a99fee7b72693f2c264fc57e6bbb9717517f Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Wed, 12 Nov 2025 18:27:47 +0700 Subject: [PATCH 02/16] chore: update CHANGELOG with URL path prefix feature --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ad8a29..ff067fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ # 0.17.0 [unreleased] +### Features + +1. [#173](https://github.com/InfluxCommunity/influxdb3-python/pull/173): Supporting URL with path prefix. + ### CI 1. [#164](https://github.com/InfluxCommunity/influxdb3-python/pull/164): Fix pipelines not downloading the correct python images. From 4c2d5670b7730e8ebb0152e7b924a9e02e163aea Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 11:18:02 +0700 Subject: [PATCH 03/16] fix: adjust query timeout values in test cases --- tests/test_query.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_query.py b/tests/test_query.py index b951bdf..1e26220 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -445,7 +445,7 @@ async def test_query_async_timeout(self): token=token, flight_client_options={"generic_options": [('Foo', 'Bar')]}, proxy=None, - options=QueryApiOptionsBuilder().timeout(0.0001).build(), + options=QueryApiOptionsBuilder().timeout(0).build(), ) query = "SELECT * FROM data" await q_api.query_async(query, "sql", "", database) @@ -461,7 +461,7 @@ def test_query_timeout_per_call_override(self): token=token, flight_client_options={"generic_options": [('Foo', 'Bar')]}, proxy=None, - options=QueryApiOptionsBuilder().timeout(3.14).build(), + options=QueryApiOptionsBuilder().timeout(0).build(), ) query = "SELECT * FROM data" q_api.query(query, "sql", "", database, timeout=0.0001) From 2e592532df6efdba727adaf82ada292d34a43d42 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 11:34:06 +0700 Subject: [PATCH 04/16] fix: refine query timeout values in test cases --- tests/test_query.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_query.py b/tests/test_query.py index 1e26220..b951bdf 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -445,7 +445,7 @@ async def test_query_async_timeout(self): token=token, flight_client_options={"generic_options": [('Foo', 'Bar')]}, proxy=None, - options=QueryApiOptionsBuilder().timeout(0).build(), + options=QueryApiOptionsBuilder().timeout(0.0001).build(), ) query = "SELECT * FROM data" await q_api.query_async(query, "sql", "", database) @@ -461,7 +461,7 @@ def test_query_timeout_per_call_override(self): token=token, flight_client_options={"generic_options": [('Foo', 'Bar')]}, proxy=None, - options=QueryApiOptionsBuilder().timeout(0).build(), + options=QueryApiOptionsBuilder().timeout(3.14).build(), ) query = "SELECT * FROM data" q_api.query(query, "sql", "", database, timeout=0.0001) From 0fb669eef7aaae2d99499a85f5a87d817cd09a78 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 12:37:50 +0700 Subject: [PATCH 05/16] fix: use `ConstantFlightServerDelayed` in timeout tests and clean up formatting --- tests/test_query.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_query.py b/tests/test_query.py index b951bdf..d15c1db 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -138,7 +138,7 @@ def test_query_proxy_base_client(self): ) assert client._query_api._proxy == test_proxy - assert ('grpc.http_proxy', test_proxy) in\ + assert ('grpc.http_proxy', test_proxy) in \ client._query_api._flight_client_options.get('generic_options') def create_cert_file(self, file_name): @@ -154,9 +154,9 @@ def test_query_api_options_builder(self): cert_file = "cert_test.pem" self.create_cert_file(cert_file) builder = QueryApiOptionsBuilder() - options = builder.proxy(proxy_name)\ - .root_certs(cert_file)\ - .tls_verify(False)\ + options = builder.proxy(proxy_name) \ + .root_certs(cert_file) \ + .tls_verify(False) \ .build() try: @@ -175,7 +175,7 @@ def test_query_client_with_options(self): cert_chain = 'mTLS_explicit_chain' self.create_cert_file(cert_file) test_flight_client_options = {'private_key': private_key, 'cert_chain': cert_chain} - options = QueryApiOptionsBuilder()\ + options = QueryApiOptionsBuilder() \ .proxy(proxy_name) \ .root_certs(cert_file) \ .tls_verify(False) \ @@ -436,7 +436,7 @@ async def fibo(iters): @asyncio_run async def test_query_async_timeout(self): with pytest.raises(FlightTimedOutError): - with ConstantFlightServer() as server: + with ConstantFlightServerDelayed(delay=1) as server: connection_string = f"grpc://localhost:{server.port}" token = "my_token" database = "my_database" @@ -452,7 +452,7 @@ async def test_query_async_timeout(self): def test_query_timeout_per_call_override(self): with pytest.raises(FlightTimedOutError): - with ConstantFlightServer() as server: + with ConstantFlightServerDelayed(delay=1) as server: connection_string = f"grpc://localhost:{server.port}" token = "my_token" database = "my_database" From 6c7f642a54effe2c02245a119de60f8c46413748 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 12:39:44 +0700 Subject: [PATCH 06/16] fix: add missing newline in test_influxdb_client_3.py --- tests/test_influxdb_client_3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_influxdb_client_3.py b/tests/test_influxdb_client_3.py index ca01585..7b22a67 100644 --- a/tests/test_influxdb_client_3.py +++ b/tests/test_influxdb_client_3.py @@ -380,5 +380,6 @@ def test_url_lack_path_prefix(self): except ApiException: self.assertRaises(ApiException) + if __name__ == '__main__': unittest.main() From e7eca1a9666f8c5594562876fb2e85db5ece251c Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 13:17:37 +0700 Subject: [PATCH 07/16] fix: update URL path prefix in `test_url_with_path_prefix` test --- tests/test_influxdb_client_3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_influxdb_client_3.py b/tests/test_influxdb_client_3.py index 7b22a67..7f14292 100644 --- a/tests/test_influxdb_client_3.py +++ b/tests/test_influxdb_client_3.py @@ -354,11 +354,11 @@ def test_get_version_fail(self): def test_url_with_path_prefix(self): server = self.http_server - server.expect_request('/prefix/ping').respond_with_json( + server.expect_request('/prefix/prefix1/ping').respond_with_json( response_json={"version": "3.0"}, ) version = InfluxDBClient3( - host=f'http://{server.host}:{server.port}/prefix', + host=f'http://{server.host}:{server.port}/prefix/prefix1', org="ORG", database="DB", token="TOKEN" From 56b0bb5073d63dafcb64975867dab58db350f9b6 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 15:49:44 +0700 Subject: [PATCH 08/16] fix: add test for URL path prefix handling in `test_write_with_path_prefix` --- tests/test_write_local_server.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_write_local_server.py b/tests/test_write_local_server.py index 9e7e406..885fb17 100644 --- a/tests/test_write_local_server.py +++ b/tests/test_write_local_server.py @@ -40,6 +40,22 @@ def test_write_default_params(self, httpserver: HTTPServer): method="POST", uri="/api/v2/write", query_string={"org": "ORG", "bucket": "DB", "precision": "ns"})) + def test_write_with_path_prefix(self): + httpserver = HTTPServer(host="localhost", port=8086, ssl_context=None) + httpserver.start() + httpserver.expect_request("/prefix/prefix1/api/v2/write").respond_with_data(status=200) + + InfluxDBClient3( + host=(httpserver.url_for("/prefix/prefix1")), org="ORG", database="DB", token="TOKEN", + write_client_options=write_client_options( + write_options=WriteOptions(write_type=WriteType.synchronous) + ) + ).write(self.SAMPLE_RECORD) + + self.assert_request_made(httpserver, RequestMatcher( + method="POST", uri="/prefix/prefix1/api/v2/write", + query_string={"org": "ORG", "bucket": "DB", "precision": "ns"})) + def test_write_with_write_options(self, httpserver: HTTPServer): self.set_response_status(httpserver, 200) From d84ba3a492bcd0de250a694a2a03c4b240ef29c1 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 15:52:00 +0700 Subject: [PATCH 09/16] fix: rename test for clarity on URL path prefix error handling --- tests/test_influxdb_client_3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_influxdb_client_3.py b/tests/test_influxdb_client_3.py index 7f14292..5956fcb 100644 --- a/tests/test_influxdb_client_3.py +++ b/tests/test_influxdb_client_3.py @@ -365,7 +365,7 @@ def test_url_with_path_prefix(self): ).get_server_version() assert version == "3.0" - def test_url_lack_path_prefix(self): + def test_url_error_without_path_prefix(self): server = self.http_server server.expect_request('/prefix/ping').respond_with_json( response_json={"version": "3.0"}, From 594141238e647ae56bacad583274949722691f1e Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 16:05:08 +0700 Subject: [PATCH 10/16] [EMPTY] trigger CI From f6d433ab1eb34c49a10b8cfba2b6e788bafc0267 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 16:45:00 +0700 Subject: [PATCH 11/16] fix: replace direct instantiation with context manager in InfluxDBClient3 tests --- tests/test_influxdb_client_3.py | 15 +++++++-------- tests/test_write_local_server.py | 13 +++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/test_influxdb_client_3.py b/tests/test_influxdb_client_3.py index 5956fcb..df2606d 100644 --- a/tests/test_influxdb_client_3.py +++ b/tests/test_influxdb_client_3.py @@ -357,28 +357,27 @@ def test_url_with_path_prefix(self): server.expect_request('/prefix/prefix1/ping').respond_with_json( response_json={"version": "3.0"}, ) - version = InfluxDBClient3( + with InfluxDBClient3( host=f'http://{server.host}:{server.port}/prefix/prefix1', org="ORG", database="DB", token="TOKEN" - ).get_server_version() - assert version == "3.0" + ) as client: + assert client.get_server_version() == "3.0" def test_url_error_without_path_prefix(self): server = self.http_server server.expect_request('/prefix/ping').respond_with_json( response_json={"version": "3.0"}, ) - try: - InfluxDBClient3( + with InfluxDBClient3( host=f'http://{server.host}:{server.port}', org="ORG", database="DB", token="TOKEN" - ).get_server_version() - except ApiException: - self.assertRaises(ApiException) + ) as client: + with self.assertRaises(ApiException): + client.get_server_version() if __name__ == '__main__': diff --git a/tests/test_write_local_server.py b/tests/test_write_local_server.py index 885fb17..0564dd2 100644 --- a/tests/test_write_local_server.py +++ b/tests/test_write_local_server.py @@ -45,12 +45,13 @@ def test_write_with_path_prefix(self): httpserver.start() httpserver.expect_request("/prefix/prefix1/api/v2/write").respond_with_data(status=200) - InfluxDBClient3( - host=(httpserver.url_for("/prefix/prefix1")), org="ORG", database="DB", token="TOKEN", - write_client_options=write_client_options( - write_options=WriteOptions(write_type=WriteType.synchronous) - ) - ).write(self.SAMPLE_RECORD) + with InfluxDBClient3( + host=(httpserver.url_for("/prefix/prefix1")), org="ORG", database="DB", token="TOKEN", + write_client_options=write_client_options( + write_options=WriteOptions(write_type=WriteType.synchronous) + ) + ) as client: + client.write(self.SAMPLE_RECORD) self.assert_request_made(httpserver, RequestMatcher( method="POST", uri="/prefix/prefix1/api/v2/write", From 75dbc4fdec7373c20cd20f65ef0c5a36dd372a11 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 16:52:18 +0700 Subject: [PATCH 12/16] fix: comment out redundant `test_write_with_path_prefix` test --- tests/test_write_local_server.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/test_write_local_server.py b/tests/test_write_local_server.py index 0564dd2..3e595a7 100644 --- a/tests/test_write_local_server.py +++ b/tests/test_write_local_server.py @@ -40,22 +40,22 @@ def test_write_default_params(self, httpserver: HTTPServer): method="POST", uri="/api/v2/write", query_string={"org": "ORG", "bucket": "DB", "precision": "ns"})) - def test_write_with_path_prefix(self): - httpserver = HTTPServer(host="localhost", port=8086, ssl_context=None) - httpserver.start() - httpserver.expect_request("/prefix/prefix1/api/v2/write").respond_with_data(status=200) - - with InfluxDBClient3( - host=(httpserver.url_for("/prefix/prefix1")), org="ORG", database="DB", token="TOKEN", - write_client_options=write_client_options( - write_options=WriteOptions(write_type=WriteType.synchronous) - ) - ) as client: - client.write(self.SAMPLE_RECORD) - - self.assert_request_made(httpserver, RequestMatcher( - method="POST", uri="/prefix/prefix1/api/v2/write", - query_string={"org": "ORG", "bucket": "DB", "precision": "ns"})) + # def test_write_with_path_prefix(self): + # httpserver = HTTPServer(host="localhost", port=8086, ssl_context=None) + # httpserver.start() + # httpserver.expect_request("/prefix/prefix1/api/v2/write").respond_with_data(status=200) + # + # with InfluxDBClient3( + # host=(httpserver.url_for("/prefix/prefix1")), org="ORG", database="DB", token="TOKEN", + # write_client_options=write_client_options( + # write_options=WriteOptions(write_type=WriteType.synchronous) + # ) + # ) as client: + # client.write(self.SAMPLE_RECORD) + # + # self.assert_request_made(httpserver, RequestMatcher( + # method="POST", uri="/prefix/prefix1/api/v2/write", + # query_string={"org": "ORG", "bucket": "DB", "precision": "ns"})) def test_write_with_write_options(self, httpserver: HTTPServer): self.set_response_status(httpserver, 200) From c2e0601a18cc6615d5aa4dce523d96179445f572 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 16:58:55 +0700 Subject: [PATCH 13/16] fix: uncomment and refactor `test_write_with_path_prefix` to use context manager --- tests/test_write_local_server.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/tests/test_write_local_server.py b/tests/test_write_local_server.py index 3e595a7..2fe9bc0 100644 --- a/tests/test_write_local_server.py +++ b/tests/test_write_local_server.py @@ -40,22 +40,21 @@ def test_write_default_params(self, httpserver: HTTPServer): method="POST", uri="/api/v2/write", query_string={"org": "ORG", "bucket": "DB", "precision": "ns"})) - # def test_write_with_path_prefix(self): - # httpserver = HTTPServer(host="localhost", port=8086, ssl_context=None) - # httpserver.start() - # httpserver.expect_request("/prefix/prefix1/api/v2/write").respond_with_data(status=200) - # - # with InfluxDBClient3( - # host=(httpserver.url_for("/prefix/prefix1")), org="ORG", database="DB", token="TOKEN", - # write_client_options=write_client_options( - # write_options=WriteOptions(write_type=WriteType.synchronous) - # ) - # ) as client: - # client.write(self.SAMPLE_RECORD) - # - # self.assert_request_made(httpserver, RequestMatcher( - # method="POST", uri="/prefix/prefix1/api/v2/write", - # query_string={"org": "ORG", "bucket": "DB", "precision": "ns"})) + def test_write_with_path_prefix(self): + with HTTPServer(host="localhost", port=8086, ssl_context=None) as httpserver: + httpserver.expect_request("/prefix/prefix1/api/v2/write").respond_with_data(status=200) + + with InfluxDBClient3( + host=(httpserver.url_for("/prefix/prefix1")), org="ORG", database="DB", token="TOKEN", + write_client_options=write_client_options( + write_options=WriteOptions(write_type=WriteType.synchronous) + ) + ) as client: + client.write(self.SAMPLE_RECORD) + + self.assert_request_made(httpserver, RequestMatcher( + method="POST", uri="/prefix/prefix1/api/v2/write", + query_string={"org": "ORG", "bucket": "DB", "precision": "ns"})) def test_write_with_write_options(self, httpserver: HTTPServer): self.set_response_status(httpserver, 200) From 5f968d3a742644c20a9cef04df38ebe9666d031a Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 17:17:10 +0700 Subject: [PATCH 14/16] [EMPTY] trigger CI From 219015246a6cea05ebb136782b92caa5f78ec362 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 17:21:59 +0700 Subject: [PATCH 15/16] fix: add test for `write_port_overwrite` in InfluxDBClient3 --- tests/test_influxdb_client_3.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_influxdb_client_3.py b/tests/test_influxdb_client_3.py index df2606d..a0cf9ad 100644 --- a/tests/test_influxdb_client_3.py +++ b/tests/test_influxdb_client_3.py @@ -67,6 +67,14 @@ def test_token_auth_scheme_explicit(self): ) self.assertEqual(client._client.auth_header_value, "my_scheme my_token") + def test_write_port_overwrite(self): + with InfluxDBClient3( + host="http://localhost:8080", + write_port_overwrite=8086, + token="my_token", + ) as client: + self.assertEqual(client._client.url,"http://localhost:8086") + def test_write_options(self): client = InfluxDBClient3( host="localhost", From 3fd9f017ab14cc31da1711caae09f8781278c3f4 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 13 Nov 2025 17:24:03 +0700 Subject: [PATCH 16/16] fix: add missing space in assertion for URL comparison --- tests/test_influxdb_client_3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_influxdb_client_3.py b/tests/test_influxdb_client_3.py index a0cf9ad..6792507 100644 --- a/tests/test_influxdb_client_3.py +++ b/tests/test_influxdb_client_3.py @@ -73,7 +73,7 @@ def test_write_port_overwrite(self): write_port_overwrite=8086, token="my_token", ) as client: - self.assertEqual(client._client.url,"http://localhost:8086") + self.assertEqual(client._client.url, "http://localhost:8086") def test_write_options(self): client = InfluxDBClient3(