From 24f70ed674b52774f613ebdd8a039b7fdbac38b5 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 29 Oct 2024 09:54:53 +0000 Subject: [PATCH 01/10] Renaming in examples README. --- examples/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/README.md b/examples/README.md index 53c5c0e..6e248b0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,6 +1,6 @@ -# MicroCrate Examples +# micropython-cratedb Examples -This folder contains code samples and fully working example code for the MicroCrate driver. +This folder contains code samples and fully working example code for the micropython-cratedb driver. * `example_usage.py`: Demonstrates various types of query. This does not have any specific microcontroller dependencies, and can be run on desktop MicroPython. * `object_examples.py`: Demonstrates operations using an [OBJECT](https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#objects) column in CrateDB. Also demonstrates the use of the [ARRAY](https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#array) container data type. From f4ed43687acb7e67ae2dbf65be644e0a5dbd1c9d Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 29 Oct 2024 09:59:52 +0000 Subject: [PATCH 02/10] Renamed. --- micropython-cratedb.py | 152 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 micropython-cratedb.py diff --git a/micropython-cratedb.py b/micropython-cratedb.py new file mode 100644 index 0000000..336ffb3 --- /dev/null +++ b/micropython-cratedb.py @@ -0,0 +1,152 @@ +try: + import requests +except ImportError: + import urequests as requests + +from base64 import b64encode + +# IDs of CrateDB supported data types. +# https://cratedb.com/docs/crate/reference/en/latest/interfaces/http.html#id4 +CRATEDB_TYPE_NULL = 0 +CRATEDB_TYPE_NOT_SUPPORTED = 1 +CRATEDB_TYPE_CHAR = 2 +CRATEDB_TYPE_BOOLEAN = 3 +CRATEDB_TYPE_TEXT = 4 +CRATEDB_TYPE_IP = 5 +CRATEDB_TYPE_DOUBLE_PRECISION = 6 +CRATEDB_TYPE_REAL = 7 +CRATEDB_TYPE_SMALLINT = 8 +CRATEDB_TYPE_INTEGER = 9 +CRATEDB_TYPE_BIGINT = 10 +CRATEDB_TYPE_TIMESTAMP_WITH_TIME_ZONE = 11 +CRATEDB_TYPE_OBJECT = 12 +CRATEDB_TYPE_GEO_POINT = 13 +CRATEDB_TYPE_GEO_SHAPE = 14 +CRATEDB_TYPE_TIMESTAMP_WITHOUT_TIME_ZONE = 15 +CRATEDB_TYPE_UNCHECKED_OBJECT = 16 +CRATEDB_TYPE_INTERVAL = 17 +CRATEDB_TYPE_REGPROC = 19 +CRATEDB_TYPE_TIME = 20 +CRATEDB_TYPE_OIDVECTOR = 21 +CRATEDB_TYPE_NUMERIC = 22 +CRATEDB_TYPE_REGCLASS = 23 +CRATEDB_TYPE_DATE = 24 +CRATEDB_TYPE_BIT = 25 +CRATEDB_TYPE_JSON = 26 +CRATEDB_TYPE_CHARACTER = 27 +CRATEDB_TYPE_FLOAT_VECTOR = 28 +CRATEDB_TYPE_ARRAY = 100 + +# CrateDB error codes. +# https://cratedb.com/docs/crate/reference/en/latest/interfaces/http.html#error-codes +CRATEDB_ERROR_INVALID_SYNTAX = 4000 +CRATEDB_ERROR_INVALID_ANALYZER = 4001 +CRATEDB_ERROR_INVALID_RELATION_NAME = 4002 +CRATEDB_ERROR_FIELD_TYPE_VALIDATION_FAILED = 4003 +CRATEDB_ERROR_FEATURE_UNSUPPORTED = 4004 +CRATEDB_ERROR_ALTER_TABLE_WITH_ALIAS_UNSUPPORTED = 4005 +CRATEDB_ERROR_COLUMN_ALIAS_AMBIGUOUS = 4006 +CRATEDB_ERROR_OPERATION_NOT_SUPPORTED_ON_RELATION = 4007 +CRATEDB_ERROR_INVALID_COLUMN_NAME = 4008 +CRATEDB_ERROR_USER_NOT_AUTHORIZED = 4010 +CRATEDB_ERROR_MISSING_USER_PRIVILEGE = 4011 +CRATEDB_ERROR_NODE_READ_ONLY = 4031 +CRATEDB_ERROR_UNKNOWN_RELATION = 4041 +CRATEDB_ERROR_UNKNOWN_ANALYZER = 4042 +CRATEDB_ERROR_UNKNOWN_COLUMN = 4043 +CRATEDB_ERROR_UNKNOWN_TYPE = 4044 +CRATEDB_ERROR_UNKNOWN_SCHEMA = 4045 +CRATEDB_ERROR_UNKNOWN_PARTITION = 4046 +CRATEDB_ERROR_UNKNOWN_REPOSITORY = 4047 +CRATEDB_ERROR_UNKNOWN_SNAPSHOT = 4048 +CRATEDB_ERROR_UNKNOWN_FUNCTION = 4049 +CRATEDB_ERROR_UNKNOWN_USER = 40410 +CRATEDB_ERROR_DOCUMENT_EXISTS = 4091 +CRATEDB_ERROR_VERSION_CONFLICT = 4092 +CRATEDB_ERROR_RELATION_EXISTS = 4093 +CRATEDB_ERROR_TABLE_ALIAS_SCHEMA_DIFFERS = 4094 +CRATEDB_ERROR_REPOSITORY_EXISTS = 4095 +CRATEDB_ERROR_SNAPSHOT_EXISTS = 4096 +CRATEDB_ERROR_PARTITION_EXISTS = 4097 +CRATEDB_ERROR_FUNCTION_EXISTS = 4098 +CRATEDB_ERROR_USER_EXISTS = 4099 +CRATEDB_ERROR_OBJECT_EXISTS = 4100 +CRATEDB_ERROR_UNHANDLED_SERVER_ERROR = 5000 +CRATEDB_ERROR_TASK_EXECUTION_FAILED = 5001 +CRATEDB_ERROR_SHARDS_UNAVAILABLE = 5002 +CRATEDB_ERROR_QUERY_FAILED_ON_SHARDS = 5003 +CRATEDB_ERROR_SNAPSHOT_CREATION_FAILED = 5004 +CRATEDB_ERROR_QUERY_KILLED = 5030 + +class NetworkError(Exception): + pass + +class CrateDBError(Exception): + pass + +class CrateDB: + def __init__(self, host, port=4200, user=None, password=None, schema="doc", use_ssl=True): + self.user = user + self.password = password + self.schema = schema + self.host = host + self.port = port + self.use_ssl = use_ssl + + self.cratedb_url = f"{'https' if self.use_ssl == True else 'http'}://{self.host}:{self.port}/_sql" + + if self.user is not None and self.password is not None: + self.encoded_credentials = self.__encode_credentials(self.user, self.password) + + + def __encode_credentials(self, user, password): + creds_str = f"{user}:{password}" + return b64encode(creds_str.encode("UTF-8")).decode("UTF-8") + + + def __make_request(self, sql, args=None, with_types = False, return_response = True): + headers = { + "Content-Type": "text/json", + "Default-Schema": self.schema + } + + if hasattr(self, "encoded_credentials"): + headers["Authorization"] = f"Basic {self.encoded_credentials}" + + request_url = self.cratedb_url if with_types == False else f"{self.cratedb_url}?types" + + payload = { + "stmt": sql + } + + if args is not None: + for arg in args: + if not isinstance(arg, list): + payload["args"] = args + break + + if not "args" in payload: + payload["bulk_args"] = args + + try: + response = requests.post( + request_url, + headers = headers, + json = payload + ) + except OSError as o: + raise NetworkError(o) + + if response.status_code == 400 or response.status_code == 404 or response.status_code == 409: + error_doc = response.json() + raise CrateDBError(error_doc) + elif response.status_code != 200: + raise NetworkError(f"Error {response.status_code}: {response.reason.decode('UTF-8')}") + + if return_response == True: + return response.json() + + + def execute(self, sql, args = None, with_types = False, return_response = True): + return self.__make_request(sql, args, with_types, return_response) + \ No newline at end of file From b8eaa0fbe6086e936122fb5de99e3b32c5aaa4b1 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 29 Oct 2024 10:01:13 +0000 Subject: [PATCH 03/10] Updated main script name. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 364c886..20cc790 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "urls": [ [ - "microcrate.py", - "github:simonprickett/microcrate/microcrate.py" + "micropython-cratedb.py", + "github:simonprickett/microcrate/micropython-cratedb.py" ] ], "deps": [ From b238ffebe1255c03ee8d13fd1f86cf2984403980 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 29 Oct 2024 10:10:55 +0000 Subject: [PATCH 04/10] Decided to run with the Python file being named cratedb.py --- README.md | 24 ++++---- cratedb.py | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 4 +- 3 files changed, 166 insertions(+), 14 deletions(-) create mode 100644 cratedb.py diff --git a/README.md b/README.md index 3179c33..b7e3ddf 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# MicroCrate - A CrateDB Driver for MicroPython +# micropython-cratedb - A CrateDB Driver for MicroPython ## Introduction -MicroCrate is a [CrateDB](https://cratedb.com) driver for the [MicroPython](https://micropython.org) language. It connects to CrateDB using the [HTTP Endpoint](https://cratedb.com/docs/crate/reference/en/latest/interfaces/http.html). +micropython-cratedb is a [CrateDB](https://cratedb.com) driver for the [MicroPython](https://micropython.org) language. It connects to CrateDB using the [HTTP Endpoint](https://cratedb.com/docs/crate/reference/en/latest/interfaces/http.html). To use this, you'll need a CrateDB database cluster. Sign up for our cloud free tier [here](https://console.cratedb.cloud/) or get started with Docker [here](https://hub.docker.com/_/crate). @@ -41,7 +41,7 @@ mip.install("github:simonprickett/microcrate") Import the driver like this: ```python -import microcrate +import cratedb ``` ### Connecting to CrateDB @@ -49,7 +49,7 @@ import microcrate Connect to a CrateDB cluster in the cloud by providing hostname, user name and password: ```python -crate = microcrate.CrateDB( +crate = cratedb.CrateDB( host="host", user="user", password="password" @@ -61,7 +61,7 @@ The driver uses SSL by default. If you're running CrateDB locally (with Docker for example), connect like this: ```python -crate = microcrate.CrateDB( +crate = cratedb.CrateDB( host="hostname", use_ssl=False ) @@ -70,7 +70,7 @@ crate = microcrate.CrateDB( The driver will connect to port 4200 unless you provide an alternative value: ```python -crate = microcrate.CrateDB( +crate = cratedb.CrateDB( host="host", user="user", port=4201, @@ -287,7 +287,7 @@ The response includes the number of rows affected by the update: CrateDB supports flexible storage and indexing of objects / JSON data. To learn more about this, check out our [blog post](https://cratedb.com/blog/handling-dynamic-objects-in-cratedb) that explains the different ways objects can be stored. -Here are some basic examples showing how to store objects with MicroCrate and retrieve desired fields from them. +Here are some basic examples showing how to store objects with micropython-cratedb and retrieve desired fields from them. Assume a table with the following definition having a [dynamic object](https://cratedb.com/blog/handling-dynamic-objects-in-cratedb) column: @@ -389,7 +389,7 @@ The driver can throw the following types of exception: Here's an example showing how to catch a network error: ```python -crate = microcrate.CrateDB("nonexist", use_ssl = False) +crate = cratedb.CrateDB("nonexist", use_ssl = False) try: response = crate.execute( @@ -399,7 +399,7 @@ try: ], with_types=True ) -except microcrate.NetworkError as e: +except cratedb.NetworkError as e: print("Network error:") print(e) ``` @@ -418,7 +418,7 @@ try: response = crate.execute( "SELECT nonexist FROM temp_humidity" ) -except microcrate.CrateDBError as e: +except cratedb.CrateDBError as e: print("CrateDB error:") print(e) ``` @@ -451,12 +451,12 @@ This driver library was tested using the following MicroPython versions: * **1.23.0 (Pimoroni build)** ([download](https://github.com/pimoroni/pimoroni-pico/releases)) * Raspberry Pi Pico W -If you have other microcontroller boards that you can test the driver with or provide examples for, we'd love to receive a pull request! +If you have other microcontroller boards that you can test the driver with or provide examples for, we'd love to receive a [pull request](/pulls)! ## Need Help? If you need help, have a bug report or feature request, or just want to show us your project that uses this driver then we'd love to hear from you! -For bugs or feature requests, please raise an issue on GitHub. We also welcome pull requests! +For bugs or feature requests, please raise an [issue](/issues) on GitHub. We also welcome [pull requests](/pulls)! If you have a project to share with us, or a more general question about this driver or CrateDB, please post in our [community forum](https://community.cratedb.com/). diff --git a/cratedb.py b/cratedb.py new file mode 100644 index 0000000..336ffb3 --- /dev/null +++ b/cratedb.py @@ -0,0 +1,152 @@ +try: + import requests +except ImportError: + import urequests as requests + +from base64 import b64encode + +# IDs of CrateDB supported data types. +# https://cratedb.com/docs/crate/reference/en/latest/interfaces/http.html#id4 +CRATEDB_TYPE_NULL = 0 +CRATEDB_TYPE_NOT_SUPPORTED = 1 +CRATEDB_TYPE_CHAR = 2 +CRATEDB_TYPE_BOOLEAN = 3 +CRATEDB_TYPE_TEXT = 4 +CRATEDB_TYPE_IP = 5 +CRATEDB_TYPE_DOUBLE_PRECISION = 6 +CRATEDB_TYPE_REAL = 7 +CRATEDB_TYPE_SMALLINT = 8 +CRATEDB_TYPE_INTEGER = 9 +CRATEDB_TYPE_BIGINT = 10 +CRATEDB_TYPE_TIMESTAMP_WITH_TIME_ZONE = 11 +CRATEDB_TYPE_OBJECT = 12 +CRATEDB_TYPE_GEO_POINT = 13 +CRATEDB_TYPE_GEO_SHAPE = 14 +CRATEDB_TYPE_TIMESTAMP_WITHOUT_TIME_ZONE = 15 +CRATEDB_TYPE_UNCHECKED_OBJECT = 16 +CRATEDB_TYPE_INTERVAL = 17 +CRATEDB_TYPE_REGPROC = 19 +CRATEDB_TYPE_TIME = 20 +CRATEDB_TYPE_OIDVECTOR = 21 +CRATEDB_TYPE_NUMERIC = 22 +CRATEDB_TYPE_REGCLASS = 23 +CRATEDB_TYPE_DATE = 24 +CRATEDB_TYPE_BIT = 25 +CRATEDB_TYPE_JSON = 26 +CRATEDB_TYPE_CHARACTER = 27 +CRATEDB_TYPE_FLOAT_VECTOR = 28 +CRATEDB_TYPE_ARRAY = 100 + +# CrateDB error codes. +# https://cratedb.com/docs/crate/reference/en/latest/interfaces/http.html#error-codes +CRATEDB_ERROR_INVALID_SYNTAX = 4000 +CRATEDB_ERROR_INVALID_ANALYZER = 4001 +CRATEDB_ERROR_INVALID_RELATION_NAME = 4002 +CRATEDB_ERROR_FIELD_TYPE_VALIDATION_FAILED = 4003 +CRATEDB_ERROR_FEATURE_UNSUPPORTED = 4004 +CRATEDB_ERROR_ALTER_TABLE_WITH_ALIAS_UNSUPPORTED = 4005 +CRATEDB_ERROR_COLUMN_ALIAS_AMBIGUOUS = 4006 +CRATEDB_ERROR_OPERATION_NOT_SUPPORTED_ON_RELATION = 4007 +CRATEDB_ERROR_INVALID_COLUMN_NAME = 4008 +CRATEDB_ERROR_USER_NOT_AUTHORIZED = 4010 +CRATEDB_ERROR_MISSING_USER_PRIVILEGE = 4011 +CRATEDB_ERROR_NODE_READ_ONLY = 4031 +CRATEDB_ERROR_UNKNOWN_RELATION = 4041 +CRATEDB_ERROR_UNKNOWN_ANALYZER = 4042 +CRATEDB_ERROR_UNKNOWN_COLUMN = 4043 +CRATEDB_ERROR_UNKNOWN_TYPE = 4044 +CRATEDB_ERROR_UNKNOWN_SCHEMA = 4045 +CRATEDB_ERROR_UNKNOWN_PARTITION = 4046 +CRATEDB_ERROR_UNKNOWN_REPOSITORY = 4047 +CRATEDB_ERROR_UNKNOWN_SNAPSHOT = 4048 +CRATEDB_ERROR_UNKNOWN_FUNCTION = 4049 +CRATEDB_ERROR_UNKNOWN_USER = 40410 +CRATEDB_ERROR_DOCUMENT_EXISTS = 4091 +CRATEDB_ERROR_VERSION_CONFLICT = 4092 +CRATEDB_ERROR_RELATION_EXISTS = 4093 +CRATEDB_ERROR_TABLE_ALIAS_SCHEMA_DIFFERS = 4094 +CRATEDB_ERROR_REPOSITORY_EXISTS = 4095 +CRATEDB_ERROR_SNAPSHOT_EXISTS = 4096 +CRATEDB_ERROR_PARTITION_EXISTS = 4097 +CRATEDB_ERROR_FUNCTION_EXISTS = 4098 +CRATEDB_ERROR_USER_EXISTS = 4099 +CRATEDB_ERROR_OBJECT_EXISTS = 4100 +CRATEDB_ERROR_UNHANDLED_SERVER_ERROR = 5000 +CRATEDB_ERROR_TASK_EXECUTION_FAILED = 5001 +CRATEDB_ERROR_SHARDS_UNAVAILABLE = 5002 +CRATEDB_ERROR_QUERY_FAILED_ON_SHARDS = 5003 +CRATEDB_ERROR_SNAPSHOT_CREATION_FAILED = 5004 +CRATEDB_ERROR_QUERY_KILLED = 5030 + +class NetworkError(Exception): + pass + +class CrateDBError(Exception): + pass + +class CrateDB: + def __init__(self, host, port=4200, user=None, password=None, schema="doc", use_ssl=True): + self.user = user + self.password = password + self.schema = schema + self.host = host + self.port = port + self.use_ssl = use_ssl + + self.cratedb_url = f"{'https' if self.use_ssl == True else 'http'}://{self.host}:{self.port}/_sql" + + if self.user is not None and self.password is not None: + self.encoded_credentials = self.__encode_credentials(self.user, self.password) + + + def __encode_credentials(self, user, password): + creds_str = f"{user}:{password}" + return b64encode(creds_str.encode("UTF-8")).decode("UTF-8") + + + def __make_request(self, sql, args=None, with_types = False, return_response = True): + headers = { + "Content-Type": "text/json", + "Default-Schema": self.schema + } + + if hasattr(self, "encoded_credentials"): + headers["Authorization"] = f"Basic {self.encoded_credentials}" + + request_url = self.cratedb_url if with_types == False else f"{self.cratedb_url}?types" + + payload = { + "stmt": sql + } + + if args is not None: + for arg in args: + if not isinstance(arg, list): + payload["args"] = args + break + + if not "args" in payload: + payload["bulk_args"] = args + + try: + response = requests.post( + request_url, + headers = headers, + json = payload + ) + except OSError as o: + raise NetworkError(o) + + if response.status_code == 400 or response.status_code == 404 or response.status_code == 409: + error_doc = response.json() + raise CrateDBError(error_doc) + elif response.status_code != 200: + raise NetworkError(f"Error {response.status_code}: {response.reason.decode('UTF-8')}") + + if return_response == True: + return response.json() + + + def execute(self, sql, args = None, with_types = False, return_response = True): + return self.__make_request(sql, args, with_types, return_response) + \ No newline at end of file diff --git a/package.json b/package.json index 20cc790..ef00f75 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "urls": [ [ - "micropython-cratedb.py", - "github:simonprickett/microcrate/micropython-cratedb.py" + "cratedb.py", + "github:simonprickett/microcrate/cratedb.py" ] ], "deps": [ From 72e2acf3f19409ebd759237e22c7729823accd91 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 29 Oct 2024 10:13:01 +0000 Subject: [PATCH 05/10] Renaming in Pi Pico W example. --- examples/picow_demo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/picow_demo.py b/examples/picow_demo.py index 098a44e..13ff4b1 100644 --- a/examples/picow_demo.py +++ b/examples/picow_demo.py @@ -1,4 +1,4 @@ -# MicroCrate Example for the Raspberry Pi Pico W. +# micropython-cratedb Example for the Raspberry Pi Pico W. # Configure your CrateDB credentials and WiFi SSID # and password below before running this. @@ -7,10 +7,10 @@ import sys import time -import microcrate +import cratedb # Configure CrateDB driver. -crate = microcrate.CrateDB( +crate = cratedb.CrateDB( host="hostname", user="username", password="password" From b4f86425442e3bcadf1ea804a758af94140fe978 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 29 Oct 2024 10:13:31 +0000 Subject: [PATCH 06/10] Removed old TODO comment. --- examples/picow_demo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/picow_demo.py b/examples/picow_demo.py index 13ff4b1..fc5ef63 100644 --- a/examples/picow_demo.py +++ b/examples/picow_demo.py @@ -65,7 +65,7 @@ "INSERT INTO picow_test (id, temp) VALUES (?, ?)", [ ip_addr, - temperature # TODO what units are these? + temperature ] ) From 34cda8f4c4449abeea41f81173e96ce2b41b9a94 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 29 Oct 2024 10:14:43 +0000 Subject: [PATCH 07/10] Renaming in object example script. --- examples/object_examples.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/object_examples.py b/examples/object_examples.py index 21cd19d..0316a92 100644 --- a/examples/object_examples.py +++ b/examples/object_examples.py @@ -3,13 +3,13 @@ # run in any MicroPython environment. You will need to edit the # code below to use your CrateDB credentials. -import microcrate +import cratedb # CrateDB Docker / local network, no SSL. -# crate = microcrate.CrateDB(host="hostname", use_ssl=False) +# crate = cratedb.CrateDB(host="hostname", use_ssl=False) # CrateDB Cloud. -crate = microcrate.CrateDB( +crate = cratedb.CrateDB( host="host", user="user", password="password" @@ -325,9 +325,9 @@ # {'rows': [[]], 'rowcount': 1, 'cols': [], 'duration': 67.91708} print(response) -except microcrate.NetworkError as e: +except cratedb.NetworkError as e: print("Caught NetworkError:") print(e) -except microcrate.CrateDBError as c: +except cratedb.CrateDBError as c: print("Caught CrateDBError:") print(c) \ No newline at end of file From 68b11a45df0afd9b8602ca8ec614d230c28b2f80 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 29 Oct 2024 10:16:12 +0000 Subject: [PATCH 08/10] Renaming in the example usage script. --- examples/example_usage.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/example_usage.py b/examples/example_usage.py index 0a7dd43..834fe33 100644 --- a/examples/example_usage.py +++ b/examples/example_usage.py @@ -3,13 +3,13 @@ # in any MicroPython environment. You will need to edit the # code below to use your CrateDB credentials. -import microcrate +import cratedb # CrateDB Docker / local network, no SSL. -# crate = microcrate.CrateDB(host="hostname", use_ssl=False) +# crate = cratedb.CrateDB(host="hostname", use_ssl=False) # CrateDB Cloud. -crate = microcrate.CrateDB( +crate = cratedb.CrateDB( host="host", user="user", password="password" @@ -84,9 +84,9 @@ # This will throw a CrateDBError as we dropped the table. response = crate.execute("select * from driver_test") -except microcrate.NetworkError as e: +except cratedb.NetworkError as e: print("Caught NetworkError:") print(e) -except microcrate.CrateDBError as c: +except cratedb.CrateDBError as c: print("Caught CrateDBError:") print(c) From 99cfd1de032367649a3222d8f261e93a4ef97f7a Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 29 Oct 2024 10:31:18 +0000 Subject: [PATCH 09/10] Updated with 1.24 runtime testing. --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b7e3ddf..4db9d71 100644 --- a/README.md +++ b/README.md @@ -443,13 +443,16 @@ The [`examples`](examples/) folder contains example MicroPython scripts, some of ## Testing -This driver library was tested using the following MicroPython versions: - -* **1.23.0** ([download](https://micropython.org/download/)) - * macOS/darwin - * Raspberry Pi Pico W -* **1.23.0 (Pimoroni build)** ([download](https://github.com/pimoroni/pimoroni-pico/releases)) - * Raspberry Pi Pico W +This driver library has been tested using the following MicroPython versions: + +* **1.24.0** + * macOS/darwin ([install with Homebrew package manager](https://formulae.brew.sh/formula/micropython)) + * Raspberry Pi Pico W ([download](https://micropython.org/download/RPI_PICO_W/)) +* **1.23.0** + * macOS/darwin ([install with Homebrew package manager](https://formulae.brew.sh/formula/micropython)) + * Raspberry Pi Pico W ([download](https://micropython.org/download/RPI_PICO_W/)) +* **1.23.0 (Pimoroni build)** + * Raspberry Pi Pico W ([download](https://github.com/pimoroni/pimoroni-pico/releases)) If you have other microcontroller boards that you can test the driver with or provide examples for, we'd love to receive a [pull request](/pulls)! From 6f8f287cadb5baaeb989d98fa58cdf2b73b303e7 Mon Sep 17 00:00:00 2001 From: Simon Prickett Date: Tue, 29 Oct 2024 10:54:13 +0000 Subject: [PATCH 10/10] Deleted old files. --- microcrate.py | 152 ----------------------------------------- micropython-cratedb.py | 152 ----------------------------------------- 2 files changed, 304 deletions(-) delete mode 100644 microcrate.py delete mode 100644 micropython-cratedb.py diff --git a/microcrate.py b/microcrate.py deleted file mode 100644 index 336ffb3..0000000 --- a/microcrate.py +++ /dev/null @@ -1,152 +0,0 @@ -try: - import requests -except ImportError: - import urequests as requests - -from base64 import b64encode - -# IDs of CrateDB supported data types. -# https://cratedb.com/docs/crate/reference/en/latest/interfaces/http.html#id4 -CRATEDB_TYPE_NULL = 0 -CRATEDB_TYPE_NOT_SUPPORTED = 1 -CRATEDB_TYPE_CHAR = 2 -CRATEDB_TYPE_BOOLEAN = 3 -CRATEDB_TYPE_TEXT = 4 -CRATEDB_TYPE_IP = 5 -CRATEDB_TYPE_DOUBLE_PRECISION = 6 -CRATEDB_TYPE_REAL = 7 -CRATEDB_TYPE_SMALLINT = 8 -CRATEDB_TYPE_INTEGER = 9 -CRATEDB_TYPE_BIGINT = 10 -CRATEDB_TYPE_TIMESTAMP_WITH_TIME_ZONE = 11 -CRATEDB_TYPE_OBJECT = 12 -CRATEDB_TYPE_GEO_POINT = 13 -CRATEDB_TYPE_GEO_SHAPE = 14 -CRATEDB_TYPE_TIMESTAMP_WITHOUT_TIME_ZONE = 15 -CRATEDB_TYPE_UNCHECKED_OBJECT = 16 -CRATEDB_TYPE_INTERVAL = 17 -CRATEDB_TYPE_REGPROC = 19 -CRATEDB_TYPE_TIME = 20 -CRATEDB_TYPE_OIDVECTOR = 21 -CRATEDB_TYPE_NUMERIC = 22 -CRATEDB_TYPE_REGCLASS = 23 -CRATEDB_TYPE_DATE = 24 -CRATEDB_TYPE_BIT = 25 -CRATEDB_TYPE_JSON = 26 -CRATEDB_TYPE_CHARACTER = 27 -CRATEDB_TYPE_FLOAT_VECTOR = 28 -CRATEDB_TYPE_ARRAY = 100 - -# CrateDB error codes. -# https://cratedb.com/docs/crate/reference/en/latest/interfaces/http.html#error-codes -CRATEDB_ERROR_INVALID_SYNTAX = 4000 -CRATEDB_ERROR_INVALID_ANALYZER = 4001 -CRATEDB_ERROR_INVALID_RELATION_NAME = 4002 -CRATEDB_ERROR_FIELD_TYPE_VALIDATION_FAILED = 4003 -CRATEDB_ERROR_FEATURE_UNSUPPORTED = 4004 -CRATEDB_ERROR_ALTER_TABLE_WITH_ALIAS_UNSUPPORTED = 4005 -CRATEDB_ERROR_COLUMN_ALIAS_AMBIGUOUS = 4006 -CRATEDB_ERROR_OPERATION_NOT_SUPPORTED_ON_RELATION = 4007 -CRATEDB_ERROR_INVALID_COLUMN_NAME = 4008 -CRATEDB_ERROR_USER_NOT_AUTHORIZED = 4010 -CRATEDB_ERROR_MISSING_USER_PRIVILEGE = 4011 -CRATEDB_ERROR_NODE_READ_ONLY = 4031 -CRATEDB_ERROR_UNKNOWN_RELATION = 4041 -CRATEDB_ERROR_UNKNOWN_ANALYZER = 4042 -CRATEDB_ERROR_UNKNOWN_COLUMN = 4043 -CRATEDB_ERROR_UNKNOWN_TYPE = 4044 -CRATEDB_ERROR_UNKNOWN_SCHEMA = 4045 -CRATEDB_ERROR_UNKNOWN_PARTITION = 4046 -CRATEDB_ERROR_UNKNOWN_REPOSITORY = 4047 -CRATEDB_ERROR_UNKNOWN_SNAPSHOT = 4048 -CRATEDB_ERROR_UNKNOWN_FUNCTION = 4049 -CRATEDB_ERROR_UNKNOWN_USER = 40410 -CRATEDB_ERROR_DOCUMENT_EXISTS = 4091 -CRATEDB_ERROR_VERSION_CONFLICT = 4092 -CRATEDB_ERROR_RELATION_EXISTS = 4093 -CRATEDB_ERROR_TABLE_ALIAS_SCHEMA_DIFFERS = 4094 -CRATEDB_ERROR_REPOSITORY_EXISTS = 4095 -CRATEDB_ERROR_SNAPSHOT_EXISTS = 4096 -CRATEDB_ERROR_PARTITION_EXISTS = 4097 -CRATEDB_ERROR_FUNCTION_EXISTS = 4098 -CRATEDB_ERROR_USER_EXISTS = 4099 -CRATEDB_ERROR_OBJECT_EXISTS = 4100 -CRATEDB_ERROR_UNHANDLED_SERVER_ERROR = 5000 -CRATEDB_ERROR_TASK_EXECUTION_FAILED = 5001 -CRATEDB_ERROR_SHARDS_UNAVAILABLE = 5002 -CRATEDB_ERROR_QUERY_FAILED_ON_SHARDS = 5003 -CRATEDB_ERROR_SNAPSHOT_CREATION_FAILED = 5004 -CRATEDB_ERROR_QUERY_KILLED = 5030 - -class NetworkError(Exception): - pass - -class CrateDBError(Exception): - pass - -class CrateDB: - def __init__(self, host, port=4200, user=None, password=None, schema="doc", use_ssl=True): - self.user = user - self.password = password - self.schema = schema - self.host = host - self.port = port - self.use_ssl = use_ssl - - self.cratedb_url = f"{'https' if self.use_ssl == True else 'http'}://{self.host}:{self.port}/_sql" - - if self.user is not None and self.password is not None: - self.encoded_credentials = self.__encode_credentials(self.user, self.password) - - - def __encode_credentials(self, user, password): - creds_str = f"{user}:{password}" - return b64encode(creds_str.encode("UTF-8")).decode("UTF-8") - - - def __make_request(self, sql, args=None, with_types = False, return_response = True): - headers = { - "Content-Type": "text/json", - "Default-Schema": self.schema - } - - if hasattr(self, "encoded_credentials"): - headers["Authorization"] = f"Basic {self.encoded_credentials}" - - request_url = self.cratedb_url if with_types == False else f"{self.cratedb_url}?types" - - payload = { - "stmt": sql - } - - if args is not None: - for arg in args: - if not isinstance(arg, list): - payload["args"] = args - break - - if not "args" in payload: - payload["bulk_args"] = args - - try: - response = requests.post( - request_url, - headers = headers, - json = payload - ) - except OSError as o: - raise NetworkError(o) - - if response.status_code == 400 or response.status_code == 404 or response.status_code == 409: - error_doc = response.json() - raise CrateDBError(error_doc) - elif response.status_code != 200: - raise NetworkError(f"Error {response.status_code}: {response.reason.decode('UTF-8')}") - - if return_response == True: - return response.json() - - - def execute(self, sql, args = None, with_types = False, return_response = True): - return self.__make_request(sql, args, with_types, return_response) - \ No newline at end of file diff --git a/micropython-cratedb.py b/micropython-cratedb.py deleted file mode 100644 index 336ffb3..0000000 --- a/micropython-cratedb.py +++ /dev/null @@ -1,152 +0,0 @@ -try: - import requests -except ImportError: - import urequests as requests - -from base64 import b64encode - -# IDs of CrateDB supported data types. -# https://cratedb.com/docs/crate/reference/en/latest/interfaces/http.html#id4 -CRATEDB_TYPE_NULL = 0 -CRATEDB_TYPE_NOT_SUPPORTED = 1 -CRATEDB_TYPE_CHAR = 2 -CRATEDB_TYPE_BOOLEAN = 3 -CRATEDB_TYPE_TEXT = 4 -CRATEDB_TYPE_IP = 5 -CRATEDB_TYPE_DOUBLE_PRECISION = 6 -CRATEDB_TYPE_REAL = 7 -CRATEDB_TYPE_SMALLINT = 8 -CRATEDB_TYPE_INTEGER = 9 -CRATEDB_TYPE_BIGINT = 10 -CRATEDB_TYPE_TIMESTAMP_WITH_TIME_ZONE = 11 -CRATEDB_TYPE_OBJECT = 12 -CRATEDB_TYPE_GEO_POINT = 13 -CRATEDB_TYPE_GEO_SHAPE = 14 -CRATEDB_TYPE_TIMESTAMP_WITHOUT_TIME_ZONE = 15 -CRATEDB_TYPE_UNCHECKED_OBJECT = 16 -CRATEDB_TYPE_INTERVAL = 17 -CRATEDB_TYPE_REGPROC = 19 -CRATEDB_TYPE_TIME = 20 -CRATEDB_TYPE_OIDVECTOR = 21 -CRATEDB_TYPE_NUMERIC = 22 -CRATEDB_TYPE_REGCLASS = 23 -CRATEDB_TYPE_DATE = 24 -CRATEDB_TYPE_BIT = 25 -CRATEDB_TYPE_JSON = 26 -CRATEDB_TYPE_CHARACTER = 27 -CRATEDB_TYPE_FLOAT_VECTOR = 28 -CRATEDB_TYPE_ARRAY = 100 - -# CrateDB error codes. -# https://cratedb.com/docs/crate/reference/en/latest/interfaces/http.html#error-codes -CRATEDB_ERROR_INVALID_SYNTAX = 4000 -CRATEDB_ERROR_INVALID_ANALYZER = 4001 -CRATEDB_ERROR_INVALID_RELATION_NAME = 4002 -CRATEDB_ERROR_FIELD_TYPE_VALIDATION_FAILED = 4003 -CRATEDB_ERROR_FEATURE_UNSUPPORTED = 4004 -CRATEDB_ERROR_ALTER_TABLE_WITH_ALIAS_UNSUPPORTED = 4005 -CRATEDB_ERROR_COLUMN_ALIAS_AMBIGUOUS = 4006 -CRATEDB_ERROR_OPERATION_NOT_SUPPORTED_ON_RELATION = 4007 -CRATEDB_ERROR_INVALID_COLUMN_NAME = 4008 -CRATEDB_ERROR_USER_NOT_AUTHORIZED = 4010 -CRATEDB_ERROR_MISSING_USER_PRIVILEGE = 4011 -CRATEDB_ERROR_NODE_READ_ONLY = 4031 -CRATEDB_ERROR_UNKNOWN_RELATION = 4041 -CRATEDB_ERROR_UNKNOWN_ANALYZER = 4042 -CRATEDB_ERROR_UNKNOWN_COLUMN = 4043 -CRATEDB_ERROR_UNKNOWN_TYPE = 4044 -CRATEDB_ERROR_UNKNOWN_SCHEMA = 4045 -CRATEDB_ERROR_UNKNOWN_PARTITION = 4046 -CRATEDB_ERROR_UNKNOWN_REPOSITORY = 4047 -CRATEDB_ERROR_UNKNOWN_SNAPSHOT = 4048 -CRATEDB_ERROR_UNKNOWN_FUNCTION = 4049 -CRATEDB_ERROR_UNKNOWN_USER = 40410 -CRATEDB_ERROR_DOCUMENT_EXISTS = 4091 -CRATEDB_ERROR_VERSION_CONFLICT = 4092 -CRATEDB_ERROR_RELATION_EXISTS = 4093 -CRATEDB_ERROR_TABLE_ALIAS_SCHEMA_DIFFERS = 4094 -CRATEDB_ERROR_REPOSITORY_EXISTS = 4095 -CRATEDB_ERROR_SNAPSHOT_EXISTS = 4096 -CRATEDB_ERROR_PARTITION_EXISTS = 4097 -CRATEDB_ERROR_FUNCTION_EXISTS = 4098 -CRATEDB_ERROR_USER_EXISTS = 4099 -CRATEDB_ERROR_OBJECT_EXISTS = 4100 -CRATEDB_ERROR_UNHANDLED_SERVER_ERROR = 5000 -CRATEDB_ERROR_TASK_EXECUTION_FAILED = 5001 -CRATEDB_ERROR_SHARDS_UNAVAILABLE = 5002 -CRATEDB_ERROR_QUERY_FAILED_ON_SHARDS = 5003 -CRATEDB_ERROR_SNAPSHOT_CREATION_FAILED = 5004 -CRATEDB_ERROR_QUERY_KILLED = 5030 - -class NetworkError(Exception): - pass - -class CrateDBError(Exception): - pass - -class CrateDB: - def __init__(self, host, port=4200, user=None, password=None, schema="doc", use_ssl=True): - self.user = user - self.password = password - self.schema = schema - self.host = host - self.port = port - self.use_ssl = use_ssl - - self.cratedb_url = f"{'https' if self.use_ssl == True else 'http'}://{self.host}:{self.port}/_sql" - - if self.user is not None and self.password is not None: - self.encoded_credentials = self.__encode_credentials(self.user, self.password) - - - def __encode_credentials(self, user, password): - creds_str = f"{user}:{password}" - return b64encode(creds_str.encode("UTF-8")).decode("UTF-8") - - - def __make_request(self, sql, args=None, with_types = False, return_response = True): - headers = { - "Content-Type": "text/json", - "Default-Schema": self.schema - } - - if hasattr(self, "encoded_credentials"): - headers["Authorization"] = f"Basic {self.encoded_credentials}" - - request_url = self.cratedb_url if with_types == False else f"{self.cratedb_url}?types" - - payload = { - "stmt": sql - } - - if args is not None: - for arg in args: - if not isinstance(arg, list): - payload["args"] = args - break - - if not "args" in payload: - payload["bulk_args"] = args - - try: - response = requests.post( - request_url, - headers = headers, - json = payload - ) - except OSError as o: - raise NetworkError(o) - - if response.status_code == 400 or response.status_code == 404 or response.status_code == 409: - error_doc = response.json() - raise CrateDBError(error_doc) - elif response.status_code != 200: - raise NetworkError(f"Error {response.status_code}: {response.reason.decode('UTF-8')}") - - if return_response == True: - return response.json() - - - def execute(self, sql, args = None, with_types = False, return_response = True): - return self.__make_request(sql, args, with_types, return_response) - \ No newline at end of file