From e677ee6d294521448da6609ad794651db23c8de3 Mon Sep 17 00:00:00 2001 From: jeoooo Date: Sat, 28 Dec 2024 18:12:06 +0800 Subject: [PATCH 1/2] refactor PocketbaseExtended.h to simplify method signatures and improve readability --- PocketbaseExtended.cpp | 338 +++++++---------------------------------- PocketbaseExtended.h | 94 ++---------- 2 files changed, 73 insertions(+), 359 deletions(-) diff --git a/PocketbaseExtended.cpp b/PocketbaseExtended.cpp index 67303ad..0507212 100644 --- a/PocketbaseExtended.cpp +++ b/PocketbaseExtended.cpp @@ -1,288 +1,117 @@ -// PocketbaseArduino.cpp +#include "PocketbaseExtended.h" -#include "PocketbaseArduino.h" -// #include -// #include -// #include -// #include - -#if defined(ESP8266) -#include -#include -#elif defined(ESP32) -#include -#include -#include -#endif - -PocketbaseArduino::PocketbaseArduino(const char *baseUrl) +PocketbaseExtended::PocketbaseExtended(const char *baseUrl) { base_url = baseUrl; - if (base_url.endsWith("/")) { base_url.remove(base_url.length() - 1); } - base_url += "/api/"; - current_endpoint = base_url; - expand_param = ""; - fields_param = ""; } -PocketbaseArduino &PocketbaseArduino::collection(const char *collection) +PocketbaseExtended &PocketbaseExtended::collection(const char *collection) { current_endpoint = "collections/" + String(collection) + "/"; return *this; } -String performGETRequest(const char *endpoint) +void PocketbaseExtended::initializeHttpClient(HTTPClient &http, const char *endpoint) { - #if defined(ESP32) std::unique_ptr client(new WiFiClientSecure); client->setInsecure(); - if (strncmp(url, "https", 5) == 0) + if (strncmp(endpoint, "https", 5) == 0) { - http.begin(*client, url); + http.begin(*client, endpoint); } else { - http.begin(url); + http.begin(endpoint); } #elif defined(ESP8266) + std::unique_ptr client(new BearSSL::WiFiClientSecure); + client->setInsecure(); if (strncmp(endpoint, "https", 5) == 0) { - std::unique_ptr client(new BearSSL::WiFiClientSecure); - client->setInsecure(); - HTTPClient https; - - Serial.print("[HTTPS] Full URL: "); - Serial.println(endpoint); - - if (https.begin(*client, endpoint)) - { - Serial.print("[HTTPS] GET...\n"); - int httpCode = https.GET(); - if (httpCode > 0) - { - Serial.printf("[HTTPS] GET... code: %d\n", httpCode); - // if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) - if (httpCode) - { - String payload = https.getString(); - // print request contents (must be removed) - Serial.println(payload); - https.end(); - return payload; - } - } - // else - // { - // Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str()); - // } - https.end(); - } - else - { - Serial.printf("[HTTPS] Unable to connect\n"); - } - // TODO: improve return value in case failure happens - return ""; // Return an empty string on failure + http.begin(*client, endpoint); } else { - Serial.print("[HTTP] Full URL: "); - Serial.println(endpoint); - - HTTPClient http; - Serial.print("[HTTP] begin...\n"); - if (http.begin(endpoint)) - { - Serial.print("[HTTP] GET...\n"); - int httpCode = http.GET(); - if (httpCode > 0) - { - Serial.printf("[HTTP] GET... code: %d\n", httpCode); - // if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) - if (httpCode) - { - String payload = http.getString(); - Serial.println(payload); - http.end(); - return payload; - } - } - // else - // { - // Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); - // } - http.end(); - } - else - { - Serial.printf("[HTTP] Unable to connect\n"); - } - // TODO: improve return value in case failure happens - return ""; // Return an empty string on failure + http.begin(endpoint); } #endif } -String performDELETERequest(const char *endpoint) +String PocketbaseExtended::performGETRequest(const char *endpoint) { + HTTPClient http; + initializeHttpClient(http, endpoint); -#if defined(ESP32) - std::unique_ptr client(new WiFiClientSecure); - client->setInsecure(); - if (strncmp(url, "https", 5) == 0) + int httpCode = http.GET(); + if (httpCode > 0) { - http.begin(*client, url); + String payload = http.getString(); + http.end(); + return payload; } else { - http.begin(url); + http.end(); + return ""; // Return an empty string on failure } -#elif defined(ESP8266) - if (strncmp(endpoint, "https", 5) == 0) - { - std::unique_ptr client(new BearSSL::WiFiClientSecure); - client->setInsecure(); - HTTPClient https; +} - Serial.print("[HTTPS] Full URL: "); - Serial.println(endpoint); +String PocketbaseExtended::performDELETERequest(const char *endpoint) +{ + HTTPClient http; + initializeHttpClient(http, endpoint); - if (https.begin(*client, endpoint)) - { - Serial.print("[HTTPS] DELETE...\n"); - int httpCode = https.sendRequest("DELETE"); - if (httpCode > 0) - { - Serial.printf("[HTTPS] DELETE... code: %d\n", httpCode); - // if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) - if (httpCode) - { - String payload = https.getString(); - // print request contents (must be removed) - Serial.println(payload); - https.end(); - return payload; - } - } - // else - // { - // Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str()); - // } - https.end(); - } - else - { - Serial.printf("[HTTPS] Unable to connect\n"); - } - // TODO: improve return value in case failure happens - return ""; // Return an empty string on failure + int httpCode = http.sendRequest("DELETE"); + if (httpCode > 0) + { + String payload = http.getString(); + http.end(); + return payload; } else { - Serial.print("[HTTP] Full URL: "); - Serial.println(endpoint); - - HTTPClient http; - Serial.print("[HTTP] begin...\n"); - if (http.begin(endpoint)) - { - Serial.print("[HTTP] DELETE...\n"); - int httpCode = http.sendRequest("DELETE"); - if (httpCode > 0) - { - Serial.printf("[HTTP] DELETE... code: %d\n", httpCode); - // if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) - if (httpCode) - { - String payload = http.getString(); - Serial.println(payload); - http.end(); - return payload; - } - } - // else - // { - // Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); - // } - http.end(); - } - else - { - Serial.printf("[HTTP] Unable to connect\n"); - } - // TODO: improve return value in case failure happens + http.end(); return ""; // Return an empty string on failure } -#endif } -String performPOSTRequest(const char *endpoint, const String &requestBody) +String PocketbaseExtended::performPOSTRequest(const char *endpoint, const String &requestBody) { -#if defined(ESP32) - // ESP32 implementation - // ... -#elif defined(ESP8266) - std::unique_ptr client(new BearSSL::WiFiClientSecure); - client->setInsecure(); HTTPClient http; + initializeHttpClient(http, endpoint); - Serial.print("[HTTPS] Full URL: "); - Serial.println(endpoint); - - if (http.begin(*client, endpoint)) + int httpCode = http.POST(requestBody); + if (httpCode > 0) { - Serial.print("[HTTPS] POST...\n"); - - int httpCode = http.POST(requestBody); - if (httpCode > 0) - { - Serial.printf("[HTTPS] POST... code: %d\n", httpCode); - if (httpCode) - { - String payload = http.getString(); - Serial.println(payload); - http.end(); - return payload; - } - } - else - { - Serial.printf("[HTTPS] POST... failed, error: %s\n", http.errorToString(httpCode).c_str()); - } - + String payload = http.getString(); http.end(); + return payload; } else { - Serial.printf("[HTTPS] Unable to connect\n"); + http.end(); + return ""; // Return an empty string on failure } - - // TODO: improve return value in case of failure - return ""; // Return an empty string on failure -#endif } -String PocketbaseArduino::getOne(const char *recordId, const char *expand /* = nullptr */, const char *fields /* = nullptr */) +String PocketbaseExtended::getOne(const char *recordId, const char *expand, const char *fields) { String fullEndpoint = base_url + String(current_endpoint) + "records/" + recordId; - // Append the expand parameter if provided if (expand != nullptr && strlen(expand) > 0) { fullEndpoint += "?expand=" + String(expand); } - // Append the fields parameter if provided if (fields != nullptr && strlen(fields) > 0) { - // Check if there's already a query string fullEndpoint += (fullEndpoint.indexOf('?') == -1) ? "?" : "&"; fullEndpoint += "fields=" + String(fields); } @@ -290,87 +119,38 @@ String PocketbaseArduino::getOne(const char *recordId, const char *expand /* = n return performGETRequest(fullEndpoint.c_str()); } -String PocketbaseArduino::getList( - const char *page /* = nullptr */, - const char *perPage /* = nullptr */, - const char *sort /* = nullptr */, - const char *filter /* = nullptr */, - const char *skipTotal /* = nullptr */, - const char *expand /* = nullptr */, - const char *fields /* = nullptr */) +String PocketbaseExtended::getList(const char *page, const char *perPage, const char *sort, const char *filter, const char *skipTotal, const char *expand, const char *fields) { String fullEndpoint = base_url + String(current_endpoint) + "records/"; - // Append the expand parameter if provided - if (expand != nullptr && strlen(expand) > 0) - { - fullEndpoint += (fullEndpoint.indexOf('?') == -1) ? "?" : "&"; - fullEndpoint += "?expand=" + String(expand); - } - - // Append the fields parameter if provided - if (fields != nullptr && strlen(fields) > 0) + auto appendQueryParam = [&](const char *paramName, const char *paramValue) { - // Check if there's already a query string - fullEndpoint += (fullEndpoint.indexOf('?') == -1) ? "?" : "&"; - fullEndpoint += "fields=" + String(fields); - } - - // Append the page parameter if provided - if (page != nullptr && strlen(page) > 0) - { - // Check if there's already a query string - fullEndpoint += (fullEndpoint.indexOf('?') == -1) ? "?" : "&"; - fullEndpoint += "page=" + String(page); - } - - // Append the perPage parameter if provided - if (perPage != nullptr && strlen(perPage) > 0) - { - // Check if there's already a query string - fullEndpoint += (fullEndpoint.indexOf('?') == -1) ? "?" : "&"; - fullEndpoint += "perPage=" + String(perPage); - } - - // Append the sort parameter if provided - if (sort != nullptr && strlen(sort) > 0) - { - // Check if there's already a query string - fullEndpoint += (fullEndpoint.indexOf('?') == -1) ? "?" : "&"; - fullEndpoint += "sort=" + String(sort); - } - - // Append the skipTotal parameter if provided - if (skipTotal != nullptr && strlen(skipTotal) > 0) - { - // Check if there's already a query string - fullEndpoint += (fullEndpoint.indexOf('?') == -1) ? "?" : "&"; - fullEndpoint += "skipTotal=" + String(skipTotal); - } + if (paramValue != nullptr && strlen(paramValue) > 0) + { + fullEndpoint += (fullEndpoint.indexOf('?') == -1) ? "?" : "&"; + fullEndpoint += String(paramName) + "=" + String(paramValue); + } + }; - // Append the filter parameter if provided - if (filter != nullptr && strlen(filter) > 0) - { - // Check if there's already a query string - fullEndpoint += (fullEndpoint.indexOf('?') == -1) ? "?" : "&"; - fullEndpoint += "skipTotal=" + String(filter); - } + appendQueryParam("expand", expand); + appendQueryParam("fields", fields); + appendQueryParam("page", page); + appendQueryParam("perPage", perPage); + appendQueryParam("sort", sort); + appendQueryParam("skipTotal", skipTotal); + appendQueryParam("filter", filter); return performGETRequest(fullEndpoint.c_str()); } -String PocketbaseArduino::deleteRecord(const char *recordId) +String PocketbaseExtended::deleteRecord(const char *recordId) { String fullEndpoint = base_url + String(current_endpoint) + "records/" + recordId; - return performDELETERequest(fullEndpoint.c_str()); } -String PocketbaseArduino::create(const String &requestBody) +String PocketbaseExtended::create(const String &requestBody) { - // Construct the endpoint based on the current_endpoint String fullEndpoint = current_endpoint + "records/"; - - // Call performPOSTRequest with the constructed endpoint and provided parameters return performPOSTRequest(fullEndpoint.c_str(), requestBody); } \ No newline at end of file diff --git a/PocketbaseExtended.h b/PocketbaseExtended.h index 3e2fa5b..60216d2 100644 --- a/PocketbaseExtended.h +++ b/PocketbaseExtended.h @@ -1,103 +1,37 @@ -// PocketbaseExtended.h - #ifndef PocketbaseExtended_h #define PocketbaseExtended_h #include "Arduino.h" +#if defined(ESP8266) #include #include -#include - #include +#elif defined(ESP32) +#include +#include +#include +#endif class PocketbaseExtended { public: - PocketbaseExtended(const char *baseUrl); // Constructor + PocketbaseExtended(const char *baseUrl); - // Methods to build collection and record URLs PocketbaseExtended &collection(const char *collection); - /** - * @brief Fetches a single record from a Pocketbase collection - * - * @param recordId The ID of the record to view. - * - * @param expand (Optional) Auto expand record relations. Ex.:?expand=relField1,relField2.subRelField Supports up to 6-levels depth nested relations expansion. - * The expanded relations will be appended to the record under the expand property (eg. "expand": {"relField1": {...}, ...}). - * Only the relations to which the request user has permissions to view will be expanded. - * - * @param fields (Optional) Comma separated string of the fields to return in the JSON response (by default returns all fields). Ex.: ?fields=*,expand.relField.name - * * targets all keys from the specific depth level. - * In addition, the following field modifiers are also supported: - * :excerpt(maxLength, withEllipsis?) - * Returns a short plain text version of the field string value. - * Ex.: ?fields=*,description:excerpt(200,true) - * - * For more information, see: https://pocketbase.io/docs - */ - String getOne( - const char *recordId, - const char *expand /* = nullptr */, - const char *fields /* = nullptr */); - - /** - * @brief Deletes a single record from a Pocketbase collection - * - * @param recordId The ID of the record to delete. - * - * For more information, see: https://pocketbase.io/docs - */ + String getOne(const char *recordId, const char *expand = nullptr, const char *fields = nullptr); String deleteRecord(const char *recordId); - - /** - * @brief Fetches a multiple records from a Pocketbase collection. Supports sorting and filtering. - * - * @param page The page (aka. offset) of the paginated list (default to 1). - * - * @param perPage Specify the max returned records per page (default to 30). - * - * @param sort Specify the records order attribute(s). - * Add - / + (default) in front of the attribute for DESC / ASC order. Ex.: - * `DESC by created and ASC by id` - * `?sort=-created,id` - * - * @param filter Filter the returned records. - * - * @param expand (Optional) Auto expand record relations. Ex.:?expand=relField1,relField2.subRelField Supports up to 6-levels depth nested relations expansion. - * The expanded relations will be appended to the record under the expand property (eg. "expand": {"relField1": {...}, ...}). - * Only the relations to which the request user has permissions to view will be expanded. - * - * @param fields (Optional) Comma separated string of the fields to return in the JSON response (by default returns all fields). Ex.: ?fields=*,expand.relField.name - * * targets all keys from the specific depth level. - * In addition, the following field modifiers are also supported: - * :excerpt(maxLength, withEllipsis?) - * Returns a short plain text version of the field string value. - * Ex.: ?fields=*,description:excerpt(200,true) - * - * @param skipTotal If it is set the total counts query will be skipped and the response fields totalItems and totalPages will have -1 value. - * This could drastically speed up the search queries when the total counters are not needed or cursor based pagination is used. - * For optimization purposes, it is set by default for the getFirstListItem() and getFullList() SDKs methods. - * - * For more information, see: https://pocketbase.io/docs - */ - String getList( - const char *page /* = nullptr */, - const char *perPage /* = nullptr */, - const char *sort /* = nullptr */, - const char *filter /* = nullptr */, - const char *skipTotal /* = nullptr */, - const char *expand /* = nullptr */, - const char *fields /* = nullptr */); - + String getList(const char *page = nullptr, const char *perPage = nullptr, const char *sort = nullptr, const char *filter = nullptr, const char *skipTotal = nullptr, const char *expand = nullptr, const char *fields = nullptr); String create(const String &requestBody); private: String base_url; String current_endpoint; - String expand_param; - String fields_param; + + String performGETRequest(const char *endpoint); + String performDELETERequest(const char *endpoint); + String performPOSTRequest(const char *endpoint, const String &requestBody); }; -#endif +#endif \ No newline at end of file From f6df15d76a9aeef914ad235527df8b826ed1d38c Mon Sep 17 00:00:00 2001 From: jeoooo Date: Sat, 28 Dec 2024 18:12:14 +0800 Subject: [PATCH 2/2] add unit tests for PocketbaseExtended functionality --- tests/test_PocketbaseExtended.ino | 63 +++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 tests/test_PocketbaseExtended.ino diff --git a/tests/test_PocketbaseExtended.ino b/tests/test_PocketbaseExtended.ino new file mode 100644 index 0000000..a573679 --- /dev/null +++ b/tests/test_PocketbaseExtended.ino @@ -0,0 +1,63 @@ +#include +#include "PocketbaseExtended.h" + +// Mock WiFi and HTTPClient for testing +class MockWiFiClientSecure : public WiFiClientSecure +{ +public: + void setInsecure() {} +}; + +class MockHTTPClient : public HTTPClient +{ +public: + void begin(WiFiClientSecure &client, const char *url) {} + void begin(const char *url) {} + int GET() { return 200; } + int POST(const String &payload) { return 200; } + int sendRequest(const char *method) { return 200; } + String getString() { return "{\"result\":\"success\"}"; } + void end() {} +}; + +PocketbaseExtended pocketbase("http://example.com"); + +test(getOneTest) +{ + pocketbase.collection("testCollection"); + String result = pocketbase.getOne("recordId"); + assertEqual(result, "{\"result\":\"success\"}"); +} + +test(getListTest) +{ + pocketbase.collection("testCollection"); + String result = pocketbase.getList("1", "10"); + assertEqual(result, "{\"result\":\"success\"}"); +} + +test(createTest) +{ + pocketbase.collection("testCollection"); + String result = pocketbase.create("{\"name\":\"test\"}"); + assertEqual(result, "{\"result\":\"success\"}"); +} + +test(deleteRecordTest) +{ + pocketbase.collection("testCollection"); + String result = pocketbase.deleteRecord("recordId"); + assertEqual(result, "{\"result\":\"success\"}"); +} + +void setup() +{ + // Initialize AUnit + aunit::TestRunner::run(); +} + +void loop() +{ + // Run AUnit tests + aunit::TestRunner::run(); +} \ No newline at end of file