diff --git a/README.md b/README.md index aee3084..baba8bf 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,26 @@ isTimerActive(); //Returns a bool if timer is active. # Misc ```c++ -getWorkSpace(); // Returns a "pretty" string of all workspaces and ID's -getProject(int const& WID); // Returns a "pretty" string of all project is specific workplace -CreateTag(String Name, int WID); // Requires the Workspace ID +getWorkSpaces(); // Returns a KVReturn struct of all workspaces +getProjects(int const& WID); // Returns a KVReturn struct of all projects for a given workspace +getTags(int const& WID); // Returns a KVReturn struct of all tags for a given workspace +CreateTag(String Name, int WID); // Requires the Workspace ID ``` +The `KVReturn` struct is as follows: + +```c++ +struct KVReturn { + int HTTPCode; + int pairCount; + KVPair * KVPairs; +}; +``` + +It is important to `delete[]` the KVPairs after you've used what you've needed, or set it to `NULL` if you plan to use it again. See the "Get All Projects" example for more info. + + + # General Setup @@ -76,4 +91,4 @@ ESP32 default libraries - [Create a Time Entry:](https://github.com/JoeyStrandnes/Arduino-Toggl-API/tree/master/examples/Create_Time_Entry) Create a time entry with duration 1 hour. - [Button Timer:](https://github.com/JoeyStrandnes/Arduino-Toggl-API/tree/master/examples/Button_Timer) Starts a timer and LED when a button is pressed. Stops timer and turns of LED when it is pressed again. - [Advanced Button Timer:](https://github.com/JoeyStrandnes/Arduino-Toggl-API/tree/master/examples/Advanced_Button_Timer) Does everything that the regular button timer does but will "sync" with other active timers. It will know if a timer is started and will know if it is stopped. The LED and button function will adjust acording to the timer status. - +- [Get All Projects:](/examples/Get_All_Projects) Gets all projects in all workspaces for a given user. diff --git a/examples/Get_All_Projects/Get_All_Projects.ino b/examples/Get_All_Projects/Get_All_Projects.ino new file mode 100644 index 0000000..744087d --- /dev/null +++ b/examples/Get_All_Projects/Get_All_Projects.ino @@ -0,0 +1,45 @@ +#include + +const char* SSID = ""; +const char* PASS = ""; + +String const Token {""}; //API Token is found in "Profile Settings" + +Toggl toggl; + +void setup() { + Serial.begin(115200); + toggl.init(SSID, PASS); + toggl.setAuth(Token); + delay(100); + Serial.println("Get all projects for all workspaces"); + KVReturn workspaces = toggl.getWorkSpaces(); + KVReturn projects; + if (workspaces.HTTPCode >= 200 && workspaces.HTTPCode <= 226) { + Serial.println("There are " + String(workspaces.pairCount) + " workspaces"); + + for (int i = 0; i < workspaces.pairCount; i++) { + projects = toggl.getProjects(workspaces.KVPairs[i].id); + if (projects.HTTPCode >= 200 && projects.HTTPCode <= 226) { + Serial.println("There are " + String(projects.pairCount) + " projects in workspace " + workspaces.KVPairs[i].name); + for (int i = 0; i < projects.pairCount; i++) { + Serial.println(String(projects.KVPairs[i].id) + ": " + projects.KVPairs[i].name); + } + projects.KVPairs = NULL; // set to null here, as we plan to use this again later. + } + else { + Serial.println("Failed to get projects with HTTPCode " + String(projects.HTTPCode)); + } + } + } + else { + Serial.println("Failed to get workspaces with HTTPCode " + String(workspaces.HTTPCode)); + } + + delete[] workspaces.KVPairs; // we're done with the workspace KVPairs + delete[] projects.KVPairs; // we're done with the project KVPairs + +} + +void loop() { +} diff --git a/src/Toggl.h b/src/Toggl.h index 5b75263..4d8c5c5 100644 --- a/src/Toggl.h +++ b/src/Toggl.h @@ -32,10 +32,24 @@ # endif +// Key/Value Pair +struct KVPair { + String name; + int id; +}; + +// A Key/Value return struct, including HTTP code for readability/ +// This is essentially a std::arr, but there's not support for that in the default Arduino libs +struct KVReturn { + int HTTPCode; + int pairCount; + KVPair * KVPairs; +}; + class Toggl{ public: Toggl(); - + //Get the induvidual account settings/data const uint16_t getID(); const String getApiToken(); @@ -55,8 +69,9 @@ class Toggl{ const String getTimezone(); //Misc - const String getWorkSpace(); - const String getProject(int const& WID); + KVReturn getWorkSpaces(); + KVReturn getProjects(int const& WID); + KVReturn getTags(int const& WID); //const int getPID(String const& WID ,String const& ProjectName); const String CreateTag(String const& Name, int const& WID); @@ -73,11 +88,13 @@ class Toggl{ void setAuth(String const& Token); private: - const uint32_t getCurrentTime(const String Timezone); - const String getUserData(String Input); - const String getTimerData(String Input); - String AuthorizationKey{}; - const char* Fingerprint{"41c40c6a907d364b26d40d40d24f0c1b42f126da"}; // Fingerprint valid until 22 April 2021 + + void getKVPairs(String const URL, KVReturn &data); + const uint32_t getCurrentTime(const String Timezone); + const String getUserData(String Input); + const String getTimerData(String Input); + String AuthorizationKey{}; + const char* Fingerprint{"dac8ac00a1aab269af2149ddb4bd5c881a1a9613"}; // Fingerprint valid until 18 June 2021 const char* root_ca = \ "-----BEGIN CERTIFICATE-----\n"\ "MIIESjCCAzKgAwIBAgINAeO0nXfN9AwGGRa24zANBgkqhkiG9w0BAQsFADBMMSAw\n"\ diff --git a/src/Toggl_ESP32.cpp b/src/Toggl_ESP32.cpp index 5a05863..326f06c 100644 --- a/src/Toggl_ESP32.cpp +++ b/src/Toggl_ESP32.cpp @@ -201,8 +201,30 @@ const String Toggl::CreateTag(String const& Name, int const& WID){ } } + +KVReturn Toggl::getWorkSpaces() { + KVReturn returnData{}; + String URL = BaseUrl + "/workspaces"; + getKVPairs(URL, returnData); + return returnData; +} + +KVReturn Toggl::getProjects(int const &WID) { + KVReturn returnData{}; + String URL = BaseUrl + "/workspaces/" + String(WID) + "/projects"; + getKVPairs(URL, returnData); + return returnData; +} + +KVReturn Toggl::getTags(int const &WID) { + KVReturn returnData{}; + String URL = BaseUrl + "/workspaces/" + String(WID) + "/tags"; + getKVPairs(URL, returnData); + return returnData; +} + //Returns Workplace ID (WID) -const String Toggl::getWorkSpace(){ +void Toggl::getKVPairs(String const URL, KVReturn &data){ if ((WiFi.status() == WL_CONNECTED)) { @@ -210,7 +232,7 @@ const String Toggl::getWorkSpace(){ uint16_t HTTP_Code{}; HTTPClient https; - https.begin(BaseUrl + "/workspaces", root_ca); + https.begin(URL, root_ca); https.addHeader("Authorization", AuthorizationKey, true); HTTP_Code = https.GET(); @@ -218,86 +240,35 @@ const String Toggl::getWorkSpace(){ if(HTTP_Code >= 200 && HTTP_Code <= 226){ DynamicJsonDocument doc(1024); - StaticJsonDocument<50> filter; filter[0]["id"] = true; filter[0]["name"] = true; deserializeJson(doc, https.getString(), DeserializationOption::Filter(filter)); - - JsonArray arr = doc.as(); - - for (JsonVariant value : arr) { - - const int TmpID{value["id"]}; - Output += TmpID; - Output += "\n"; + + JsonArray arr = doc.as(); + + data = (KVReturn){HTTP_Code, arr.size(), new KVPair[arr.size()]}; + int i = 0; + for (JsonVariant value : arr) { + // directly assigning value["name"] doesn't work String TmpName = value["name"]; - Output += TmpName + "\n" + "\n"; - + data.KVPairs[i].name = TmpName; + data.KVPairs[i].id = int(value["id"]); + i++; } doc.garbageCollect(); filter.garbageCollect(); } - else{ - Output = ("Error: " + String(HTTP_Code)); + data = (KVReturn){HTTP_Code, 0, NULL}; } https.end(); - return Output; - - } -} - - -const String Toggl::getProject(int const& WID){ - - if ((WiFi.status() == WL_CONNECTED)) { - - String Output{}; - uint16_t HTTP_Code{}; - - DynamicJsonDocument doc(1024); - - StaticJsonDocument<50> filter; - filter[0]["id"] = true; - filter[0]["name"] = true; - HTTPClient https; - https.begin("https://api.track.toggl.com/api/v8/workspaces/" + String(WID) + "/projects", root_ca); - https.addHeader("Authorization", AuthorizationKey, true); - - HTTP_Code = https.GET(); - - if(HTTP_Code >= 200 && HTTP_Code <= 226){ - - deserializeJson(doc, https.getString(), DeserializationOption::Filter(filter)); - - - JsonArray arr = doc.as(); - - - for (JsonVariant value : arr) { - - const int TmpID{value["id"]}; - Output += TmpID; - Output += "\n"; - String TmpName = value["name"]; - Output += TmpName + "\n" + "\n"; - - } - doc.garbageCollect(); - filter.garbageCollect(); - } - - else{ - Output = ("Error: " + String(HTTP_Code)); - } - - https.end(); - return Output; - } + else { + data = (KVReturn){-1, 0, NULL}; + } } /* diff --git a/src/Toggl_ESP8266.cpp b/src/Toggl_ESP8266.cpp index 939ff00..136bc8d 100644 --- a/src/Toggl_ESP8266.cpp +++ b/src/Toggl_ESP8266.cpp @@ -228,114 +228,81 @@ const String Toggl::CreateTag(String const& Name, int const& WID){ } -const String Toggl::getWorkSpace(){ - - if ((WiFi.status() == WL_CONNECTED)) { - - String Output{}; - uint16_t HTTP_Code{}; - uint8_t Counter{}; - - std::unique_ptrclient(new BearSSL::WiFiClientSecure); - client->setFingerprint(Fingerprint); - - - - HTTPClient https; - https.begin(*client, BaseUrl + "/workspaces"); - https.addHeader("Authorization", AuthorizationKey, true); - - HTTP_Code = https.GET(); - - if(HTTP_Code >= 200 && HTTP_Code <= 226){ +KVReturn Toggl::getWorkSpaces() { + KVReturn returnData{}; + String URL = BaseUrl + "/workspaces"; + getKVPairs(URL, returnData); + return returnData; +} - DynamicJsonDocument doc(1024); - StaticJsonDocument<50> filter; - filter[0]["id"] = true; - filter[0]["name"] = true; - - deserializeJson(doc, https.getString(), DeserializationOption::Filter(filter)); - - JsonArray arr = doc.as(); +KVReturn Toggl::getProjects(int const &WID) { + KVReturn returnData{}; + String URL = BaseUrl + "/workspaces/" + String(WID) + "/projects"; + getKVPairs(URL, returnData); + return returnData; +} - for (JsonVariant value : arr) { - - const int TmpID{value["id"]}; - Output += TmpID; - Output += "\n"; - String TmpName = value["name"]; - Output += TmpName + "\n" + "\n"; +KVReturn Toggl::getTags(int const &WID) { + KVReturn returnData{}; + String URL = BaseUrl + "/workspaces/" + String(WID) + "/tags"; + getKVPairs(URL, returnData); + return returnData; +} - } - doc.garbageCollect(); - filter.garbageCollect(); - } +void Toggl::getKVPairs(String const URL, KVReturn &data) { + + if ((WiFi.status() == WL_CONNECTED)) { - else{ - Output = ("Error: " + String(HTTP_Code)); - } + String Output{}; + uint16_t HTTP_Code{}; - https.end(); - return Output; + std::unique_ptr client(new BearSSL::WiFiClientSecure); + client->setFingerprint(Fingerprint); - } -} + HTTPClient https; + https.begin(*client, URL); + https.addHeader("Authorization", AuthorizationKey, true); + HTTP_Code = https.GET(); -// Need to solve the address problem.. -const String Toggl::getProject(int const& WID){ + if (HTTP_Code >= 200 && HTTP_Code <= 226) { - - if ((WiFi.status() == WL_CONNECTED)) { - - String Output{}; - uint16_t HTTP_Code{}; - DynamicJsonDocument doc(1024); - StaticJsonDocument<50> filter; filter[0]["id"] = true; filter[0]["name"] = true; - std::unique_ptrclient(new BearSSL::WiFiClientSecure); - client->setFingerprint(Fingerprint); - - HTTPClient https; - https.begin(*client, BaseUrl + "/workspaces/" + String(WID) + "/projects"); - https.addHeader("Authorization", AuthorizationKey); + deserializeJson(doc, https.getString(),DeserializationOption::Filter(filter)); - HTTP_Code = https.GET(); + JsonArray arr = doc.as(); - if(HTTP_Code >= 200 && HTTP_Code <= 226){ - - deserializeJson(doc, https.getString(), DeserializationOption::Filter(filter)); - - JsonArray arr = doc.as(); - - for (JsonVariant value : arr){ - - const int TmpID{value["id"]}; - Output += TmpID; - Output += "\n"; - String TmpName = value["name"]; - Output += TmpName + "\n" + "\n"; - } - doc.garbageCollect(); - filter.garbageCollect(); + data = (KVReturn){HTTP_Code, arr.size(), new KVPair[arr.size()]}; + int i = 0; + for (JsonVariant value : arr) { + // directly assigning value["name"] doesn't work + String TmpName = value["name"]; + data.KVPairs[i].name = TmpName; + data.KVPairs[i].id = int(value["id"]); + i++; } + + doc.garbageCollect(); + filter.garbageCollect(); - else{ - Output = ("Error: " + String(HTTP_Code)); - } - - https.end(); - return Output; - - } - + } + else { + data = (KVReturn){HTTP_Code, 0, NULL}; + } + + https.end(); + } + else { + data = (KVReturn){-1, 0, NULL}; + } } + const String Toggl::getTimerData(String Input){ if ((WiFi.status() == WL_CONNECTED)) {