diff --git a/CHANGELOG.md b/CHANGELOG.md index 727c6ea1..3da5e8ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## 1.50.0 [unreleased] + +### Features + +1. [696](https://github.com/influxdata/influxdb-client-python/pull/696): Move "setuptools" package to build dependency. + +## 1.49.0 [2025-05-22] + +### Bug Fixes + +1. [#682](https://github.com/influxdata/influxdb-client-python/pull/682): Check core types when creating Authentication instances. + +### Examples + +1. [#682](https://github.com/influxdata/influxdb-client-python/pull/682): New example for working with Authentication API. + +## 1.48.0 [2024-11-27] + +### Bug Fixes + +1. [#679](https://github.com/influxdata/influxdb-client-python/pull/679): Add note to caught errors about need to check client timeout. + ## 1.47.0 [2024-10-22] ### Bug Fixes diff --git a/README.md b/README.md index ef4eff86..5b541dcf 100644 --- a/README.md +++ b/README.md @@ -1313,6 +1313,13 @@ All async APIs are available via `influxdb_client.client.influxdb_client_async.I and also check to readiness of the InfluxDB via `/ping` endpoint: +The `InfluxDBClientAsync` constructor accepts a number of __configuration properties__. Most useful among these are: + +* `connection_pool_maxsize` - The total number of simultaneous connections. Defaults to `multiprocessing.cpu_count() * 5`. +* `enable_gzip` - enable gzip compression during `write` and `query` calls. Defaults to `false`. +* `proxy` - URL of an HTTP proxy to be used. +* `timeout` - The maximum number of milliseconds for handling HTTP requests from initial handshake to handling response data. This is passed directly to the underlying transport library. If large amounts of data are anticipated, for example from `query_api.query_stream(...)`, this should be increased to avoid `TimeoutError` or `CancelledError`. Defaults to 10_000 ms. + > ``` python > import asyncio > diff --git a/conda/meta.yaml b/conda/meta.yaml index 2595c51e..33a0c26c 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -1,5 +1,5 @@ {% set name = "influxdb_client" %} -{% set version = "1.46.0" %} +{% set version = "1.49.0" %} package: @@ -7,8 +7,8 @@ package: version: {{ version }} source: - url: https://files.pythonhosted.org/packages/53/9e/4bd499eff06eab47f7995178623d508703d2b4fedab1a3544b04ef06fb0c/influxdb_client-1.46.0.tar.gz - sha256: d5b5f3787db8ad75e64bf069fdc4d441e43b1a1d57f2c11082af309ef0b9722c + url: https://files.pythonhosted.org/packages/2a/f3/9c418215cf399529175ed5b198d15a21c2e29f28d90932107634b375c9ee/influxdb_client-1.49.0.tar.gz + sha256: 4a53a218adef6ac9458bfbd31fa08c76194f70310c6b4e01f53d804bd2c48e03 build: number: 0 diff --git a/examples/README.md b/examples/README.md index 2b42ffd7..7d3a5eea 100644 --- a/examples/README.md +++ b/examples/README.md @@ -28,6 +28,7 @@ - [monitoring_and_alerting.py](monitoring_and_alerting.py) - How to create the Check with Slack notification. - [task_example.py](task_example.py) - How to create a Task by API - [templates_management.py](templates_management.py) - How to use Templates and Stack API +- [authorizations.py](authorizations.py) - How to create and use authorizations. ## InfluxDB Cloud diff --git a/examples/authorizations.py b/examples/authorizations.py new file mode 100644 index 00000000..5857f624 --- /dev/null +++ b/examples/authorizations.py @@ -0,0 +1,103 @@ +import os + +from influxdb_client import InfluxDBClient, BucketRetentionRules, PermissionResource, Permission, Authorization, \ + WriteOptions +from influxdb_client.client.write_api import WriteType +from influxdb_client.rest import ApiException + +HOST_URL = os.environ.get("INFLUX_HOST") if os.environ.get("INFLUX_HOST") is not None else "http://localhost:8086" +TOKEN = os.environ.get("INFLUX_TOKEN") if os.environ.get("INFLUX_TOKEN") is not None else "my-token" +ORG = os.environ.get("INFLUX_ORG") if os.environ.get("INFLUX_ORG") is not None else "my-org" +SYS_BUCKET = os.environ.get("INFLUX_DB") if os.environ.get("INFLUX_DB") is not None else "my-bucket" +BUCKET = "special-bucket" + + +def create_auths(): + # Create authorizations with an initial client using all-access permissions + with InfluxDBClient(url=HOST_URL, token=TOKEN, org=ORG, debug=False) as globalClient: + bucket_rules = BucketRetentionRules(type="expire", every_seconds=3600) + bucket = globalClient.buckets_api().create_bucket(bucket_name=BUCKET, + retention_rules=bucket_rules, + org=ORG) + + bucket_permission_resource_r = PermissionResource(org=ORG, + org_id=bucket.org_id, + type="buckets", + id=bucket.id) + bucket_permission_resource_w = PermissionResource(org=ORG, + org_id=bucket.org_id, + type="buckets", + id=bucket.id) + read_bucket = Permission(action="read", resource=bucket_permission_resource_r) + write_bucket = Permission(action="write", resource=bucket_permission_resource_w) + permissions = [read_bucket, write_bucket] + auth_payload = Authorization(org_id=bucket.org_id, + permissions=permissions, + description="Shared bucket auth from Authorization object", + id="auth1_base") + auth_api = globalClient.authorizations_api() + # use keyword arguments + auth1 = auth_api.create_authorization(authorization=auth_payload) + # or use positional arguments + auth2 = auth_api.create_authorization(bucket.org_id, permissions) + + return auth1, auth2 + + +def try_sys_bucket(client): + print("starting to write") + + w_api = client.write_api(write_options=WriteOptions(write_type=WriteType.synchronous)) + try: + w_api.write(bucket=SYS_BUCKET, record="cpu,host=r2d2 use=3.14") + except ApiException as ae: + print(f"Write to {SYS_BUCKET} failed (as expected) due to:") + print(ae) + + +def try_restricted_bucket(client): + print("starting to write") + w_api = client.write_api(write_options=WriteOptions(write_type=WriteType.synchronous)) + + w_api.write(bucket=BUCKET, record="cpu,host=r2d2 usage=3.14") + print("written") + print("now query") + q_api = client.query_api() + query = f''' + from(bucket:"{BUCKET}") + |> range(start: -5m) + |> filter(fn: (r) => r["_measurement"] == "cpu")''' + + tables = q_api.query(query=query, org=ORG) + for table in tables: + for record in table.records: + print(record["_time"].isoformat(sep="T") + " | " + record["host"] + " | " + record["_field"] + "=" + str(record["_value"])) + + +def main(): + """ + a1 is generated using a local Authorization instance + a2 is generated using local permissions and an internally created Authorization + :return: void + """ + print("=== Setting up authorizations ===") + a1, a2 = create_auths() + + print("=== Using a1 authorization ===") + client1 = InfluxDBClient(url=HOST_URL, token=a1.token, org=ORG, debug=False) + print(" --- Try System Bucket ---") + try_sys_bucket(client1) + print(" --- Try Special Bucket ---") + try_restricted_bucket(client1) + print() + + print("=== Using a2 authorization ===") + client2 = InfluxDBClient(url=HOST_URL, token=a2.token, org=ORG, debug=False) + print(" --- Try System Bucket ---") + try_sys_bucket(client2) + print(" --- Try Special Bucket ---") + try_restricted_bucket(client2) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/influxdb_client/client/authorizations_api.py b/influxdb_client/client/authorizations_api.py index b7179b62..05be6ecd 100644 --- a/influxdb_client/client/authorizations_api.py +++ b/influxdb_client/client/authorizations_api.py @@ -11,7 +11,7 @@ def __init__(self, influxdb_client): self._influxdb_client = influxdb_client self._authorizations_service = AuthorizationsService(influxdb_client.api_client) - def create_authorization(self, org_id=None, permissions: list = None, + def create_authorization(self, org_id: str = None, permissions: list = None, authorization: Authorization = None) -> Authorization: """ Create an authorization. @@ -23,6 +23,8 @@ def create_authorization(self, org_id=None, permissions: list = None, """ if authorization is not None: + if not isinstance(authorization, Authorization): + raise TypeError(f"Attempt to use non-Authorization value for authorization: {authorization}") return self._authorizations_service.post_authorizations(authorization_post_request=authorization) # if org_id is not None and permissions is not None: diff --git a/influxdb_client/client/flux_csv_parser.py b/influxdb_client/client/flux_csv_parser.py index 7a73e3f8..99e68094 100644 --- a/influxdb_client/client/flux_csv_parser.py +++ b/influxdb_client/client/flux_csv_parser.py @@ -1,6 +1,5 @@ """Parsing response from InfluxDB to FluxStructures or DataFrame.""" - import base64 import codecs import csv as csv_parser @@ -147,6 +146,11 @@ async def _parse_flux_response_async(self): df = self._prepare_data_frame() if not self._is_profiler_table(metadata.table): yield df + except BaseException as e: + e_type = type(e).__name__ + if "CancelledError" in e_type or "TimeoutError" in e_type: + e.add_note("Stream cancelled during read. Recommended: Check Influxdb client `timeout` setting.") + raise finally: self._close() diff --git a/influxdb_client/client/influxdb_client.py b/influxdb_client/client/influxdb_client.py index 6079aac0..cbae75a9 100644 --- a/influxdb_client/client/influxdb_client.py +++ b/influxdb_client/client/influxdb_client.py @@ -265,7 +265,7 @@ def retry(self, conf: (str, str, str), data: str, exception: InfluxDBError): :param write_options: Write API configuration :param point_settings: settings to store default tags - :key success_callback: The callable ``callback`` to run after successfully writen a batch. + :key success_callback: The callable ``callback`` to run after having successfully written a batch. The callable must accept two arguments: - `Tuple`: ``(bucket, organization, precision)`` @@ -273,7 +273,7 @@ def retry(self, conf: (str, str, str), data: str, exception: InfluxDBError): **[batching mode]** - :key error_callback: The callable ``callback`` to run after unsuccessfully writen a batch. + :key error_callback: The callable ``callback`` to run after having unsuccessfully written a batch. The callable must accept three arguments: - `Tuple`: ``(bucket, organization, precision)`` diff --git a/influxdb_client/domain/authorization.py b/influxdb_client/domain/authorization.py index 67a0bfd3..aef38d9c 100644 --- a/influxdb_client/domain/authorization.py +++ b/influxdb_client/domain/authorization.py @@ -82,8 +82,12 @@ def __init__(self, created_at=None, updated_at=None, org_id=None, permissions=No if updated_at is not None: self.updated_at = updated_at if org_id is not None: + if not isinstance(org_id, str): + raise TypeError("org_id must be a string.") self.org_id = org_id if permissions is not None: + if not isinstance(permissions, list): + raise TypeError("permissions must be a list.") self.permissions = permissions if id is not None: self.id = id diff --git a/influxdb_client/version.py b/influxdb_client/version.py index 19339327..03ca288e 100644 --- a/influxdb_client/version.py +++ b/influxdb_client/version.py @@ -1,3 +1,3 @@ """Version of the Client that is used in User-Agent header.""" -VERSION = '1.47.0' +VERSION = '1.50.0dev0' diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..20c12656 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=21.0.0"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/setup.py b/setup.py index cda0d087..76c2748c 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,6 @@ 'reactivex >= 4.0.4', 'certifi >= 14.05.14', 'python_dateutil >= 2.5.3', - 'setuptools >= 21.0.0', 'urllib3 >= 1.26.0' ] diff --git a/tests/test_AuthorizationApi.py b/tests/test_AuthorizationApi.py index 8b1850d9..036f0d60 100644 --- a/tests/test_AuthorizationApi.py +++ b/tests/test_AuthorizationApi.py @@ -45,6 +45,25 @@ def test_createAuthorization(self): self.assertEqual(authorization.links["user"], "/api/v2/users/" + self.user.id) + def test_AuthorizationTypeAssert(self): + self.assertRaisesRegex(TypeError, "org_id must be a string.", Authorization, org_id={}) + self.assertRaisesRegex(TypeError, "permissions must be a list.", Authorization, permissions={}) + + def test_createAuthorizationWrongTypes(self): + user_resource = PermissionResource(org_id=self.organization.id, type="users") + read_users = Permission(action="read", resource=user_resource) + + org_resource = PermissionResource(org_id=self.organization.id, type="orgs") + write_organizations = Permission(action="write", resource=org_resource) + + permissions = [read_users, write_organizations] + self.assertRaisesRegex(TypeError, "org_id must be a string.", + self.authorizations_api.create_authorization, permissions) + self.assertRaisesRegex(TypeError, "permissions must be a list", + self.authorizations_api.create_authorization, "123456789ABCDEF0", "Foo") + self.assertRaisesRegex(TypeError, "Attempt to use non-Authorization value for authorization: Foo", + self.authorizations_api.create_authorization, "123456789ABCDEF0", permissions, "Foo") + def test_authorizationDescription(self): organization = self.my_organization pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy