From fa9df0c48fe811c91e4e2d8ec9ce2c3fbb547e87 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Sat, 12 Sep 2015 20:45:28 -0400 Subject: [PATCH 01/19] updating C++ READMEs --- C++/README.md | 57 ++++++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/C++/README.md b/C++/README.md index 60c6157..88cb4e3 100644 --- a/C++/README.md +++ b/C++/README.md @@ -1,23 +1,16 @@ C++ Example Code ================ -It is recommended that you compile any projects using the iSENSE C++ code in a Unix like system, -either Linux or Mac OS. +It is recommended that you compile any projects using the iSENSE C++ API code using either Linux or Mac OS X. The API code works in windows but it is not as easy to setup and requires using Visual Studios. -The code does work in Visual Studios but it is not trivial to set up. +Some of these examples use cURL through the libcURL library for C. Below is the setup guide for various operating systems. -Setup ------- -Some of these examples use cURL through the libcURL library for C. - -Linux ------ +##Linux You can get libcurl on Linux (Ubuntu/Debian) by running the following commands: ``` -sudo apt-get install curl -sudo apt-get install libcurl4-gnutls-dev sudo apt-get update +sudo apt-get install curl libcurl4-gnutls-dev ``` If those prerequisites are not installed, you may see the following error: @@ -36,44 +29,38 @@ Example: g++ GET_basic_curl.cpp -lcurl ``` -Windows -------- -To compile in Windows, you will need to use Visual Studios Express 2013 (Professional / Community versions should also work). -See the "Windows" directory for more information. An example project is provided - however, you will need to change -the paths of the curl directory and the lib directory. - -To download Visual Studios 2013, [go to the following website.](http://www.visualstudio.com/) - -MacOS ------ +##MacOS X MacOS users should have curl / libcurl installed by default. -You will however need to add "-lcurl" to the Other Linker Flags. +You will however need to add "-lcurl" to the Other Linker Flags in Xcode or when using GCC. This can be found by going to build settings within an Xcode project, and then clicking on "All" and scrolling down until you see an option for Other Linker Flags. Here you can type "-lcurl" and add the API files (api.cpp as well as the "include" directory) to the Xcode project. For more help with Xcode, [see the following site.](http://docs.millennialmedia.com/iOS-SDK/iOSAddingLinkerFlag.html) -You can also check out the example project in the "Mac" directory in the Github repo. +You can also check out the example project in the "Mac" directory in the [Teaching Github repo](https://github.com/isenseDev/Teaching) (ExampleCode / C++ / Projects / Mac). You should also be able to use the provided Makefile for Linux. -Confirmed working in Windows 7, 8.1 (x64), Mac OS X 10.10.1 Yosemite and Ubuntu 14.04LTS (x64) +##Windows +To compile in Windows, you will need to use Visual Studios Express 2013 (Professional / Community versions should also work). +See the "Windows" directory for more information. An example project is provided - however, you will need to change +the paths of the curl directory and the lib directory. +To download Visual Studios 2013, [go to the following website.](http://www.visualstudio.com/) + +##Testing +Confirmed working in Windows 7, 8.1 (x64), Mac OS X 10.10.1 Yosemite and Ubuntu 14.04LTS (x64). -NOTES: ------- -For some of these programs, I use a JSON library called picojson to serialize and parse JSON. -[You can grab the code for that off the following github repo.](https://github.com/kazuho/picojson) -I have also included it in this GitHub Repository, inside the Projects directory in a -zip called "include.zip". Unzip that zip and you will have picojson, -as well as memfile.h and API.h. +##NOTES: +For some of these programs, a JSON library called picojson is used to serialize and parse JSON. +[You can grab the code for that off the following github repo.](https://github.com/kazuho/picojson) -You will likely want to check this repo for updates to API.h / API.cpp, as those files contain -the iSENSE class that provides API wrappers for using iSENSE APIs in C++. +It is also included it in the iSENSE Teaching GitHub Repository, inside the Projects directory in a +zip called "include.zip". Unzip that zip and you will have picojson, as well as memfile.h and API.h. -[The one header file you need can be found here.](https://raw.githubusercontent.com/kazuho/picojson/master/picojson.h) +[The latest PicoJSON can be found here.](https://raw.githubusercontent.com/kazuho/picojson/master/picojson.h) You can also just git clone the repository using the following command: @@ -81,4 +68,4 @@ You can also just git clone the repository using the following command: git clone https://github.com/kazuho/picojson.git ``` -Note that picojson is just one file, a .h that you will need to include when compiling. \ No newline at end of file +Picojson is contained in one header file called "picojson.h". \ No newline at end of file From 8603f0d7fbce9aeb7653e18ebe002111e0cd024f Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Sat, 12 Sep 2015 20:53:03 -0400 Subject: [PATCH 02/19] adding picojson as a submodule --- .gitmodules | 3 +++ C++/API/picojson | 1 + C++/README.md | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 160000 C++/API/picojson diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..14727eb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "C++/API/picojson"] + path = C++/API/picojson + url = git@github.com:kazuho/picojson.git diff --git a/C++/API/picojson b/C++/API/picojson new file mode 160000 index 0000000..cc130fb --- /dev/null +++ b/C++/API/picojson @@ -0,0 +1 @@ +Subproject commit cc130fbcb165ffbc2aa7a7fadfe98ff6ed4120c8 diff --git a/C++/README.md b/C++/README.md index 88cb4e3..db87685 100644 --- a/C++/README.md +++ b/C++/README.md @@ -1,4 +1,4 @@ -C++ Example Code +C++ API Code ================ It is recommended that you compile any projects using the iSENSE C++ API code using either Linux or Mac OS X. The API code works in windows but it is not as easy to setup and requires using Visual Studios. From d6f7a77a618b54b3662b2ce1f086e2958b20def4 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Sat, 12 Sep 2015 20:57:50 -0400 Subject: [PATCH 03/19] moving picojson submodule --- .gitmodules | 3 --- C++/API/picojson | 1 - 2 files changed, 4 deletions(-) delete mode 160000 C++/API/picojson diff --git a/.gitmodules b/.gitmodules index 14727eb..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "C++/API/picojson"] - path = C++/API/picojson - url = git@github.com:kazuho/picojson.git diff --git a/C++/API/picojson b/C++/API/picojson deleted file mode 160000 index cc130fb..0000000 --- a/C++/API/picojson +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cc130fbcb165ffbc2aa7a7fadfe98ff6ed4120c8 From d997c1ef55d6a7dc188b02abd1dad465eaa98ce6 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Sat, 12 Sep 2015 20:59:24 -0400 Subject: [PATCH 04/19] adding picojson as a submodule to make it easier for users to use. --- .gitmodules | 3 +++ C++/API/include/picojson | 1 + 2 files changed, 4 insertions(+) create mode 160000 C++/API/include/picojson diff --git a/.gitmodules b/.gitmodules index e69de29..915eb91 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "C++/API/include/picojson"] + path = C++/API/include/picojson + url = git@github.com:kazuho/picojson.git diff --git a/C++/API/include/picojson b/C++/API/include/picojson new file mode 160000 index 0000000..cc130fb --- /dev/null +++ b/C++/API/include/picojson @@ -0,0 +1 @@ +Subproject commit cc130fbcb165ffbc2aa7a7fadfe98ff6ed4120c8 From 27278ea2d5718e9248ccc593c702d053e6546446 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Sat, 12 Sep 2015 21:01:44 -0400 Subject: [PATCH 05/19] fixing API.h --- C++/API/Makefile | 4 ++-- C++/API/include/API.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/C++/API/Makefile b/C++/API/Makefile index 533e0a1..e48fa2d 100644 --- a/C++/API/Makefile +++ b/C++/API/Makefile @@ -13,11 +13,11 @@ all: tests.out tests.out: tests.o API.o $(CC) tests.o API.o -o tests.out $(CFLAGS) $(Boost) -tests.o: tests.cpp include/API.h include/memfile.h include/picojson.h +tests.o: tests.cpp include/API.h include/memfile.h include/picojson/picojson.h $(CC) -c tests.cpp $(CFLAGS) # API code -API.o: API.cpp include/API.h include/memfile.h include/picojson.h +API.o: API.cpp include/API.h include/memfile.h include/picojson/picojson.h $(CC) -c API.cpp $(CFLAGS) clean: diff --git a/C++/API/include/API.h b/C++/API/include/API.h index 68e7c4b..09e34b9 100644 --- a/C++/API/include/API.h +++ b/C++/API/include/API.h @@ -10,7 +10,7 @@ #include #endif -#include "picojson.h" +#include "picojson/picojson.h" #include "memfile.h" #include #include From 5f9bc3fc253993ca162ce84cdd8b497b8a51e75a Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Sat, 12 Sep 2015 21:08:03 -0400 Subject: [PATCH 06/19] updating README to detail how to install boost for unit testing. --- C++/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/C++/README.md b/C++/README.md index db87685..edf1581 100644 --- a/C++/README.md +++ b/C++/README.md @@ -13,6 +13,14 @@ sudo apt-get update sudo apt-get install curl libcurl4-gnutls-dev ``` +In order to run the makefile included in the API directory, you will need to install the Boost unit testing library. You can either find this library at the Boost website or in a Debian based Linux distribution just run the following command: + +(note: requires 100MB of available storage space) + +``` +sudo apt-get install libboost-test-dev +``` + If those prerequisites are not installed, you may see the following error: ``` From 2d4c5a7bf0974b7343b869b6dea77eefadbc8acf Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Sat, 12 Sep 2015 21:19:15 -0400 Subject: [PATCH 07/19] updating documentation some more. --- C++/API/README.md | 40 ++++++++++++++++++---------------------- C++/README.md | 17 +++++------------ 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/C++/API/README.md b/C++/API/README.md index 34e2d90..e3c354a 100644 --- a/C++/API/README.md +++ b/C++/API/README.md @@ -1,38 +1,34 @@ -API Source +C++ API Source ========== API source code for iSENSE related projects in C++. -You will need to make a project in either Visual Studios / Xcode or some other IDE, -or use Linux and the included makefile as well as a text editor. +##Linux & Mac OS X command line setup -Required to use the API in a C++ program: - -1. API.cpp -> This file contains the class function definitions. Look through it and read the +1. API.cpp: This file contains the API class functions. Look through it and read the comments to understand what they do and how they can be used. -2. A main file -> You can check out some of the example mains (GET_search.cpp, POST_email.cpp, etc) -so that you can make your own program that uses the API. +2. The include directory: This should contain API.h, memfile.h and a submodule (directory) named picojson. +The memfile.h and the picojson library are used along with libcurl to make HTTP GET / POST requests to iSENSE's +REST API. I suggest looking through API.h for the iSENSE class declaration. +It provides a simple overview - more detail can be found in the API.cpp file. -3. An include directory -> This should contain API.h, memfile.h and ** picojson.h. ** memfile.h and -picojson.h are used along with libcurl to make HTTP requests GETing / POSTing JSON to iSENSE's -RESTFUL API. I only suggest looking through API.h for the iSENSE class declaration. -Note: picojson.h is not in this github repo. You should either -[download the include.zip here](https://github.com/JasonD94/Teaching/blob/jd_data_sets/ExampleCode/C%2B%2B/Projects/include.zip) -here by clicking on Raw, then unzip it and copy the picojson.h file into your code directory, -or -[go to the following link](https://raw.githubusercontent.com/kazuho/picojson/master/picojson.h) -to find a recent copy of picojson. Right click and save the file as a header file inside your -code directory. +3. A main file: You can check out some of the example mains (GET_search.cpp, POST_email.cpp, etc) in the +[iSENSE Teaching Github repo](https://github.com/isenseDev/Teaching) +so that you can make your own program that uses the API. -4. A makefile - one is provided for testing. Simply add on to it to compile your program. +4. A makefile: one is provided for testing. Simply add on to it to compile your program. -Also see the notes in the C++ directory's README and look through the Projects directory -for example projects in Visual Studios and Xcode. +##Windows & Mac OS X IDE Setup +See the example projects in the [iSENSE Teaching Github repo](https://github.com/isenseDev/Teaching). +(Teaching / ExampleCode / C++ / Projects) -To run the tests file in Linux, I suggest the following commands: +##API Testing +To test the API, run the provided Makefile. You can use the following commands: +``` make ./tests.out > stdout.txt 2> stderr.txt +``` This will place all the standard IO into a text file called stdout.txt, and all of the standard errors will end up in a text file called stderr.txt. diff --git a/C++/README.md b/C++/README.md index edf1581..de0fae1 100644 --- a/C++/README.md +++ b/C++/README.md @@ -46,7 +46,8 @@ add the API files (api.cpp as well as the "include" directory) to the Xcode proj For more help with Xcode, [see the following site.](http://docs.millennialmedia.com/iOS-SDK/iOSAddingLinkerFlag.html) -You can also check out the example project in the "Mac" directory in the [Teaching Github repo](https://github.com/isenseDev/Teaching) (ExampleCode / C++ / Projects / Mac). +You can also check out the example project in the "Mac" directory in the +[iSENSE Teaching Github repo](https://github.com/isenseDev/Teaching) (ExampleCode / C++ / Projects / Mac). You should also be able to use the provided Makefile for Linux. @@ -63,17 +64,9 @@ Confirmed working in Windows 7, 8.1 (x64), Mac OS X 10.10.1 Yosemite and Ubuntu ##NOTES: For some of these programs, a JSON library called picojson is used to serialize and parse JSON. -[You can grab the code for that off the following github repo.](https://github.com/kazuho/picojson) - -It is also included it in the iSENSE Teaching GitHub Repository, inside the Projects directory in a -zip called "include.zip". Unzip that zip and you will have picojson, as well as memfile.h and API.h. - [The latest PicoJSON can be found here.](https://raw.githubusercontent.com/kazuho/picojson/master/picojson.h) -You can also just git clone the repository using the following command: - -``` -git clone https://github.com/kazuho/picojson.git -``` +It is also included it in the iSENSE API repository as a submodule, so if you git clone the API repository it will automatically +git clone the picojson repository. -Picojson is contained in one header file called "picojson.h". \ No newline at end of file +Picojson is contained in one header file called "picojson.h". From c6b51e92cc026d850bc40f1101cc6106b97c6dc1 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Sat, 12 Sep 2015 23:28:46 -0400 Subject: [PATCH 08/19] only two tests failing now. --- C++/API/API.cpp | 7 +- C++/API/README.md | 6 ++ C++/API/tests.cpp | 161 +++++++++++++++++++++------------------------- 3 files changed, 84 insertions(+), 90 deletions(-) diff --git a/C++/API/API.cpp b/C++/API/API.cpp index d6fb4e0..138bdb5 100644 --- a/C++/API/API.cpp +++ b/C++/API/API.cpp @@ -949,8 +949,7 @@ bool iSENSE::append_key_byName(std::string dataset_name) { // If we didn't get any errors, call the append by ID function. if (dataset_ID != GET_ERROR) { - append_key_byID(dataset_ID); - return true; + return append_key_byID(dataset_ID); } // If we got here, we failed to find that dataset name in the current project. @@ -1172,8 +1171,7 @@ bool iSENSE::append_email_byName(std::string dataset_name) { // If we didn't get any errors, call the append by ID function. if (dataset_ID != GET_ERROR) { - append_key_byID(dataset_ID); - return true; + return append_email_byID(dataset_ID); } // If we got here, we failed to find that dataset name in the current project. @@ -1201,6 +1199,7 @@ void iSENSE::format_upload_string(int post_type) { case APPEND_KEY: upload_data["contribution_key"] = value(contributor_key); + upload_data["contributor_name"] = value(contributor_label); upload_data["id"] = value(dataset_ID); break; diff --git a/C++/API/README.md b/C++/API/README.md index e3c354a..491d6c5 100644 --- a/C++/API/README.md +++ b/C++/API/README.md @@ -32,3 +32,9 @@ make This will place all the standard IO into a text file called stdout.txt, and all of the standard errors will end up in a text file called stderr.txt. + +You can also use the following command to redirect all of Boost's error output to a file: + +``` +./tests.out --log_sink=fileName.log +``` \ No newline at end of file diff --git a/C++/API/tests.cpp b/C++/API/tests.cpp index 8132439..41eb18c 100644 --- a/C++/API/tests.cpp +++ b/C++/API/tests.cpp @@ -32,6 +32,20 @@ using namespace picojson; */ +// Constants for running C++ Tests +const std::string test_project_ID = "1406"; +const std::string test_project_name = "C++ Testing "; +const std::string test_dataset_name = "Testing "; +const std::string test_dataset_ID_email = "11366"; +const std::string test_dataset_ID_key = "11367"; +const std::string test_email = "j@j.j"; +const std::string test_password = "j"; +const std::string test_project_key = "123"; +const std::string test_project_label = "Boost"; +const std::string test_search_true = "test"; +const std::string test_search_empty = "abcdefghig"; + + /* * This is a derived class to quickly test the append_byID functions. * It is derived from iSENSE, and by doing this I can create a public function @@ -54,42 +68,38 @@ class Test: public iSENSE { }; -// Ensure the CheckUser() method works. +// Test the Check User method. BOOST_AUTO_TEST_CASE(get_check_user) { iSENSE test; // Add test project - test.set_project_ID("929"); - test.set_project_title("Boost " + test.generate_timestamp()); + test.set_project_ID(test_project_ID); + test.set_project_title(test_dataset_name + test.generate_timestamp()); // This is a valid email / password - so it should return true. - BOOST_REQUIRE(test.set_email_password("j@j.j", "j") == true); + BOOST_REQUIRE(test.set_email_password(test_email, test_password) == true); } // Test the GET project fields method. BOOST_AUTO_TEST_CASE(get_project_fields) { - iSENSE test_true("929", "test ", "BOOST Test", "123"); - // Make the title unique - test_true.set_project_title("Boost " + test_true.generate_timestamp()); - - // Test the GET method, should work for this project ID + // This iSENSE object should work. + iSENSE test_true(test_project_ID, test_project_name, test_project_label, test_project_key); BOOST_REQUIRE(test_true.get_project_fields() == true); + // This one should fail. iSENSE test_false; - - // This should fail. BOOST_REQUIRE(test_false.get_project_fields() == false); } // Test the get_datasets / media objects method BOOST_AUTO_TEST_CASE(get_datasets_and_mediaobjects) { - iSENSE test_true("929", "test ", "BOOST Test", "123"); + iSENSE test_true(test_project_ID, test_project_name, test_project_label, test_project_key); // Make the title unique - test_true.set_project_title("Boost " + test_true.generate_timestamp()); + test_true.set_project_title(test_dataset_name + test_true.generate_timestamp()); // Test the GET method, should work for this project ID BOOST_REQUIRE(test_true.get_datasets_and_mediaobjects() == true); @@ -103,23 +113,19 @@ BOOST_AUTO_TEST_CASE(get_datasets_and_mediaobjects) { // Test the search project method BOOST_AUTO_TEST_CASE(get_projects_search) { - iSENSE test_true("929", "test ", "BOOST Test", "123"); - - std::string search_term = "hello"; + iSENSE test_true(test_project_ID, test_project_name, test_project_label, test_project_key); std::vector project_titles; - project_titles = test_true.get_projects_search(search_term); + project_titles = test_true.get_projects_search(test_search_true); - BOOST_REQUIRE(project_titles.empty() == false); + BOOST_REQUIRE(project_titles.empty() == false); // This should not be empty. iSENSE test_false; - search_term = EMPTY; - project_titles.clear(); - project_titles = test_false.get_projects_search(search_term); + project_titles = test_false.get_projects_search(test_search_empty); - BOOST_REQUIRE(project_titles.empty() == true); + BOOST_REQUIRE(project_titles.empty() == true); // This should be empty, since its a blank search term. } @@ -151,7 +157,7 @@ BOOST_AUTO_TEST_CASE(get_dataset_ID) { // Try and get dataset ID for the given dataset name. std::string datasetID = test_true.get_dataset_ID("MLB Team Statistics 2013"); - BOOST_REQUIRE(datasetID == "1190"); + BOOST_REQUIRE(datasetID == "2801"); iSENSE test_false; @@ -185,12 +191,13 @@ BOOST_AUTO_TEST_CASE(get_field_ID) { } -// Test POSTing with Contributor keys +// Test POST with Contributor keys BOOST_AUTO_TEST_CASE(post_JSON_withKey) { - iSENSE test("929", "test ", "BOOST Test", "123"); + std::string project_title = "POST Test with Key "; + iSENSE test(test_project_ID, project_title, test_project_label, test_project_key); // Make the title unique - test.set_project_title("Boost " + test.generate_timestamp()); + test.set_project_title(project_title + test.generate_timestamp()); // Push some data back test.push_back("Timestamp", test.generate_timestamp()); @@ -201,13 +208,15 @@ BOOST_AUTO_TEST_CASE(post_JSON_withKey) { } -// Test POSTing with Email/Password +// Test POST with Email/Password BOOST_AUTO_TEST_CASE(post_JSON_withEmail) { - iSENSE test("929", "test ", "BOOST Test", "123"); + std::string project_title = "POST Test with Email "; + iSENSE test; // Make the title unique + set email/password - test.set_project_title("Boost " + test.generate_timestamp()); - test.set_email_password("j@j.j", "j"); + test.set_project_ID(test_project_ID); + test.set_project_title(project_title + test.generate_timestamp()); + test.set_email_password(test_email, test_password); // Push some data back test.push_back("Timestamp", test.generate_timestamp()); @@ -222,26 +231,19 @@ BOOST_AUTO_TEST_CASE(post_JSON_withEmail) { // (Email / Password) BOOST_AUTO_TEST_CASE(append_withDatasetID_byEmail) { Test test; - std::string title, ID, dataset_ID, email, password, letters, num, timestamp; - - // This will be a test of the append method. - ID = "929"; - title = "valid"; - email = "j@j.j"; - password = "j"; - dataset_ID = "7659"; + std::string test_dataset_name_email = "C++ Dataset Append Test Email"; // Add project info / dataset info to the object - test.set_project_ID(ID); - test.check_set_dataset_ID(dataset_ID); - test.set_project_title(title); - test.set_email_password(email, password); + test.set_project_ID(test_project_ID); + test.set_project_title(test_dataset_name_email); + test.check_set_dataset_ID(test_dataset_ID_email); + test.set_email_password(test_email, test_password); test.push_back("Number", "123456789"); - test.push_back("Text", "THIS"); + test.push_back("Text", "Datset ID Test - Email"); test.push_back("Timestamp", test.generate_timestamp()); - BOOST_REQUIRE(test.check_append_email_byID(dataset_ID) == true); + BOOST_REQUIRE(test.check_append_email_byID(test_dataset_ID_email) == true); } @@ -249,26 +251,19 @@ BOOST_AUTO_TEST_CASE(append_withDatasetID_byEmail) { // (Email / Password) BOOST_AUTO_TEST_CASE(append_withDatasetName_byEmail) { iSENSE test; - std::string title, ID, dataset_name, email, password, letters, num, timestamp; - - // This will be a test of the append method. - ID = "929"; - title = "valid"; - email = "j@j.j"; - password = "j"; - dataset_name = "testing"; + std::string test_dataset_name_email = "C++ Dataset Append Test Email"; // Add project info / dataset info to the object - test.set_project_ID(ID); - test.set_project_title(title); - test.set_email_password(email, password); + test.set_project_ID(test_project_ID); + test.set_project_title(test_dataset_name_email); + test.set_email_password(test_email, test_password); // Push data back to the object. test.push_back("Number", "123456789"); - test.push_back("Text", "THIS"); + test.push_back("Text", "Dataset Name Test - Email"); test.push_back("Timestamp", test.generate_timestamp()); - BOOST_REQUIRE(test.append_email_byName(dataset_name) == true); + BOOST_REQUIRE(test.append_email_byName(test_dataset_name_email) == true); } @@ -276,25 +271,24 @@ BOOST_AUTO_TEST_CASE(append_withDatasetName_byEmail) { // (Contributor keys) BOOST_AUTO_TEST_CASE(append_withDatasetID_byKey) { Test test; - std::string title, ID, dataset_ID, key, letters, num, timestamp; - - // This will be a test of the append method. - title = "this works?"; - ID = "1029"; - key = "key"; - dataset_ID = "7795"; + std::string test_dataset_name_key = "C++ Dataset Append Test Key"; // Add project info / dataset info to the object - test.set_project_ID(ID); - test.check_set_dataset_ID(dataset_ID); - test.set_project_title(title); - test.set_contributor_key(key); + test.set_project_ID(test_project_ID); + test.set_project_title(test_project_name); + test.check_set_dataset_ID(test_dataset_ID_key); + test.set_project_label(test_project_label); + test.set_contributor_key(test_project_key); - timestamp = test.generate_timestamp(); + // Push data back to the object. + test.push_back("Number", "123456789"); + test.push_back("Text", "DatasetID Test --- Key"); + test.push_back("Timestamp", test.generate_timestamp()); - test.push_back("Number", "999999"); + BOOST_REQUIRE(test.check_append_key_byID(test_dataset_ID_key) == true); - BOOST_REQUIRE(test.check_append_key_byID(dataset_ID) == true); + // Dump some data for testing + test.debug(); } @@ -302,26 +296,21 @@ BOOST_AUTO_TEST_CASE(append_withDatasetID_byKey) { // (Contributor keys) BOOST_AUTO_TEST_CASE(append_withDatasetName_byKey) { iSENSE test; - std::string title, ID, dataset_name, label, key; - - // This will be a test of the append method. - ID = "1029"; - title = "test"; - label = "BOOST Test"; - key = "key"; - dataset_name = "this works?"; + std::string test_dataset_name_key = "C++ Dataset Append Test Key"; // Add project info / dataset info to the object - test.set_project_ID(ID); - test.set_project_title(title); - test.set_project_label(label); - test.set_contributor_key(key); + test.set_project_ID(test_project_ID); + test.set_project_title(test_project_name); + test.set_project_label(test_project_label); + test.set_contributor_key(test_project_key); // Push data back to the object. test.push_back("Number", "123456789"); - test.push_back("Text", "THIS"); + test.push_back("Text", "Dataset Name Test -- Key"); test.push_back("Timestamp", test.generate_timestamp()); - BOOST_REQUIRE(test.append_key_byName(dataset_name) == true); -} + BOOST_REQUIRE(test.append_key_byName(test_dataset_name_key) == true); + // Dump some data for testing + test.debug(); +} From 65b690f5f45b5322d1d06f9838e4f6a758e6ba3d Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Sun, 13 Sep 2015 00:26:27 -0400 Subject: [PATCH 09/19] appending with keys is still broken. --- C++/API/tests.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/C++/API/tests.cpp b/C++/API/tests.cpp index 41eb18c..275ad0b 100644 --- a/C++/API/tests.cpp +++ b/C++/API/tests.cpp @@ -285,10 +285,10 @@ BOOST_AUTO_TEST_CASE(append_withDatasetID_byKey) { test.push_back("Text", "DatasetID Test --- Key"); test.push_back("Timestamp", test.generate_timestamp()); - BOOST_REQUIRE(test.check_append_key_byID(test_dataset_ID_key) == true); - // Dump some data for testing test.debug(); + + BOOST_REQUIRE(test.check_append_key_byID(test_dataset_ID_key) == true); } @@ -309,8 +309,10 @@ BOOST_AUTO_TEST_CASE(append_withDatasetName_byKey) { test.push_back("Text", "Dataset Name Test -- Key"); test.push_back("Timestamp", test.generate_timestamp()); - BOOST_REQUIRE(test.append_key_byName(test_dataset_name_key) == true); + test.append_key_byName(test_dataset_name_key); // Dump some data for testing test.debug(); + + BOOST_REQUIRE(test.append_key_byName(test_dataset_name_key) == true); } From 2f931d0271d95cc4332f9f3e3e49140ec9206cc1 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Sat, 26 Sep 2015 16:07:57 -0400 Subject: [PATCH 10/19] simplifying stuff is awesome. --- C++/API/API.cpp | 710 ++++++++++---------------------------- C++/API/include/API.h | 8 +- C++/API/include/memfile.h | 4 +- C++/README.md | 9 +- 4 files changed, 187 insertions(+), 544 deletions(-) diff --git a/C++/API/API.cpp b/C++/API/API.cpp index 138bdb5..281c4b7 100644 --- a/C++/API/API.cpp +++ b/C++/API/API.cpp @@ -1,8 +1,6 @@ #include "include/API.h" -// Default constructor -iSENSE::iSENSE() { - // Set these to default values for error checking. +iSENSE::iSENSE() { // Default constructor upload_URL = EMPTY; get_URL = EMPTY; get_UserURL = EMPTY; @@ -14,9 +12,7 @@ iSENSE::iSENSE() { email = EMPTY; password = EMPTY; } - - -// Constructor with parameters + // Constructor with parameters iSENSE::iSENSE(std::string proj_ID, std::string proj_title, std::string label, std::string contr_key) { set_project_ID(proj_ID); @@ -25,7 +21,6 @@ iSENSE::iSENSE(std::string proj_ID, std::string proj_title, set_contributor_key(contr_key); } - // Similar to the constructor with parameters, but can be called at anytime void iSENSE::set_project_all(std::string proj_ID, std::string proj_title, std::string label, std::string contr_key) { @@ -35,7 +30,6 @@ void iSENSE::set_project_all(std::string proj_ID, std::string proj_title, set_contributor_key(contr_key); } - // Set the Project ID, and the upload/get URLs as well. void iSENSE::set_project_ID(std::string proj_ID) { project_ID = proj_ID; @@ -44,137 +38,107 @@ void iSENSE::set_project_ID(std::string proj_ID) { get_project_fields(); } - // The user should also set the project title void iSENSE::set_project_title(std::string proj_title) { title = proj_title; } - -// This one is optional, by default the label will be "cURL". +// This one is optional, by default the label will be "label". void iSENSE::set_project_label(std::string proj_label) { contributor_label = proj_label; } - // As well as the contributor key they will be using void iSENSE::set_contributor_key(std::string proj_key) { contributor_key = proj_key; } - -/* Users should never have to call this method, as it is possible to - * pull datasets and compare dataset names to get the dataset_ID. - * Users should instead use the appendbyName methods. - */ +// Users should not call this and should instead use the appendbyName methods. void iSENSE::set_dataset_ID(std::string proj_dataset_ID) { dataset_ID = proj_dataset_ID; } - // Sets both email & password at once. Checks for valid email / password. bool iSENSE::set_email_password(std::string proj_email, std::string proj_password) { email = proj_email; password = proj_password; - if (get_check_user()) { - std::cout << "\nEmail and password are valid.\n"; - return true; + if (!get_check_user()) { + std::cerr << "\nError in: set_email_password()\n"; + std::cerr << "Your email and password are **not** valid.\n"; + std::cerr << "You also need to have created an account on iSENSE.\n"; + std::cerr << "See: http://rsense-dev.cs.uml.edu/users/new \n\n"; + return false; } - std::cerr << "\nError in: set_email_password()\n"; - std::cerr << "Your email and password are **not** valid.\n"; - std::cerr << "Try entering your password again.\n"; - std::cerr << "You also need to have created an account on iSENSE.\n"; - std::cerr << "See: http://rsense-dev.cs.uml.edu/users/new \n\n"; - - return false; + return true; } - -// Extra function that the user can call to just generate an ISO 8601 timestamp -// It does not push back to the map of vectors. It merely returns a string, -// that users may grab and then send off to the push_back function. +// Function that the user can call to just generate an ISO 8601 timestamp std::string iSENSE::generate_timestamp(void) { time_t time_stamp; time(&time_stamp); char buffer[sizeof "2011-10-08T07:07:09Z"]; - // Generates the timestamp, stores it in buffer. // Timestamp is in the form of Year - Month - Day -- Hour - Minute - Seconds strftime(buffer, sizeof buffer, "%Y-%m-%dT%H:%M:%SZ", gmtime(&time_stamp)); - // Converts char array (buffer) to C++ string - std::string cplusplus_timestamp(buffer); + std::string cplusplus_timestamp(buffer); // Convert char array to C++ string return cplusplus_timestamp; } - // Resets the object and clears the map. void iSENSE::clear_data(void) { upload_URL = EMPTY; get_URL = EMPTY; get_UserURL = EMPTY; title = EMPTY; - project_ID = EMPTY; // Set these to default values + project_ID = EMPTY; // Set these to default values contributor_key = EMPTY; contributor_label = "label"; email = EMPTY; password = EMPTY; - std::cout << "\nClearing the data arrays.\n"; - map_data.clear(); // Clear the map_data - /* Clear the picojson objects: - * object: upload_data, fields_data; - * value: get_data, fields; - * array: fields_array; - */ + // Clear the picojson objects - // Under the hood picojson::objects are STL maps and - // picojson::arrays are STL vectors. + // Under the hood picojson::objects are STL maps and picojson::arrays are STL vectors. upload_data.clear(); fields_data.clear(); owner_info.clear(); - // Uses picojson's = operator to clear - // the get_data object and the fields object. + // Uses picojson's = operator to clear the get_data obj and the fields obj. + // Should check and see if this is bad. value new_object; get_data = new_object; fields = new_object; - // Clear the field array (STL vector) + // Clear the field array (STL vectors) fields_array.clear(); media_objects.clear(); data_sets.clear(); } - -// Adds a string to the map, which keeps track of the data to be uploaded. +// Add one piece of data to the map of data. void iSENSE::push_back(std::string field_name, std::string data) { - // Add the piece of data to the back of the vector with the given field name. map_data[field_name].push_back(data); } - // Add a field name / vector of strings (data) to the map. -void iSENSE::push_vector(std::string field_name, - std::vector data) { +void iSENSE::push_vector(std::string field_name, std::vector data) { // This will store a copy of the vector in the map. // If you decide to add more data, you will need to use the push_back method. map_data[field_name] = data; } - // Searches for projects with the search term. -// Returns a vector with projects that show up. std::vector iSENSE::get_projects_search(std::string search_term) { - std::string get_search = devURL + "/projects?utf8=true&search=" \ - + search_term + "&sort=updated_at&order=DESC"; + + search_term + "&sort=updated_at&order=DESC"; + // Vector of project titles. std::vector project_titles; @@ -296,7 +260,6 @@ std::vector iSENSE::get_projects_search(std::string search_term) { return project_titles; } - // Checks to see if the email / password is valid bool iSENSE::get_check_user() { if (email == EMPTY || password == EMPTY) { @@ -350,7 +313,6 @@ bool iSENSE::get_check_user() { return false; } - // GET the project fields for a given project ID bool iSENSE::get_project_fields() { if (project_ID == EMPTY || project_ID.empty()) { @@ -429,7 +391,6 @@ bool iSENSE::get_project_fields() { return true; } - // Given that a project ID has been set, this function // makes a GET request and saves all the datasets & media objects // into two picojson arrays. @@ -530,7 +491,6 @@ bool iSENSE::get_datasets_and_mediaobjects() { return true; } - // Calls the get_datasets function, returns a vector of the data // Must be given a valid iSENSE dataset name & field name // Must have a project ID set. @@ -647,7 +607,6 @@ std::vector iSENSE::get_dataset(std::string dataset_name, return vector_data; } - std::string iSENSE::get_field_ID(std::string field_name) { // Grab all the fields using an iterator. // Similar to printing them all out below in the debug function. @@ -686,7 +645,6 @@ std::string iSENSE::get_field_ID(std::string field_name) { return GET_ERROR; } - std::string iSENSE::get_dataset_ID(std::string dataset_name) { // Compare the dataset name the user provided with datasets in the project. // Use an iterator to go through all the datasets @@ -718,133 +676,39 @@ std::string iSENSE::get_dataset_ID(std::string dataset_name) { return GET_ERROR; } - -// Call this function to POST data to rSENSE bool iSENSE::post_json_key() { - /* These first couple of if statements perform some basic error checking, - * such as whether or not all the required fields have been set up. - */ - - // Check that the project ID is set properly. - // When the ID is set, the fields are also pulled down as well. - if (project_ID == EMPTY || project_ID.empty()) { - std::cerr << "\nError in method: post_json_key()\n"; - std::cerr << "Please set a project ID!\n"; + if(!empty_project_check(APPEND_EMAIL,"post_json_key()")) { return false; } - // Check that a title and contributor key has been set. - if (title == EMPTY || title.empty()) { - std::cerr << "\nError in method: post_json_key()\n"; - std::cerr << "\nPlease set a project title!\n"; - return false; - } + upload_URL = devURL + "/projects/" + project_ID + "/jsonDataUpload"; + int http_code = post_data_function(POST_KEY); - if (contributor_key == EMPTY || contributor_key.empty()) { - std::cerr << "\nError in method: post_json_key()\n"; - std::cerr << "\nPlease set a contributor key!\n"; + if(!check_http_code(http_code, "post_json_key()")) { return false; } - // If a label wasn't set, automatically set it to "cURL" - if (contributor_label == "label" || contributor_label.empty()) { - contributor_label = "cURL"; - } + return true; +} - // Make sure the map actually has stuff pushed to it. - if (map_data.empty()) { - std::cerr << "\nError in method: post_json_key()\n"; - std::cerr << "Map of keys/data is empty.\n"; - std::cerr << "Please push some data back to this object.\n"; +bool iSENSE::post_json_email() { + if(!empty_project_check(APPEND_EMAIL,"post_json_email()")) { return false; } - // Should make sure each vector is not empty as well, since I had issues - // uploading if any ONE vector was empty. rSENSE complained about a nil class. - upload_URL = devURL + "/projects/" + project_ID + "/jsonDataUpload"; + int http_code = post_data_function(POST_EMAIL); - // Call the POST function, give it type 1 - // since this is a upload JSON by contributor key. - int http_code = post_data_function(POST_KEY); - - /* - * The iSENSE API gives us two response codes to check against: - * Success: 200 OK (iSENSE handled the request fine) - * Failure: 401 Unauthorized (email/pw or contributor key was not valid) - * Failure: 422 Unprocessable Entity (email or contributor key was fine, - * but there was an issue with the request's formatting. - * Something in the formatting caused iSENSE to fail.) - */ - - if (http_code == HTTP_AUTHORIZED) { - std::cout << "\n\nPOST request successfully sent off to iSENSE!\n"; - std::cout << "HTTP Response Code was: " << http_code << "\n"; - std::cout << "The URL to your project is: " << dev_baseURL; - std::cout << "/projects/" << project_ID << "\n"; - return true; - } - - std::cerr << "\n\nError in method: post_json_key()\n"; - std::cerr << "POST request **failed**\n"; - std::cerr << "HTTP Response Code was: " << http_code; - - // Make a function for this! - if (http_code == HTTP_UNAUTHORIZED) { - std::cerr << "Try checking to make sure your contributor key is valid\n"; - std::cerr << "for the project you are trying to contribute to.\n"; - } - if (http_code == HTTP_NOT_FOUND) { - std::cerr << "Unable to find that project ID.\n"; - } - if (http_code == HTTP_UNPROC_ENTRY) { - std::cerr << "Something went wrong with iSENSE.\n"; - std::cerr << "Try formatting your data differently, using a contributor \n"; - std::cerr << "key instead of an email, or asking for help from others. \n"; - std::cerr << "You can also try running the the program with the debug \n"; - std::cerr << "method enabled, by typing: object_name.debug()\n"; - std::cerr << "This will output a ton of data to the console and \n"; - std::cerr << "may help you in debugging your program.\n"; - } - if (http_code == CURL_ERROR) { - std::cerr << "Curl failed for some unknown reason.\n"; - std::cerr << "Make sure you've installed curl / libcurl, and have the \n"; - std::cerr << "picojson header file as well.\n"; + if(!check_http_code(http_code, "post_json_email()")) { + return false; } - return false; + return true; } - -/* Append to a dataset using its dataset_ID. - * The dataset ID can be found on iSENSE by going to a project - * and clicking on a dataset. - * In the future, uploading JSON will return the dataset ID for this function - */ +// Append using a contributor key bool iSENSE::append_key_byID(std::string dataset_ID) { - if (project_ID == EMPTY || project_ID.empty()) { - std::cerr << "\nError in method: append_key_byID()\n"; - std::cerr << "Please set a project ID!\n"; - return false; - } - if (title == EMPTY || title.empty()) { - std::cerr << "\nError in method: append_key_byID()\n"; - std::cerr << "Please set a project title!\n"; - return false; - } - if (contributor_key == EMPTY || contributor_key.empty()) { - std::cerr << "\nError in method: append_key_byID()\n"; - std::cerr << "Please set a contributor key!\n"; - return false; - } - if (contributor_label == "label" || contributor_label.empty()) { - // If a label wasn't set, automatically set it to "cURL" - contributor_label = "cURL"; - } - if (map_data.empty()) { - std::cerr << "\nError in method: append_key_byID()\n"; - std::cerr << "Map of keys/data is empty.\n"; - std::cerr << "You should push some data back to this object.\n"; + if(!empty_project_check(APPEND_EMAIL,"append_key_byID")) { return false; } @@ -858,91 +722,21 @@ bool iSENSE::append_key_byID(std::string dataset_ID) { // since this is an append by contributor key. int http_code = post_data_function(APPEND_KEY); - /* - * The iSENSE API gives us two response codes to check against: - * Success: 200 OK (iSENSE handled the request fine) - * Failure: 401 Unauthorized (email/pw or contributor key was not valid) - * Failure: 422 Unprocessable Entity (email or contributor key was fine, - * but there was an issue with the request's formatting. - * Something in the formatting caused iSENSE to fail.) - */ - - if (http_code == HTTP_AUTHORIZED) { - std::cout << "\n\nPOST request successfully sent off to iSENSE!\n"; - std::cout << "HTTP Response Code was: " << http_code << "\n"; - std::cout << "The URL to your project is: " << dev_baseURL; - std::cout << "/projects/" << project_ID << "\n"; - return true; - } - - std::cerr << "\n\nError in method: append_key_byID()\n"; - std::cerr << "POST request **failed**\n"; - std::cerr << "HTTP Response Code was: " << http_code << "\n"; - - // Make a function for this! - if (http_code == HTTP_UNAUTHORIZED) { - std::cerr << "Try checking to make sure your contributor key is valid\n"; - std::cerr << "for the project you are trying to contribute to.\n"; - } - if (http_code == HTTP_NOT_FOUND) { - std::cerr << "Unable to find that project ID.\n"; - } - if (http_code == HTTP_UNPROC_ENTRY) { - std::cerr << "Something went wrong with iSENSE.\n"; - std::cerr << "Try formatting your data differently, using a contributor \n"; - std::cerr << "key instead of an email, or asking for help from others. \n"; - std::cerr << "You can also try running the the program with the debug \n"; - std::cerr << "method enabled, by typing: object_name.debug()\n"; - std::cerr << "This will output a ton of data to the console and \n"; - std::cerr << "may help you in debugging your program.\n"; - } - if (http_code == CURL_ERROR) { - std::cerr << "Curl failed for some unknown reason.\n"; - std::cerr << "Make sure you've installed curl / libcurl, and have the \n"; - std::cerr << "picojson header file as well.\n"; + if(!check_http_code(http_code, "append_key_byID")) { + return false; } - return false; + return true; } - -/* - * Appends to a dataset using its dataset name, which can - * be used to find a dataset ID - * We can find the dataset ID by comparing against all the datasets - * in a given project until we find the dataset with the given name. - * - */ +// Appends to a dataset using its dataset name. Calls append_key_byID bool iSENSE::append_key_byName(std::string dataset_name) { - if (project_ID == EMPTY || project_ID.empty()) { - std::cerr << "\nError in method: append_key_byName()\n"; - std::cerr << "Please set a project ID!\n"; - return false; - } - if (title == EMPTY || title.empty()) { - std::cerr << "\nError in method: append_key_byName()\n"; - std::cerr << "Please set a project title!\n"; - return false; - } - if (contributor_key == EMPTY || contributor_key.empty()) { - std::cerr << "\nError in method: append_key_byName()\n"; - std::cerr << "Please set a contributor key!\n"; - return false; - } - if (contributor_label == "label" || contributor_label.empty()) { - // If a label wasn't set, automatically set it to "cURL" - contributor_label = "cURL"; - } - if (map_data.empty()) { - std::cerr << "\nError in method: append_key_byName()\n"; - std::cerr << "Map of keys/data is empty.\n"; - std::cerr << "You should push some data back to this object.\n"; + if(!empty_project_check(APPEND_EMAIL,"append_key_byName")) { return false; } - // We can now find the dataset ID by comparing - // against all the datasets in this project. - get_datasets_and_mediaobjects(); // First pull down the datasets + // We can now find the dataset ID by looking at all datasets in this project. + get_datasets_and_mediaobjects(); // Call the get_dataset_ID function std::string dataset_ID = get_dataset_ID(dataset_name); @@ -959,120 +753,9 @@ bool iSENSE::append_key_byName(std::string dataset_name) { return false; } - -// Post using a email / password -bool iSENSE::post_json_email() { - if (project_ID == EMPTY || project_ID.empty()) { - std::cerr << "\nError in method: post_json_email()\n"; - std::cerr << "Please set a project ID!\n"; - return false; - } - if (title == EMPTY || title.empty()) { - std::cerr << "\nError in method: post_json_email()\n"; - std::cerr << "Please set a project title!\n"; - return false; - } - if (email == EMPTY || email.empty()) { - std::cerr << "\nError in method: post_json_email()\n"; - std::cerr << "Please set an email address!\n"; - return false; - } - if (password == EMPTY || password.empty()) { - std::cerr << "\nError in method: post_json_email()\n"; - std::cerr << "Please set a password!\n"; - return false; - } - if (contributor_label == "label" || contributor_label.empty()) { - // If a label wasn't set, automatically set it to "cURL" - contributor_label = "cURL"; - } - if (map_data.empty()) { - std::cerr << "\nError in method: post_json_email()\n"; - std::cerr << "Map of keys/data is empty.\n"; - std::cerr << "You should push some data back to this object.\n"; - return false; - } - - // Make sure to set the upload URL! - upload_URL = devURL + "/projects/" + project_ID + "/jsonDataUpload"; - - // Call the POST function, give it type 3 - // since this is upload JSON by email & password. - int http_code = post_data_function(POST_EMAIL); - - /* - * The iSENSE API gives us two response codes to check against: - * Success: 200 OK (iSENSE handled the request fine) - * Failure: 401 Unauthorized (email or contributor key was not valid) - * Failure: 422 Unprocessable Entity (email or contributor key was fine, - * but there was an issue with the upload for some reason. - * Something in the formatting caused iSENSE to fail.) - */ - - if (http_code == HTTP_AUTHORIZED) { - std::cout << "\n\nPOST request successfully sent off to iSENSE!\n"; - std::cout << "HTTP Response Code was: " << http_code << "\n"; - std::cout << "The URL to your project is: " << dev_baseURL; - std::cout << "/projects/" << project_ID << "\n"; - return true; - } - std::cerr << "\n\nError in method: post_json_email()\n"; - std::cerr << "POST request **failed**\n"; - std::cerr << "HTTP Response Code was: " << http_code << "\n"; - - // Make a function for this! - if (http_code == HTTP_UNAUTHORIZED) { - std::cerr << "Try checking to make sure your contributor key is valid\n"; - std::cerr << "for the project you are trying to contribute to.\n"; - } - if (http_code == HTTP_NOT_FOUND) { - std::cerr << "Unable to find that project ID.\n"; - } - if (http_code == HTTP_UNPROC_ENTRY) { - std::cerr << "Something went wrong with iSENSE.\n"; - std::cerr << "Try formatting your data differently, using a contributor \n"; - std::cerr << "key instead of an email, or asking for help from others. \n"; - std::cerr << "You can also try running the the program with the debug \n"; - std::cerr << "method enabled, by typing: object_name.debug()\n"; - std::cerr << "This will output a ton of data to the console and \n"; - std::cerr << "may help you in debugging your program.\n"; - } - if (http_code == CURL_ERROR) { - std::cerr << "Curl failed for some unknown reason.\n"; - std::cerr << "Make sure you've installed curl / libcurl, and have the \n"; - std::cerr << "picojson header file as well.\n"; - } - - return false; -} - - -// Post append using email and password +// Append using email and password bool iSENSE::append_email_byID(std::string dataset_ID) { - if (project_ID == EMPTY || project_ID.empty()) { - std::cerr << "\nError in method: append_email_byID\n"; - std::cerr << "Please set a project ID!\n"; - return false; - } - if (title == EMPTY || title.empty()) { - std::cerr << "\nError in method: append_email_byID\n"; - std::cerr << "Please set a project title!\n"; - return false; - } - if (email == EMPTY || email.empty()) { - std::cerr << "\nError in method: append_email_byID\n"; - std::cerr << "Please set an email address!\n"; - return false; - } - if (password == EMPTY || password.empty()) { - std::cerr << "\nError in method: append_email_byID\n"; - std::cerr << "Please set a password!\n"; - return false; - } - if (map_data.empty()) { - std::cerr << "\nError in method: append_email_byID\n"; - std::cerr << "Map of keys/data is empty.\n"; - std::cerr << "You should push some data back to this object.\n"; + if(!empty_project_check(APPEND_EMAIL,"append_email_byID")) { return false; } @@ -1080,86 +763,18 @@ bool iSENSE::append_email_byID(std::string dataset_ID) { // Change the upload URL to append rather than create a new dataset. upload_URL = devURL + "/data_sets/append"; - - // Call the POST function with type 4 for appending via email. int http_code = post_data_function(APPEND_EMAIL); - /* The iSENSE API gives us two response codes to check against: - * Success: 200 OK (iSENSE handled the request fine) - * Failure: 401 Unauthorized (email/pw or contributor key not valid) - * Failure: 422 Unprocessable Entity (email or contributor key was fine, - * but there was an issue with the upload for some reason.) - * Something in the formatting caused iSENSE to fail.) - */ - - if (http_code == HTTP_AUTHORIZED) { - std::cout << "\n\nPOST request successfully sent off to iSENSE!\n"; - std::cout << "HTTP Response Code was: " << http_code << "\n"; - std::cout << "The URL to your project is: " << dev_baseURL; - std::cout << "/projects/" << project_ID << "\n"; - return true; - } - - std::cerr << "\nError in method: append_email_byID()\n"; - std::cerr << "POST request **failed**\n"; - std::cerr << "HTTP Response Code was: " << http_code << "\n"; - - // Make a function for this! - if (http_code == HTTP_UNAUTHORIZED) { - std::cerr << "Try checking to make sure your contributor key is valid\n"; - std::cerr << "for the project you are trying to contribute to.\n"; - } - if (http_code == HTTP_NOT_FOUND) { - std::cerr << "Unable to find that project ID.\n"; - } - if (http_code == HTTP_UNPROC_ENTRY) { - std::cerr << "Something went wrong with iSENSE.\n"; - std::cerr << "Try formatting your data differently, using a contributor \n"; - std::cerr << "key instead of an email, or asking for help from others. \n"; - std::cerr << "You can also try running the the program with the debug \n"; - std::cerr << "method enabled, by typing: object_name.debug()\n"; - std::cerr << "This will output a ton of data to the console and \n"; - std::cerr << "may help you in debugging your program.\n"; - } - if (http_code == CURL_ERROR) { - std::cerr << "Curl failed for some unknown reason.\n"; - std::cerr << "Make sure you've installed curl / libcurl, and have the \n"; - std::cerr << "picojson header file as well.\n"; + if(!check_http_code(http_code, "append_email_byID()")) { + return false; } - return false; + return true; } - -/* Appends to a dataset using its dataset name. - * We can find the dataset ID by comparing against all the datasets in - * a given project until we find the dataset with the given name. - */ +// Appends to a dataset using its dataset name. Calls append_email_byID bool iSENSE::append_email_byName(std::string dataset_name) { - if (project_ID == EMPTY || project_ID.empty()) { - std::cerr << "\nError in method: append_email_byName\n"; - std::cerr << "Please set a project ID!\n"; - return false; - } - if (title == EMPTY || title.empty()) { - std::cerr << "\nError in method: append_email_byName\n"; - std::cerr << "Please set a project title!\n"; - return false; - } - if (email == EMPTY || email.empty()) { - std::cerr << "\nError in method: append_email_byName\n"; - std::cerr << "Please set an email address!\n"; - return false; - } - if (password == EMPTY || password.empty()) { - std::cerr << "\nError in method: append_email_byName\n"; - std::cerr << "Please set a password!\n"; - return false; - } - if (map_data.empty()) { - std::cerr << "\nError in method: append_email_byName\n"; - std::cerr << "Map of keys/data is empty.\n"; - std::cerr << "You should push some data back to this object.\n"; + if(!empty_project_check(APPEND_EMAIL,"append_email_byName")) { return false; } @@ -1175,22 +790,20 @@ bool iSENSE::append_email_byName(std::string dataset_name) { } // If we got here, we failed to find that dataset name in the current project. - std::cerr << "\nError in method: append_email_byName\nFailed to find"; - std::cerr << "the dataset name in project # " << project_ID << "\n"; - std::cerr << "Make sure to type the exact name, as it appears on iSENSE. \n"; + std::cerr << "\nError in method: append_email_byName()\n"; + std::cerr << "Failed to find the dataset name in project # " << project_ID; + std::cerr << "\nMake sure to type the exact name, as it appears on iSENSE.\n"; return false; } +//****************************************************************************** +// Below this point are helper functions. Users should only call functions +// above this point, as these are all called by the API functions. -// This function is called by the JSON upload function -// It formats the upload string -// Users should not have to call this function - API methods will, -// and will pass an int value indicating which API method they are using. +// Format JSON Upload strings. void iSENSE::format_upload_string(int post_type) { - // Add the title + the correct formatting - upload_data["title"] = value(title); + upload_data["title"] = value(title); // Add the title - // This is now a switch. Future API methods can be added here. switch (post_type) { case POST_KEY: upload_data["contribution_key"] = value(contributor_key); @@ -1215,14 +828,8 @@ void iSENSE::format_upload_string(int post_type) { break; } - // Add each field, with its field ID and an array of all the data. - - // Grab all the fields using an iterator. - // Similar to printing them all out below in the debug function. - array::iterator it; - - // Pointer to one of the vectors in the map - std::vector *vect; + array::iterator it; // Grab all the fields using an iterator. + std::vector *vect; // Pointer to one of the vectors in the map // Check and see if the fields object is empty if (fields.is() == true) { @@ -1234,132 +841,165 @@ void iSENSE::format_upload_string(int post_type) { // We made an iterator above, that will let us run through the fields for (it = fields_array.begin(); it != fields_array.end(); it++) { - // Get the current object - object obj = it->get(); + object obj = it->get(); // Get the current object + std::string field_ID = obj["id"].to_str(); // Grab the field ID + std::string name = obj["name"].get(); // Grab the field name - // Grab the field ID and save it in a string/ - std::string field_ID = obj["id"].to_str(); - - // Grab the field name - std::string name = obj["name"].get(); - - // Now add all the data in that field's vector (inside the map) - // to the fields_array object. + // Add the data in that field's vector to the fields_array object. vect = &map_data[name]; format_data(vect, it, field_ID); } - // Once we've made the field_data object, we can - // add the field_data object to the upload_data object + // Add the field_data obj to the upload_data obj upload_data["data"] = value(fields_data); } - -// This makes the switch above shorter, -// since I reuse this code for all 5 types of data. +// This makes format_upload_string() much shorter. void iSENSE::format_data(std::vector *vect, array::iterator it, std::string field_ID) { std::vector::iterator x; + value::array data; // picojson::value::array, represents a JSON array. - // picojson::value::array, basically a vector but represents a json array. - value::array data; - - // First we push all the vector data into a json array. for (x = vect -> begin(); x < vect -> end(); x++) { - data.push_back(value(*x)); + data.push_back(value(*x)); // Push all the vector data into a JSON array. } - // Now we push the json array to the upload_data object. - fields_data[field_ID] = value(data); + fields_data[field_ID] = value(data); // Push the JSON array to the upload_data obj. } -/* This function is called by all of the POST functions. - * It must be given a parameter, an integer "type", which determines which - * way the JSON should be formatted in the format_upload_string function. - * - * The method returns an HTTP response code, like "200", "404", "503", etc. - */ +// This function is called by all of the POST functions. int iSENSE::post_data_function(int post_type) { - // Upload_URL must have already been set. - // Otherwise the POST request will fail unexpectedly. + // Upload_URL must have already been set. Otherwise the request will fail. if (upload_URL == EMPTY || upload_URL.empty()) { std::cerr << "\nError in method: post_data_function()\n"; std::cerr << "Please set a valid upload URL.\n"; return CURL_ERROR; } - // Format the data to be uploaded. Call another function to format this. - format_upload_string(post_type); - - /* Once we get the data formatted, we can try to POST to rSENSE - * The below code uses cURL. It - * 1. Sets the headers, so iSENSE knows we are sending it JSON - * 2. Does some curl init stuff that makes the magic happen. - * 3. cURL sends off the request, we can grab the return code to see - * if cURL failed. - * Also check the curl verbose debug to see why something failed. - * 4. We also get the HTTP status code so we know if iSENSE handled - * the request or not. - */ + format_upload_string(post_type); // format the upload string - // CURL object and response code. CURL *curl = curl_easy_init(); // cURL object long http_code = 0; // HTTP status code + curl_global_init(CURL_GLOBAL_DEFAULT); // In windows, init the winsock stuff - // In windows, this will init the winsock stuff - curl_global_init(CURL_GLOBAL_DEFAULT); - - // Set the headers to JSON, make sure to use UTF-8 - struct curl_slist *headers = NULL; + struct curl_slist *headers = NULL; // Headers for uploading via JSON headers = curl_slist_append(headers, "Accept: application/json"); headers = curl_slist_append(headers, "Accept-Charset: utf-8"); headers = curl_slist_append(headers, "charsets: utf-8"); headers = curl_slist_append(headers, "Content-Type: application/json"); - // get a curl handle - curl = curl_easy_init(); - if (curl) { - // Set the URL that we will be using for our POST. - curl_easy_setopt(curl, CURLOPT_URL, upload_URL.c_str()); + // Get the upload JSON as a std::string + std::string upload_str = (value(upload_data).serialize()); - // This is necessary! As I had issues with only 1 byte being sent off - // to iSENSE unless I made sure to make a string out of the upload_data - // picojson object, then with that string you can call c_str() on it below. - std::string upload_real = (value(upload_data).serialize()); + // POST data + curl_easy_setopt(curl, CURLOPT_URL, upload_URL.c_str()); // URL + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, upload_str.c_str()); // JSON data + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // JSON Headers - // POST data. Upload will be the string with all the data. - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, upload_real.c_str()); - - // JSON Headers - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - - // Verbose debug output - turn this on if you are having problems. - // It will spit out a ton of information, such as bytes sent off, - // headers/access/etc. Useful to see if you formatted the data right. + // Verbose debug output - turn this on if you are having problems uploading. // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); std::cout << "\nrSENSE response: \n"; - // Perform the request, res will get the return code - curl_easy_perform(curl); - - // This will put the HTTP response code into the "http_code" variable. + curl_easy_perform(curl);// Perform the request, res will get the return code curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - - // Clean up curl. - curl_easy_cleanup(curl); + curl_easy_cleanup(curl); // Clean up curl. curl_global_cleanup(); - // Return the HTTP code we get from curl. - return http_code; + return http_code; // Return the HTTP code we get from curl. } - // If curl fails for some reason, return CURL_ERROR (-1). - return CURL_ERROR; + return CURL_ERROR; // If curl fails, return CURL_ERROR (-1). } +// Checks to see if the given project has been properly setup. +// Shouldn't be any empty values, such as project ID, contributor key, etc. +bool iSENSE::empty_project_check(int type, std::string method) { + // Check email based values, such as email & password. + if (type == POST_EMAIL || type == APPEND_EMAIL) { + if (email == EMPTY || email.empty()) { + std::cerr << "\nError in method: " << method << "\n"; + std::cerr << "Please set an email address!\n"; + return false; + } + if (password == EMPTY || password.empty()) { + std::cerr << "\nError in method: " << method << "\n"; + std::cerr << "Please set a password!\n"; + return false; + } + } + + // Check key based values, such as contributor key and label. + if (type == POST_KEY || type == APPEND_KEY) { + if (contributor_key == EMPTY || contributor_key.empty()) { + std::cerr << "\nError in method: " << method << "\n"; + std::cerr << "Please set a contributor key!\n"; + return false; + } + if (contributor_label == "label" || contributor_label.empty()) { + // If a label wasn't set, automatically set it to "cURL" + contributor_label = "cURL"; + } + } + + // The rest are general checks that should not be empty, since the calling + // method depends on them being set properly. + if (project_ID == EMPTY || project_ID.empty()) { + std::cerr << "\nError in method: " << method << "\n"; + std::cerr << "Please set a project ID!\n"; + return false; + } + if (title == EMPTY || title.empty()) { + std::cerr << "\nError in method: " << method << "\n"; + std::cerr << "Please set a project title!\n"; + return false; + } + if (map_data.empty()) { + std::cerr << "\nError in method: " << method << "\n"; + std::cerr << "Map of keys/data is empty.\n"; + std::cerr << "You should push some data back to this object.\n"; + return false; + } + + return true; +} + +// Checks a given HTTP code for errors. +bool iSENSE::check_http_code(int http_code, std::string method) { + if(http_code == HTTP_AUTHORIZED) { + return true; // Print nothing for OK codes. + } + + // Print out error messages. + std::cerr << "\nError in method: " << method << "\n"; + std::cerr << "Request **failed**\n"; + std::cerr << "HTTP Response Code was: " << http_code << "\n"; + + if (http_code == HTTP_UNAUTHORIZED) { + std::cerr << "Try checking to make sure your contributor key is valid\n"; + std::cerr << "for the project you are trying to contribute to.\n"; + } + else if (http_code == HTTP_NOT_FOUND) { + std::cerr << "Unable to find that project ID.\n"; + } + else if (http_code == HTTP_UNPROC_ENTRY) { + std::cerr << "Something went wrong with your formatting.\n"; + std::cerr << "Try formatting your data differently, using a contributor \n"; + std::cerr << "key instead of an email, or asking for help from others. \n"; + std::cerr << "You can also try running the the program with the debug \n"; + std::cerr << "method enabled, by typing: object_name.debug()\n"; + std::cerr << "This will output a ton of data to the console and \n"; + std::cerr << "may help you in debugging your program.\n"; + } + else if (http_code == CURL_ERROR) { + std::cerr << "Curl failed for some unknown reason.\n"; + std::cerr << "Make sure you've installed curl / libcurl, and have the \n"; + std::cerr << "picojson header file as well.\n"; + } + return false; +} // Call this function to dump all the data in the given object. void iSENSE::debug() { @@ -1417,7 +1057,7 @@ void iSENSE::debug() { } -//**************************************************************************** +//****************************************************************************** // These are needed for picojson & libcURL. // Declared in memfile.h but defined below. MEMFILE* memfopen() { @@ -1427,7 +1067,6 @@ MEMFILE* memfopen() { return mf; } - void memfclose(MEMFILE* mf) { // Double check to make sure that mf exists. if (mf == NULL) { @@ -1443,14 +1082,12 @@ void memfclose(MEMFILE* mf) { free(mf); } - // Simple function only used by the get_check_user function to // suppress curl's output to the screen. size_t suppress_output(char* ptr, size_t size, size_t nmemb, void* stream) { return size * nmemb; } - size_t memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) { MEMFILE* mf = (MEMFILE*) stream; int block = size * nmemb; @@ -1469,7 +1106,6 @@ size_t memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) { return block; } - char* memfstrdup(MEMFILE* mf) { char* buf = (char*)malloc(mf->size + 1); memcpy(buf, mf->data, mf->size); diff --git a/C++/API/include/API.h b/C++/API/include/API.h index 09e34b9..f20e442 100644 --- a/C++/API/include/API.h +++ b/C++/API/include/API.h @@ -70,11 +70,11 @@ class iSENSE { void set_project_all(std::string proj_ID, std::string proj_title, std::string label, std::string contr_key); + // These can be used to manually project data. Be sure to set a contributor + // key or email / password as well! void set_project_ID(std::string proj_ID); void set_project_title(std::string proj_title); void set_contributor_key(std::string proj_key); - - // Optional, by default the label will be "cURL" void set_project_label(std::string proj_label); // This should be used for setting the email / password for a project. @@ -165,6 +165,10 @@ class iSENSE { std::string get_dataset_ID(std::string dataset_name); std::string get_field_ID(std::string field_name); + // Error methods - makes error checking simple. + bool empty_project_check(int type, std::string method); + bool check_http_code(int http_code, std::string method); + /* Future functions to be implemented at a later date. // Editing API calls (not yet implemented) bool get_edit_key(); // Edit a dataset with a contributor key diff --git a/C++/API/include/memfile.h b/C++/API/include/memfile.h index 54d8fa0..b721645 100644 --- a/C++/API/include/memfile.h +++ b/C++/API/include/memfile.h @@ -9,8 +9,8 @@ */ typedef struct { - char* data; // response data from server - size_t size; // response size of data + char* data; // response data from server + size_t size; // response size of data } MEMFILE; MEMFILE* memfopen(); diff --git a/C++/README.md b/C++/README.md index de0fae1..ced78b4 100644 --- a/C++/README.md +++ b/C++/README.md @@ -1,7 +1,10 @@ C++ API Code ================ -It is recommended that you compile any projects using the iSENSE C++ API code using either Linux or Mac OS X. The API code works in windows but it is not as easy to setup and requires using Visual Studios. +It is recommended that you compile any projects using the iSENSE C++ API code +using either Linux or Mac OS X. + +The API code works in windows but it is not as easy to setup and requires using Visual Studios. Some of these examples use cURL through the libcURL library for C. Below is the setup guide for various operating systems. @@ -64,9 +67,9 @@ Confirmed working in Windows 7, 8.1 (x64), Mac OS X 10.10.1 Yosemite and Ubuntu ##NOTES: For some of these programs, a JSON library called picojson is used to serialize and parse JSON. + [The latest PicoJSON can be found here.](https://raw.githubusercontent.com/kazuho/picojson/master/picojson.h) -It is also included it in the iSENSE API repository as a submodule, so if you git clone the API repository it will automatically -git clone the picojson repository. +It is also included it in the iSENSE API repository as a submodule, so if you git clone the API repository it will automatically git clone the picojson repository. Picojson is contained in one header file called "picojson.h". From 3b9a37f00e54268b2e47d3cfef9228017e7463f6 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Sat, 26 Sep 2015 17:44:22 -0400 Subject: [PATCH 11/19] simplified the C++ code a ton. --- C++/API/API.cpp | 431 ++++++++++-------------------------------- C++/API/include/API.h | 110 ++++------- C++/API/tests.cpp | 6 - 3 files changed, 138 insertions(+), 409 deletions(-) diff --git a/C++/API/API.cpp b/C++/API/API.cpp index 281c4b7..c29e227 100644 --- a/C++/API/API.cpp +++ b/C++/API/API.cpp @@ -12,8 +12,8 @@ iSENSE::iSENSE() { // Default constructor email = EMPTY; password = EMPTY; } - // Constructor with parameters -iSENSE::iSENSE(std::string proj_ID, std::string proj_title, + +iSENSE::iSENSE(std::string proj_ID, std::string proj_title, // Constructor with parameters std::string label, std::string contr_key) { set_project_ID(proj_ID); set_project_title(proj_title); @@ -89,8 +89,7 @@ std::string iSENSE::generate_timestamp(void) { return cplusplus_timestamp; } -// Resets the object and clears the map. -void iSENSE::clear_data(void) { +void iSENSE::clear_data(void) { // Resets the object and clears the map. upload_URL = EMPTY; get_URL = EMPTY; get_UserURL = EMPTY; @@ -138,66 +137,38 @@ void iSENSE::push_vector(std::string field_name, std::vector data) std::vector iSENSE::get_projects_search(std::string search_term) { std::string get_search = devURL + "/projects?utf8=true&search=" \ + search_term + "&sort=updated_at&order=DESC"; + std::vector project_titles; // Vector of project titles. - // Vector of project titles. - std::vector project_titles; - - // This project will try using CURL to make a basic GET request to rSENSE - CURL *curl = curl_easy_init(); // cURL object - long http_code = 0; // HTTP status code - MEMFILE* json_file = memfopen(); // Writing JSON to this file. - char error[256]; // Errors get written here + CURL *curl = curl_easy_init(); // cURL object + MEMFILE* json_file = memfopen(); // Writing JSON to this file. + long http_code = 0; // HTTP status code if (curl) { - // Set the GET URL, in this case the one be created above using the user's - // email & password. curl_easy_setopt(curl, CURLOPT_URL, get_search.c_str()); - - // Write errors to the "error" array - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error); - - // From the picojson example, "github-issues.cc". - // Used for writing the JSON to a file. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); curl_easy_setopt(curl, CURLOPT_WRITEDATA, json_file); - // Perform the request - curl_easy_perform(curl); - - // This will put the HTTP response code into the "http_code" variable. + curl_easy_perform(curl); // Perform the request curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - - // cout the http code. - std::cout << "\nhttp code was: " << http_code << "\n\n"; } - /* The iSENSE API gives us one response code to check against: - * Success: 200 OK - * If we do not get a code 200 from iSENSE, something went wrong. - */ + curl_easy_cleanup(curl); // Clean up cURL + curl_global_cleanup(); - // If we do not get a code 200, or cURL quits for some reason, - // we didn't successfully get the project's fields. - if (http_code != HTTP_AUTHORIZED) { + if (http_code != HTTP_AUTHORIZED) { // Didn't get HTTP code 200. std::cerr << "\nError in: get_projects_search(string search_term) \n"; std::cerr << "Project search failed.\n"; std::cerr << "Something with either curl, your internet connection, \n"; std::cerr << "iSENSE or something else failed.\n"; - - // Clean up cURL and close the memfile - curl_easy_cleanup(curl); - curl_global_cleanup(); memfclose(json_file); return project_titles; } - // We got a code 200, so try and parse the JSON into a PICOJSON object. - // Error checking for the parsing occurs below. std::string errors; value projects_json; - // This will parse the JSON file. + // Parse the JSON file. parse(projects_json, json_file->data, json_file->data + json_file->size, &errors); // If we have errors, print them out and quit. @@ -205,17 +176,12 @@ std::vector iSENSE::get_projects_search(std::string search_term) { std::cerr << "\nError in: get_projects_search(string search_term)"; std::cerr << "Error parsing JSON file.\n"; std::cerr << "Error was: " << errors << "\n"; - - // Clean up cURL and close the memfile - curl_easy_cleanup(curl); - curl_global_cleanup(); memfclose(json_file); - return project_titles; + return project_titles; // Return an empty vector } - // Convert the JSON array (projects_json) into a vector of strings - // (strings would be project titles); + // Convert the JSON array (projects_json) into a vector of project title strings array projects_array = projects_json.get(); array::iterator it, the_begin, the_end; @@ -224,96 +190,53 @@ std::vector iSENSE::get_projects_search(std::string search_term) { // Check and see if the projects_title JSON array is empty if (the_begin == the_end) { - /* - * Print an error and quit, we can't make a vector of - * project titles to return if the JSON array ended up empty. - * Probably wasn't any projects with that search term. - */ std::cerr << "\nError in: get_projects_search(string search_term) \n"; std::cerr << "Project title array is empty.\n"; - - // Clean up cURL and close the memfile - curl_easy_cleanup(curl); - curl_global_cleanup(); memfclose(json_file); - return project_titles; // this is an empty vector + return project_titles; // Return an empty vector } for (it = projects_array.begin(); it != projects_array.end(); it++) { - // Get the current object - object obj = it->get(); - - // Grab the field name - std::string name = obj["name"].get(); - - // Push the name back into the vector. - project_titles.push_back(name); + object obj = it->get(); // Get the current object + std::string name = obj["name"].get(); // Grab the field name + project_titles.push_back(name); // Push the name back into the vector. } - // Clean up cURL and close the memfile - curl_easy_cleanup(curl); - curl_global_cleanup(); memfclose(json_file); - - // Return a vector of project titles - return project_titles; + return project_titles; // Return a vector of project titles } -// Checks to see if the email / password is valid bool iSENSE::get_check_user() { - if (email == EMPTY || password == EMPTY) { - std::cerr << "\nPlease set an email & password for this project.\n"; + if (email == EMPTY || email.empty()) { + std::cerr << "\nPlease set an email for this project.\n"; return false; - } else if (email.empty() || password.empty()) { - std::cerr << "\nPlease set an email & password for this project.\n"; + } else if (password == EMPTY || password.empty()) { + std::cerr << "\nPlease set a password for this project.\n"; return false; } - // If we get here, an email and password have been set, so do a GET using - // the email & password. - get_UserURL = devURL + "/users/myInfo?email=" + - email + "&password=" + password; + get_UserURL = devURL + "/users/myInfo?email=" + email + "&password=" + password; - // This project will try using CURL to make a basic GET request to rSENSE CURL *curl = curl_easy_init(); // cURL object long http_code = 0; // HTTP status code if (curl) { - // Set the GET URL - curl_easy_setopt(curl, CURLOPT_URL, get_UserURL.c_str()); - - // Stop libcURL from outputting to stdio. - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, suppress_output); - - curl_easy_perform(curl); // Perform the request - - // This will put the HTTP response code into the "http_code" variable. - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + curl_easy_setopt(curl, CURLOPT_URL, get_UserURL.c_str()); // GET URL + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, suppress_output); // Suppress output to STDOUT + curl_easy_perform(curl); // Perform the request + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); // get HTTP code. } + curl_easy_cleanup(curl); // Always cleanup. + curl_global_cleanup(); - /* The iSENSE API gives us two response codes to check against: - * Success: 200 OK - * Failure: 401 Unauthorized - */ if (http_code == HTTP_AUTHORIZED) { - // Clean up cURL - curl_easy_cleanup(curl); - curl_global_cleanup(); - - // Return success. return true; } - // Clean up cURL - curl_easy_cleanup(curl); - curl_global_cleanup(); - - // If curl fails, return false. return false; } -// GET the project fields for a given project ID bool iSENSE::get_project_fields() { if (project_ID == EMPTY || project_ID.empty()) { std::cerr << "Error - project ID not set!\n"; @@ -322,79 +245,45 @@ bool iSENSE::get_project_fields() { get_URL = devURL + "/projects/" + project_ID; - // This project will try using CURL to make a basic GET request to rSENSE - // It will then save the JSON it recieves into a picojson object. CURL *curl = curl_easy_init(); // cURL object - long http_code = 0; // HTTP status code MEMFILE* json_file = memfopen(); // Writing JSON to this file. - char error[256]; // Errors get written here + long http_code = 0; // HTTP status code if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, get_URL.c_str()); - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error); - - // From the picojson example, "github-issues.cc". - // Used for writing the JSON to a file. - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, json_file); - - // Perform the request, res will get the return code - curl_easy_perform(curl); - - // This will put the HTTP response code into the "http_code" variable. - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + curl_easy_setopt(curl, CURLOPT_URL, get_URL.c_str()); // GET URL + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); // Write function. + curl_easy_setopt(curl, CURLOPT_WRITEDATA, json_file); // JSON saved here. + curl_easy_perform(curl); // peform the GET request + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); // Store HTTP Code. } + curl_easy_cleanup(curl); // Clean up cURL + curl_global_cleanup(); - /* The iSENSE API gives us one response code to check against: - * Success: 200 OK - * Failure: 404 Not Found - */ - - if (http_code != HTTP_AUTHORIZED) { + if (http_code != HTTP_AUTHORIZED) { // Check for valid HTTP code. std::cerr << "Error in method: get_project_fields()\n"; - std::cerr << "GET project fields failed.\n"; - std::cerr << "Is the project ID you entered valid?\n"; - - // Clean up cURL and close the memfile - curl_easy_cleanup(curl); - curl_global_cleanup(); - memfclose(json_file); + std::cerr << "GET project fields failed. Project ID may be invalid.\n"; + memfclose(json_file); // Close the memfile return false; } - - // We got a code 200, so try and parse the JSON into a PICOJSON object. - // Error checking for the parsing occurs below. std::string errors; - // This will parse the JSON file. - parse(get_data, json_file->data, - json_file->data + json_file->size, &errors); + // Parse the JSON file. + parse(get_data, json_file->data, json_file->data + json_file->size, &errors); + memfclose(json_file); // Close the memfile - // If we have errors, print them out and quit. - if (!errors.empty()) { + if (!errors.empty()) { // If we have errors, print them out and quit. std::cerr << "\nError parsing JSON file in method: get_project_fields()\n"; std::cerr << "Error was: " << errors; + return false; } - - // Save the fields to the field array - fields = get_data.get("fields"); + fields = get_data.get("fields"); // Save the fields to the field array fields_array = fields.get(); - // Clean up cURL and close the memfile - curl_easy_cleanup(curl); - curl_global_cleanup(); - memfclose(json_file); - - // Return true as we were able to successfully get the project's fields. return true; } -// Given that a project ID has been set, this function -// makes a GET request and saves all the datasets & media objects -// into two picojson arrays. -// It will also update the fields for that given project ID bool iSENSE::get_datasets_and_mediaobjects() { // Check that the project ID is set properly. // When the ID is set, the fields are also pulled down as well. @@ -405,95 +294,59 @@ bool iSENSE::get_datasets_and_mediaobjects() { } // The "?recur=true" will make iSENSE return: - // ALL datasets in that project. - // ALL media objects in that project - // And owner information. + // ALL datasets in that project and ALL media objects in that project get_URL = devURL + "/projects/" + project_ID + "?recur=true"; - // This project will try using CURL to make a basic GET request to rSENSE - // It will then save the JSON it recieves into a picojson object. CURL *curl = curl_easy_init(); // cURL object - long http_code = 0; // HTTP status code MEMFILE* json_file = memfopen(); // Writing JSON to this file. - char error[256]; // Errors get written here + long http_code = 0; // HTTP status code if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, get_URL.c_str()); - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error); - - // From the picojson example, "github-issues.cc". - // Used for writing the JSON to a file. - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, json_file); - - // Perform the request, res will get the return code - curl_easy_perform(curl); - - // This will put the HTTP response code into the "http_code" variable. - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - - // Clean up cURL - curl_easy_cleanup(curl); - curl_global_cleanup(); + curl_easy_setopt(curl, CURLOPT_URL, get_URL.c_str()); // GET URL + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); // Write function + curl_easy_setopt(curl, CURLOPT_WRITEDATA, json_file); // JSON saved here + curl_easy_perform(curl); // Perform the request + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); // Get HTTP Code } + curl_easy_cleanup(curl); // Clean up cURL + curl_global_cleanup(); - /* The iSENSE API gives us one response code to check against: - * Success: 200 OK - * Failure: 404 Not Found - */ - - // If we do not get a code 200, or cURL quits for some reason, - // we didn't successfully get the project's fields. if (http_code != HTTP_AUTHORIZED) { std::cerr << "\nError in: get_datasets_and_mediaobjects().\n"; std::cerr << "GET project fields failed.\n"; std::cerr << "Is the project ID you entered valid?\n"; - - // Close the memfile - memfclose(json_file); + memfclose(json_file); // Close the memfile return false; } - // We got a code 200, so try and parse the JSON into a PICOJSON object. - // Error checking for the parsing occurs below. std::string errors; - // This will parse the JSON file. + // Parse the JSON file. parse(get_data, json_file->data, json_file->data + json_file->size, &errors); + memfclose(json_file); // close the memfile - // close the memfile - memfclose(json_file); - - // If we have errors, print them out and quit. - if (errors.empty() != true) { + if (errors.empty() != true) { // If we have errors, print them out and quit. std::cerr << "\nError in method: get_datasets_and_mediaobjects()\n"; std::cerr << "Parsing JSON file failed. Error was: " << errors; return false; } - // Save the fields to the field array - fields = get_data.get("fields"); + fields = get_data.get("fields"); // Save the fields to the field array fields_array = fields.get(); - // Save the datasets to the datasets array - value temp = get_data.get("dataSets"); + value temp = get_data.get("dataSets"); // Save the datasets to the datasets array data_sets = temp.get(); - // Save the media objects to the media objects array - temp = get_data.get("mediaObjects"); + temp = get_data.get("mediaObjects"); // Save the media objs to the media objs array media_objects = temp.get(); - // Save the owner info. - temp = get_data.get("owner"); + temp = get_data.get("owner"); // Save the owner info. owner_info = temp.get(); return true; } -// Calls the get_datasets function, returns a vector of the data -// Must be given a valid iSENSE dataset name & field name -// Must have a project ID set. std::vector iSENSE::get_dataset(std::string dataset_name, std::string field_name) { std::vector vector_data; @@ -507,21 +360,16 @@ std::vector iSENSE::get_dataset(std::string dataset_name, // First call get_datasets_and_mediaobjects() and see if that is sucessful. if (!get_datasets_and_mediaobjects()) { - // We failed. Let the user know, and return an empty map. std::cerr << "\n\nError in method: get_dataset(string, string)\n"; std::cerr << "Failed to get datasets.\n"; return vector_data; } - // If we get here, we can try and find the field name, - // and return a vector of data for that field name. array::iterator it, the_begin, the_end; - the_begin = data_sets.begin(); the_end = data_sets.end(); - // Check and see if the data_sets array is empty - if (the_begin == the_end) { + if (the_begin == the_end) { // Check and see if the data_sets array is empty std::cerr << "\n\nError in method: get_dataset(string, string)\n"; std::cerr << "Datasets array is empty.\n"; return vector_data; // this is an empty vector @@ -536,30 +384,13 @@ std::vector iSENSE::get_dataset(std::string dataset_name, std::cerr << "Either the dataset / field names are incorrect, \n"; std::cerr << "Or the project ID is wrong.\n"; - return vector_data; + return vector_data; // this is an empty vector } - // If we make here, we can make a vector containing - // data points and return that to the user. - - /* Note on why there's 3 for loops: - * First for loop goes through all datasets in the project and - * looks for one with the dataset name we found above. - * We then have the object containing all the info about that dataset. - * The second for loop then goes through the info object and - * looks for the array of data points for this dataset. - * The final for loop goes through this array and grabs - * all the data points with the above field ID (field name) and - * stores these data points into a vector, which is then returned to the user. - */ - // This outer for loop is for going through all datasets in the project for (it = data_sets.begin(); it != data_sets.end(); it++) { - // Get the current object - object obj = it->get(); - - // Get that object's dataset ID to compare against - std::string id = obj["id"].to_str(); + object obj = it->get(); // Get the current object + std::string id = obj["id"].to_str(); // Get that object's dataset ID // If we have the right object, we can now start looking for the data array if (id == dataset_ID) { @@ -567,55 +398,35 @@ std::vector iSENSE::get_dataset(std::string dataset_name, const object& cur_obj = it->get(); // This basically lets us iterate through the current object - for (object::const_iterator i = cur_obj.begin(); - i != cur_obj.end(); i++) { - // When we get here, we've found the data array! WOO HOO! - if (i->first == "data") { - // Now we need one final for loop to go through the array, - // and push_back just the data points for our field name - // (using the field ID we found above) + for (object::const_iterator i = cur_obj.begin(); i != cur_obj.end(); i++) { + if (i->first == "data") { // When we get here, we've found the data array! WOO HOO! array dataset_list = i->second.get(); - for (array::iterator iter = dataset_list.begin(); - iter != dataset_list.end(); iter++) { - // We make some tmp objects for getting datapoints, since the - // dataset array for each dataset stores objects for each data point - object tmp_obj = iter->get(); - std::string data_pt = tmp_obj[field_ID].to_str(); - vector_data.push_back(data_pt); + // Go through the array and push_back data points for the given field name + for (array::iterator iter = dataset_list.begin(); iter != dataset_list.end(); iter++) { + object tmp_obj = iter->get(); // Temp obj to + std::string data_pt = tmp_obj[field_ID].to_str(); // get data point. + vector_data.push_back(data_pt); // finally push_back. } - - // When we get here, we're finished. Finally return the vector of - // data for the given field name. - return vector_data; + return vector_data; // Return the vector of data for the given field name. } } } } - /* Note: should we not find the data we're looking for, this vector is empty. - * Users should check the vector to make sure it is not empty & - * look for any error messages that are printed. - * Also note - if we get here, then we failed somewhere above - * since we should have returned a vector of data points. - */ std::cerr << "\n\nError in method: get_dataset(string, string)\n"; - std::cerr << "Failed to get data points. \n"; + std::cerr << "Failed to get dataset. \n"; std::cerr << "Check the following & make sure they are correct:\n"; std::cerr << "field name, dataset name, project ID\n"; - return vector_data; + return vector_data; // This should be empty, or may not contain all the data. } std::string iSENSE::get_field_ID(std::string field_name) { - // Grab all the fields using an iterator. - // Similar to printing them all out below in the debug function. array::iterator it; // Check and see if the fields object is empty if (fields.is() == true) { - // Print an error and quit, we can't do anything if - // the field array wasn't set up correctly. std::cerr << "\nError in method: get_field_ID()\n"; std::cerr << "Field array wasn't set up."; std::cerr << "Have you pulled the fields off iSENSE?\n"; @@ -624,60 +435,44 @@ std::string iSENSE::get_field_ID(std::string field_name) { // We made an iterator above, that will let us run through the fields for (it = fields_array.begin(); it != fields_array.end(); it++) { - // Get the current object - object obj = it->get(); - - // Grab the field ID and save it in a string/ - std::string field_ID = obj["id"].to_str(); + object obj = it->get(); // Get the current object + std::string field_ID = obj["id"].to_str(); // Grab the field ID + std::string name = obj["name"].get(); // Grab the field name - // Grab the field name - std::string name = obj["name"].get(); - - // See if we can find the field name in this project - if (name == field_name) { - return field_ID; + if (name == field_name) { // Found the given field name + return field_ID; // So return the field ID } } std::cerr << "\nError in method: get_field_ID()\n"; std::cerr << "Unable to find the field ID for the given field name.\n"; - std::cerr << "Did you spell the field name right?\n"; return GET_ERROR; } std::string iSENSE::get_dataset_ID(std::string dataset_name) { - // Compare the dataset name the user provided with datasets in the project. - // Use an iterator to go through all the datasets array::iterator it; // Check and see if the datasets object is empty // Need to find out how to do this using picojson arrays! - // We made an iterator above, that will let us run through the fields + // This is similar to the get_field_ID function loop. for (it = data_sets.begin(); it != data_sets.end(); it++) { - // Get the current object - object obj = it->get(); - - // Grab the dataset ID and save it in a string - std::string dataset_ID = obj["id"].to_str(); + object obj = it->get(); // Get the current object + std::string dataset_ID = obj["id"].to_str(); // Grab the dataset ID + std::string name = obj["name"].get(); // Grab the dataset name - // Grab the dataset name - std::string name = obj["name"].get(); - - if (name == dataset_name) { - // We found the name, so return the dataset ID - return dataset_ID; + if (name == dataset_name) { // We found the dataset name + return dataset_ID; // So return the dataset ID } } std::cerr << "\nError in method: get_dataset_ID()\n"; std::cerr << "Unable to find the dataset ID for the given dataset name.\n"; - std::cerr << "Did you spell the dataset name right?\n"; return GET_ERROR; } bool iSENSE::post_json_key() { - if(!empty_project_check(APPEND_EMAIL,"post_json_key()")) { + if(!empty_project_check(POST_KEY, "post_json_key()")) { return false; } @@ -692,7 +487,7 @@ bool iSENSE::post_json_key() { } bool iSENSE::post_json_email() { - if(!empty_project_check(APPEND_EMAIL,"post_json_email()")) { + if(!empty_project_check(POST_EMAIL, "post_json_email()")) { return false; } @@ -708,19 +503,13 @@ bool iSENSE::post_json_email() { // Append using a contributor key bool iSENSE::append_key_byID(std::string dataset_ID) { - if(!empty_project_check(APPEND_EMAIL,"append_key_byID")) { + if(!empty_project_check(APPEND_KEY, "append_key_byID")) { return false; } - // Set the dataset_ID - set_dataset_ID(dataset_ID); - - // Set the append API URL - upload_URL = devURL + "/data_sets/append"; - - // Call the POST function, give it type 2 - // since this is an append by contributor key. - int http_code = post_data_function(APPEND_KEY); + set_dataset_ID(dataset_ID); // Set the dataset_ID + upload_URL = devURL + "/data_sets/append"; // Set the append API URL + int http_code = post_data_function(APPEND_KEY); // Call helper function. if(!check_http_code(http_code, "append_key_byID")) { return false; @@ -731,19 +520,15 @@ bool iSENSE::append_key_byID(std::string dataset_ID) { // Appends to a dataset using its dataset name. Calls append_key_byID bool iSENSE::append_key_byName(std::string dataset_name) { - if(!empty_project_check(APPEND_EMAIL,"append_key_byName")) { + if(!empty_project_check(APPEND_KEY, "append_key_byName")) { return false; } - // We can now find the dataset ID by looking at all datasets in this project. - get_datasets_and_mediaobjects(); + get_datasets_and_mediaobjects(); // Make sure we've got all the datasets. + std::string dataset_ID = get_dataset_ID(dataset_name); // Get the dataset ID - // Call the get_dataset_ID function - std::string dataset_ID = get_dataset_ID(dataset_name); - - // If we didn't get any errors, call the append by ID function. if (dataset_ID != GET_ERROR) { - return append_key_byID(dataset_ID); + return append_key_byID(dataset_ID); // Call append byID function. } // If we got here, we failed to find that dataset name in the current project. @@ -759,11 +544,9 @@ bool iSENSE::append_email_byID(std::string dataset_ID) { return false; } - set_dataset_ID(dataset_ID); // Set the dataset_ID - - // Change the upload URL to append rather than create a new dataset. - upload_URL = devURL + "/data_sets/append"; - int http_code = post_data_function(APPEND_EMAIL); + set_dataset_ID(dataset_ID); // Set the dataset_ID + upload_URL = devURL + "/data_sets/append"; // Set the API URL + int http_code = post_data_function(APPEND_EMAIL); // Call helper function. if(!check_http_code(http_code, "append_email_byID()")) { return false; @@ -778,15 +561,11 @@ bool iSENSE::append_email_byName(std::string dataset_name) { return false; } - // We can now find the dataset ID by looking at all datasets in this project. - get_datasets_and_mediaobjects(); - - // Call the get_dataset_ID function - std::string dataset_ID = get_dataset_ID(dataset_name); + get_datasets_and_mediaobjects(); // Make sure we've got all the datasets + std::string dataset_ID = get_dataset_ID(dataset_name); // Get the dataset ID - // If we didn't get any errors, call the append by ID function. if (dataset_ID != GET_ERROR) { - return append_email_byID(dataset_ID); + return append_email_byID(dataset_ID); // Call append byID function. } // If we got here, we failed to find that dataset name in the current project. diff --git a/C++/API/include/API.h b/C++/API/include/API.h index f20e442..a8a359a 100644 --- a/C++/API/include/API.h +++ b/C++/API/include/API.h @@ -18,23 +18,11 @@ #include #include -/* Currently working on: - * Linux (64 bit) -> Uses a Makefile - * Mac OS 10.10 (64 bit) -> should work the same as Linux - * Windows 7 / 8.1 (64 bit) -> Requires Visual Studios - */ - -/* NOTE: - * Most of the API calls expect that you have already set an email & password - * OR a contributor key, as well as a project ID and a project title. - * You can set these by either calling the default constructor with parameters, - * or by calling one of the set methods. - */ - // For picojson using namespace picojson; -// Constants for the rest of the class +// Currently only rSENSE is supported. In the future, allow switching between +// dev and live. const std::string dev_baseURL = "http://rsense-dev.cs.uml.edu"; const std::string live_baseURL = "http://isenseproject.org"; const std::string devURL = "http://rsense-dev.cs.uml.edu/api/v1"; @@ -60,11 +48,11 @@ const std::string EMPTY = "-----"; class iSENSE { public: + // Constructors iSENSE(); iSENSE(std::string proj_ID, std::string proj_title, std::string label, std::string contr_key); - // Similar to the constructor with parameters, but called after // the object is created. This way you can change the title/project ID/etc. void set_project_all(std::string proj_ID, std::string proj_title, @@ -81,6 +69,9 @@ class iSENSE { // Returns true if the email / password are valid, or false if they are not. bool set_email_password(std::string proj_email, std::string proj_password); + void clear_data(); // Resets the object and clears the map. + void debug(); // For debugging, this method dumps all the data. + /* This function will push data back to the map. * User must give the pushback function the following: * 1. Field name (as seen on iSENSE) @@ -88,32 +79,19 @@ class iSENSE { * * Note: this function merely pushes a single piece of data to the map. * If you want to add more than one string of data, you should used either - * a loop or create a vector of strings and push that back to the object. - */ + * a loop or create a vector of strings and push that back to the object + * using the push_vector function below. */ void push_back(std::string field_name, std::string data); /* Add a field name / vector of strings (data) to the map. * See above notes. Behaves similar to the push_back function. * Also if you push a vector back, it makes a copy of the vector and saves it - * in the map. You will need to use the push_back function to add more data. - */ + * in the map. You will need to use the push_back function to add more data! */ void push_vector(std::string field_name, std::vector data); - // Resets the object and clears the map. - void clear_data(); - // Note: only returns the timestamp, does not add it to the map. std::string generate_timestamp(void); - // This formats the upload string - void format_upload_string(int post_type); - - // This formats one FIELD ID : DATA pair - void format_data(std::vector *vect, array::iterator it, std::string field_ID); - - // This function makes a POST request via libcurl - int post_data_function(int post_type); - // iSENSE API functions bool get_check_user(); // Verifies email / password. bool get_project_fields(); // GETs fields off iSENSE @@ -129,39 +107,31 @@ class iSENSE { std::vector get_dataset(std::string dataset_name, std::string field_name); // Future: return a map of media objects - // map > get_media_objects(); + // map> get_media_objects(); bool post_json_email(); // Post using a email / password bool post_json_key(); // Post using contributor key /* Notes about the append & edit functions * - * Appending & editing by key is tricky. - * In general, you should only be able to append or edit a dataset that you - * uploaded with your contributor key. - * - * You cannot append to just any project with email & password. - * It DOES not work like uploading a dataset - you will only be able to - * append to datasets you own (projects you've created while logged in). - * - * To sum up: * A Contributor key can append to datasets that YOUR KEY uploaded. * Email & password can append to datasets YOU uploaded * OR any datasets in projects YOU created. * - * NOTE: You do not need call - * bool get_datasets_and_mediaobjects(); - * before using any of the editing and appending methods. - * All append methods automatically call this function. - * - * Note - the edit functions are not yet complete. + * You also do not need call get_datasets_and_mediaobjects() before using + * any of the editing and appending methods, as the append methods + * automatically call this function. */ // Appends to a dataset using a dataset name and a key or email/password bool append_key_byName(std::string dataset_name); bool append_email_byName(std::string dataset_name); - // Helper methods + //**************************************************************************** + // Functions below this line should be ignored by users of the API, unless + // an error occurs in one of them. Users should instead use the API functions + // listed above. + std::string get_dataset_ID(std::string dataset_name); std::string get_field_ID(std::string field_name); @@ -169,36 +139,25 @@ class iSENSE { bool empty_project_check(int type, std::string method); bool check_http_code(int http_code, std::string method); -/* Future functions to be implemented at a later date. - // Editing API calls (not yet implemented) - bool get_edit_key(); // Edit a dataset with a contributor key - bool get_edit_user(); // Edit a dataset with a email / password - - // Post media objects - bool post_media_objects_email(); - bool post_media_objects_key(); - - void get_fields_by_id(); // Get information about a field by field ID - void post_fields_email(); // Post fields by email / password - void post_projects_email(); // Post a project by email / password -*/ + // This formats the upload string + void format_upload_string(int post_type); - // For debugging, this method dumps all the data. - void debug(); + // This formats one FIELD ID : DATA pair + void format_data(std::vector *vect, array::iterator it, std::string field_ID); + // This function makes a POST request via libcurl + int post_data_function(int post_type); protected: /* Users do not need to worry about dataset IDs. They only need to pass the * append function a valid dataset name - the name as it appears on iSENSE. - * This method is marked as protected to prevent users from accessing it. - */ + * This method is marked as protected to prevent users from accessing it. */ void set_dataset_ID(std::string proj_dataset_ID); bool append_key_byID(std::string dataset_ID); bool append_email_byID(std::string dataset_ID); private: - /* These two picojson objects will be used to upload to iSENSE. * The upload_data object contains the entire upload string, in JSON format. * Picojson can output this to a string and then we can pass that string @@ -206,36 +165,33 @@ class iSENSE { * The fields_data object is the object that the 'data' part * contains in the upload_data object. * Basically it is a bunch of key:values, with the key being the field ID - * and the value being an array of data (numbers/text/GPS coordinates/etc. - */ + * and the value being an array of data (numbers/text/GPS coordinates/etc. */ object upload_data, fields_data, owner_info; /* These three objects are the data that is pulled off iSENSE. * The get_data object contains all the data we can pull off of iSENSE * (what you find on /api/v1/projects/DATASET_ID_HERE). * The fields object then contains just the field information, and the - * fields_array has that same data in an array form for iterating through it. - */ + * fields_array has that same data in an array form for iterating through it. */ value get_data, fields; array fields_array, data_sets, media_objects; /* Data to be uploaded to iSENSE. The string is the field name, - * the vector of strings contains all the data for that field name. - */ + * the vector of strings contains all the data for that field name. */ std::map > map_data; - // Data needed for processing the upload request //bool usingDev; // Whether the user wants iSENSE or rSENSE // (currently not implemented, future idea) - std::string upload_URL; // URL to upload the JSON to - std::string get_URL; // URL to grab JSON from + // Data needed for processing the upload request std::string get_UserURL; // URL to test credentials + std::string get_URL; // URL to get JSON from + std::string upload_URL; // URL to upload JSON to std::string title; // title for the dataset std::string project_ID; // project ID of the project - std::string dataset_ID; // dataset ID for appending. - std::string contributor_label; // Label for the key, by default "cURL" + std::string dataset_ID; // dataset ID for appending std::string contributor_key; // contributor key for the project + std::string contributor_label; // Label for the key, by default "cURL" std::string email; // Email to be used to upload the data std::string password; // Password to be used with an email address }; diff --git a/C++/API/tests.cpp b/C++/API/tests.cpp index 275ad0b..03d3310 100644 --- a/C++/API/tests.cpp +++ b/C++/API/tests.cpp @@ -285,9 +285,6 @@ BOOST_AUTO_TEST_CASE(append_withDatasetID_byKey) { test.push_back("Text", "DatasetID Test --- Key"); test.push_back("Timestamp", test.generate_timestamp()); - // Dump some data for testing - test.debug(); - BOOST_REQUIRE(test.check_append_key_byID(test_dataset_ID_key) == true); } @@ -311,8 +308,5 @@ BOOST_AUTO_TEST_CASE(append_withDatasetName_byKey) { test.append_key_byName(test_dataset_name_key); - // Dump some data for testing - test.debug(); - BOOST_REQUIRE(test.append_key_byName(test_dataset_name_key) == true); } From 78160e53b04c3e4af75dbdedffa8ed62f85e64c0 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Mon, 28 Sep 2015 13:38:06 -0400 Subject: [PATCH 12/19] fixes --- C++/API/API.cpp | 100 ++++++++++++++++++++++++----------------------- C++/API/Makefile | 4 +- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/C++/API/API.cpp b/C++/API/API.cpp index c29e227..a0fa2a7 100644 --- a/C++/API/API.cpp +++ b/C++/API/API.cpp @@ -422,54 +422,7 @@ std::vector iSENSE::get_dataset(std::string dataset_name, return vector_data; // This should be empty, or may not contain all the data. } -std::string iSENSE::get_field_ID(std::string field_name) { - array::iterator it; - - // Check and see if the fields object is empty - if (fields.is() == true) { - std::cerr << "\nError in method: get_field_ID()\n"; - std::cerr << "Field array wasn't set up."; - std::cerr << "Have you pulled the fields off iSENSE?\n"; - return GET_ERROR; - } - - // We made an iterator above, that will let us run through the fields - for (it = fields_array.begin(); it != fields_array.end(); it++) { - object obj = it->get(); // Get the current object - std::string field_ID = obj["id"].to_str(); // Grab the field ID - std::string name = obj["name"].get(); // Grab the field name - - if (name == field_name) { // Found the given field name - return field_ID; // So return the field ID - } - } - - std::cerr << "\nError in method: get_field_ID()\n"; - std::cerr << "Unable to find the field ID for the given field name.\n"; - return GET_ERROR; -} - -std::string iSENSE::get_dataset_ID(std::string dataset_name) { - array::iterator it; - - // Check and see if the datasets object is empty - // Need to find out how to do this using picojson arrays! - - // This is similar to the get_field_ID function loop. - for (it = data_sets.begin(); it != data_sets.end(); it++) { - object obj = it->get(); // Get the current object - std::string dataset_ID = obj["id"].to_str(); // Grab the dataset ID - std::string name = obj["name"].get(); // Grab the dataset name - - if (name == dataset_name) { // We found the dataset name - return dataset_ID; // So return the dataset ID - } - } - std::cerr << "\nError in method: get_dataset_ID()\n"; - std::cerr << "Unable to find the dataset ID for the given dataset name.\n"; - return GET_ERROR; -} bool iSENSE::post_json_key() { if(!empty_project_check(POST_KEY, "post_json_key()")) { @@ -534,7 +487,6 @@ bool iSENSE::append_key_byName(std::string dataset_name) { // If we got here, we failed to find that dataset name in the current project. std::cerr << "\nError in method: append_key_byName()\n"; std::cerr << "Failed to find the dataset name in project # " << project_ID; - std::cerr << "\nMake sure to type the exact name, as it appears on iSENSE.\n"; return false; } @@ -571,7 +523,6 @@ bool iSENSE::append_email_byName(std::string dataset_name) { // If we got here, we failed to find that dataset name in the current project. std::cerr << "\nError in method: append_email_byName()\n"; std::cerr << "Failed to find the dataset name in project # " << project_ID; - std::cerr << "\nMake sure to type the exact name, as it appears on iSENSE.\n"; return false; } @@ -579,6 +530,57 @@ bool iSENSE::append_email_byName(std::string dataset_name) { // Below this point are helper functions. Users should only call functions // above this point, as these are all called by the API functions. +// Convert field name to field ID +std::string iSENSE::get_field_ID(std::string field_name) { + array::iterator it; + + // Check and see if the fields object is empty + if (fields.is() == true) { + std::cerr << "\nError in method: get_field_ID()\n"; + std::cerr << "Field array wasn't set up."; + std::cerr << "Have you pulled the fields off iSENSE?\n"; + return GET_ERROR; + } + + // We made an iterator above, that will let us run through the fields + for (it = fields_array.begin(); it != fields_array.end(); it++) { + object obj = it->get(); // Get the current object + std::string field_ID = obj["id"].to_str(); // Grab the field ID + std::string name = obj["name"].get(); // Grab the field name + + if (name == field_name) { // Found the given field name + return field_ID; // So return the field ID + } + } + + std::cerr << "\nError in method: get_field_ID()\n"; + std::cerr << "Unable to find the field ID for the given field name.\n"; + return GET_ERROR; +} + +// Convert dataset name to dataset ID +std::string iSENSE::get_dataset_ID(std::string dataset_name) { + array::iterator it; + + // Check and see if the datasets object is empty + // Need to find out how to do this using picojson arrays! + + // This is similar to the get_field_ID function loop. + for (it = data_sets.begin(); it != data_sets.end(); it++) { + object obj = it->get(); // Get the current object + std::string dataset_ID = obj["id"].to_str(); // Grab the dataset ID + std::string name = obj["name"].get(); // Grab the dataset name + + if (name == dataset_name) { // We found the dataset name + return dataset_ID; // So return the dataset ID + } + } + + std::cerr << "\nError in method: get_dataset_ID()\n"; + std::cerr << "Unable to find the dataset ID for the given dataset name.\n"; + return GET_ERROR; +} + // Format JSON Upload strings. void iSENSE::format_upload_string(int post_type) { upload_data["title"] = value(title); // Add the title diff --git a/C++/API/Makefile b/C++/API/Makefile index e48fa2d..43e83b7 100644 --- a/C++/API/Makefile +++ b/C++/API/Makefile @@ -13,11 +13,11 @@ all: tests.out tests.out: tests.o API.o $(CC) tests.o API.o -o tests.out $(CFLAGS) $(Boost) -tests.o: tests.cpp include/API.h include/memfile.h include/picojson/picojson.h +tests.o: tests.cpp include/API.h $(CC) -c tests.cpp $(CFLAGS) # API code -API.o: API.cpp include/API.h include/memfile.h include/picojson/picojson.h +API.o: API.cpp include/API.h $(CC) -c API.cpp $(CFLAGS) clean: From b9d74aa8238f0d6a4e13cff33a688da872f738d0 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Wed, 30 Sep 2015 23:06:30 -0400 Subject: [PATCH 13/19] fixing horrible errors when using curl. libcurl documentation is very helpful. next to simplify the GET requests so there isn't so much repeated curl calls everywhere. --- C++/API/API.cpp | 69 +++++++++++++++++++++------------------ C++/API/include/API.h | 33 +++++++++++++++++-- C++/API/include/memfile.h | 22 ------------- C++/README.md | 47 ++++++++++++++++++++------ 4 files changed, 104 insertions(+), 67 deletions(-) delete mode 100644 C++/API/include/memfile.h diff --git a/C++/API/API.cpp b/C++/API/API.cpp index a0fa2a7..4c245f0 100644 --- a/C++/API/API.cpp +++ b/C++/API/API.cpp @@ -11,14 +11,28 @@ iSENSE::iSENSE() { // Default constructor contributor_label = "label"; email = EMPTY; password = EMPTY; + + // This should be called ONCE, and it setups libcurl. + curl_global_init(CURL_GLOBAL_ALL); } -iSENSE::iSENSE(std::string proj_ID, std::string proj_title, // Constructor with parameters +// Constructor with parameters +iSENSE::iSENSE(std::string proj_ID, std::string proj_title, std::string label, std::string contr_key) { set_project_ID(proj_ID); set_project_title(proj_title); set_project_label(label); set_contributor_key(contr_key); + + // This should be called ONCE for the entire programs lifetime. + // This is simply to setup libcurl. + curl_global_init(CURL_GLOBAL_ALL); +} + +// Override the constructor, we need to make sure we cleanup libcurl. +iSENSE::~iSENSE() { + // Make sure to cleanup libcurl exactly ONCE. + curl_global_cleanup(); } // Similar to the constructor with parameters, but can be called at anytime @@ -139,22 +153,25 @@ std::vector iSENSE::get_projects_search(std::string search_term) { + search_term + "&sort=updated_at&order=DESC"; std::vector project_titles; // Vector of project titles. - CURL *curl = curl_easy_init(); // cURL object + curl = curl_easy_init(); // get curl handle MEMFILE* json_file = memfopen(); // Writing JSON to this file. - long http_code = 0; // HTTP status code if (curl) { curl_easy_setopt(curl, CURLOPT_URL, get_search.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); curl_easy_setopt(curl, CURLOPT_WRITEDATA, json_file); - curl_easy_perform(curl); // Perform the request + // Perform the request, res will get the return code. + res = curl_easy_perform(curl); + + // Get HTTP code for error checking. curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); } - curl_easy_cleanup(curl); // Clean up cURL - curl_global_cleanup(); - + // Check for errors. + if(res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + } if (http_code != HTTP_AUTHORIZED) { // Didn't get HTTP code 200. std::cerr << "\nError in: get_projects_search(string search_term) \n"; std::cerr << "Project search failed.\n"; @@ -203,6 +220,8 @@ std::vector iSENSE::get_projects_search(std::string search_term) { project_titles.push_back(name); // Push the name back into the vector. } + // Always cleanup + curl_easy_cleanup(curl); memfclose(json_file); return project_titles; // Return a vector of project titles } @@ -218,8 +237,7 @@ bool iSENSE::get_check_user() { get_UserURL = devURL + "/users/myInfo?email=" + email + "&password=" + password; - CURL *curl = curl_easy_init(); // cURL object - long http_code = 0; // HTTP status code + curl = curl_easy_init(); // get curl handle if (curl) { curl_easy_setopt(curl, CURLOPT_URL, get_UserURL.c_str()); // GET URL @@ -227,8 +245,8 @@ bool iSENSE::get_check_user() { curl_easy_perform(curl); // Perform the request curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); // get HTTP code. } + curl_easy_cleanup(curl); // Always cleanup. - curl_global_cleanup(); if (http_code == HTTP_AUTHORIZED) { return true; @@ -245,9 +263,8 @@ bool iSENSE::get_project_fields() { get_URL = devURL + "/projects/" + project_ID; - CURL *curl = curl_easy_init(); // cURL object + curl = curl_easy_init(); // get curl handle MEMFILE* json_file = memfopen(); // Writing JSON to this file. - long http_code = 0; // HTTP status code if (curl) { curl_easy_setopt(curl, CURLOPT_URL, get_URL.c_str()); // GET URL @@ -257,7 +274,6 @@ bool iSENSE::get_project_fields() { curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); // Store HTTP Code. } curl_easy_cleanup(curl); // Clean up cURL - curl_global_cleanup(); if (http_code != HTTP_AUTHORIZED) { // Check for valid HTTP code. std::cerr << "Error in method: get_project_fields()\n"; @@ -297,9 +313,8 @@ bool iSENSE::get_datasets_and_mediaobjects() { // ALL datasets in that project and ALL media objects in that project get_URL = devURL + "/projects/" + project_ID + "?recur=true"; - CURL *curl = curl_easy_init(); // cURL object + curl = curl_easy_init(); // get curl handle MEMFILE* json_file = memfopen(); // Writing JSON to this file. - long http_code = 0; // HTTP status code if (curl) { curl_easy_setopt(curl, CURLOPT_URL, get_URL.c_str()); // GET URL @@ -309,7 +324,6 @@ bool iSENSE::get_datasets_and_mediaobjects() { curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); // Get HTTP Code } curl_easy_cleanup(curl); // Clean up cURL - curl_global_cleanup(); if (http_code != HTTP_AUTHORIZED) { std::cerr << "\nError in: get_datasets_and_mediaobjects().\n"; @@ -430,7 +444,7 @@ bool iSENSE::post_json_key() { } upload_URL = devURL + "/projects/" + project_ID + "/jsonDataUpload"; - int http_code = post_data_function(POST_KEY); + http_code = post_data_function(POST_KEY); if(!check_http_code(http_code, "post_json_key()")) { return false; @@ -445,7 +459,7 @@ bool iSENSE::post_json_email() { } upload_URL = devURL + "/projects/" + project_ID + "/jsonDataUpload"; - int http_code = post_data_function(POST_EMAIL); + http_code = post_data_function(POST_EMAIL); if(!check_http_code(http_code, "post_json_email()")) { return false; @@ -462,7 +476,7 @@ bool iSENSE::append_key_byID(std::string dataset_ID) { set_dataset_ID(dataset_ID); // Set the dataset_ID upload_URL = devURL + "/data_sets/append"; // Set the append API URL - int http_code = post_data_function(APPEND_KEY); // Call helper function. + http_code = post_data_function(APPEND_KEY); // Call helper function. if(!check_http_code(http_code, "append_key_byID")) { return false; @@ -498,7 +512,7 @@ bool iSENSE::append_email_byID(std::string dataset_ID) { set_dataset_ID(dataset_ID); // Set the dataset_ID upload_URL = devURL + "/data_sets/append"; // Set the API URL - int http_code = post_data_function(APPEND_EMAIL); // Call helper function. + http_code = post_data_function(APPEND_EMAIL); // Call helper function. if(!check_http_code(http_code, "append_email_byID()")) { return false; @@ -662,7 +676,6 @@ int iSENSE::post_data_function(int post_type) { CURL *curl = curl_easy_init(); // cURL object long http_code = 0; // HTTP status code - curl_global_init(CURL_GLOBAL_DEFAULT); // In windows, init the winsock stuff struct curl_slist *headers = NULL; // Headers for uploading via JSON headers = curl_slist_append(headers, "Accept: application/json"); @@ -687,7 +700,6 @@ int iSENSE::post_data_function(int post_type) { curl_easy_perform(curl);// Perform the request, res will get the return code curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); curl_easy_cleanup(curl); // Clean up curl. - curl_global_cleanup(); return http_code; // Return the HTTP code we get from curl. } @@ -840,7 +852,6 @@ void iSENSE::debug() { //****************************************************************************** // These are needed for picojson & libcURL. -// Declared in memfile.h but defined below. MEMFILE* memfopen() { MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE)); mf->data = NULL; @@ -849,18 +860,13 @@ MEMFILE* memfopen() { } void memfclose(MEMFILE* mf) { - // Double check to make sure that mf exists. - if (mf == NULL) { + if (mf == NULL) { // Double check to make sure that mf exists. return; } - - // OK to free the char array - if (mf != NULL && mf->data) { + if (mf != NULL && mf->data) { // OK to free the char array free(mf->data); } - - // And OK to free the structure - free(mf); + free(mf); // And OK to free the structure } // Simple function only used by the get_check_user function to @@ -878,7 +884,6 @@ size_t memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) { } else { mf->data = (char*) realloc(mf->data, mf->size + block); } - if (mf->data) { memcpy(mf->data + mf->size, ptr, block); mf->size += block; diff --git a/C++/API/include/API.h b/C++/API/include/API.h index a8a359a..1ddb635 100644 --- a/C++/API/include/API.h +++ b/C++/API/include/API.h @@ -11,12 +11,11 @@ #endif #include "picojson/picojson.h" -#include "memfile.h" -#include #include #include #include #include +#include // For picojson using namespace picojson; @@ -46,6 +45,23 @@ const int CURL_ERROR = -1; const std::string GET_ERROR = "ERROR"; const std::string EMPTY = "-----"; +/* + * This is from the picojson example page + * I use it to save the JSON from iSENSE to memory (temporary) + * See the following URL for an example: + * https://github.com/kazuho/picojson/blob/master/examples/github-issues.cc + */ +typedef struct { + char* data; // response data from server + size_t size; // response size of data +} MEMFILE; + +MEMFILE* memfopen(); +void memfclose(MEMFILE* mf); +size_t suppress_output(char* ptr, size_t size, size_t nmemb, void* stream); +size_t memfwrite(char* ptr, size_t size, size_t nmemb, void* stream); +char* memfstrdup(MEMFILE* mf); + class iSENSE { public: // Constructors @@ -53,6 +69,9 @@ class iSENSE { iSENSE(std::string proj_ID, std::string proj_title, std::string label, std::string contr_key); + // Destructor for cleaning up stuff. + ~iSENSE(); + // Similar to the constructor with parameters, but called after // the object is created. This way you can change the title/project ID/etc. void set_project_all(std::string proj_ID, std::string proj_title, @@ -73,7 +92,7 @@ class iSENSE { void debug(); // For debugging, this method dumps all the data. /* This function will push data back to the map. - * User must give the pushback function the following: + * User must give the push_back function the following: * 1. Field name (as seen on iSENSE) * 2. Some data (in string format). For numbers, use std::to_string. * @@ -194,6 +213,14 @@ class iSENSE { std::string contributor_label; // Label for the key, by default "cURL" std::string email; // Email to be used to upload the data std::string password; // Password to be used with an email address + + // libcurl objects / variables. Users should ignore this. + // Defined once as the libcurl tutorial says to do: + // http://curl.haxx.se/libcurl/c/libcurl-tutorial.html + CURL *curl; // curl handle + CURLcode res; // curl response code + MEMFILE* json_file; // JSON file in memory. + long http_code; // HTTP status code }; #endif diff --git a/C++/API/include/memfile.h b/C++/API/include/memfile.h deleted file mode 100644 index b721645..0000000 --- a/C++/API/include/memfile.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef memfile_h -#define memfile_h - -/* - * This is from the picojson example page - * I use it to save the JSON from iSENSE to memory (temporary) - * See the following URL for an example: - * https://github.com/kazuho/picojson/blob/master/examples/github-issues.cc - */ - -typedef struct { - char* data; // response data from server - size_t size; // response size of data -} MEMFILE; - -MEMFILE* memfopen(); -void memfclose(MEMFILE* mf); -size_t suppress_output(char* ptr, size_t size, size_t nmemb, void* stream); -size_t memfwrite(char* ptr, size_t size, size_t nmemb, void* stream); -char* memfstrdup(MEMFILE* mf); - -#endif diff --git a/C++/README.md b/C++/README.md index ced78b4..d1d06be 100644 --- a/C++/README.md +++ b/C++/README.md @@ -4,9 +4,11 @@ C++ API Code It is recommended that you compile any projects using the iSENSE C++ API code using either Linux or Mac OS X. -The API code works in windows but it is not as easy to setup and requires using Visual Studios. +The API code works in windows but it is not as easy to setup and requires using +Visual Studios. -Some of these examples use cURL through the libcURL library for C. Below is the setup guide for various operating systems. +The C++ API uses the libcurl library for C / C++. +Below is the setup guide for various operating systems. ##Linux You can get libcurl on Linux (Ubuntu/Debian) by running the following commands: @@ -16,20 +18,40 @@ sudo apt-get update sudo apt-get install curl libcurl4-gnutls-dev ``` -In order to run the makefile included in the API directory, you will need to install the Boost unit testing library. You can either find this library at the Boost website or in a Debian based Linux distribution just run the following command: +If you do not install libcurl, you will see the following error: -(note: requires 100MB of available storage space) +``` +g++ -c tests.cpp -Wall -Werror -pedantic -std=c++0x -lcurl +In file included from tests.cpp:1:0: +include/API.h:10:23: fatal error: curl/curl.h: No such file or directory + #include + ^ +compilation terminated. +make: *** [tests.o] Error 1 +``` + +In order to run the makefile included in the API directory, you will need to +install the Boost unit testing library. You can either find this library at the +Boost website or in a Debian based Linux distribution (like Ubuntu) just run the +following command: + +(note: requires ~100MB of available storage space) ``` +sudo apt-get update sudo apt-get install libboost-test-dev ``` -If those prerequisites are not installed, you may see the following error: +If you do not install the boost unit testing library, you will see the following +error: ``` -g++ -O0 -g -Wall -Wextra -pedantic-errors -w -std=c++11 POST_simple.cpp -o simple.out -lcurl -POST_simple.cpp:1:65: fatal error: curl/curl.h: No such file or directory -#include +g++ -c tests.cpp -Wall -Werror -pedantic -std=c++0x -lcurl +tests.cpp:13:36: fatal error: boost/test/unit_test.hpp: No such file or directory + #include + ^ +compilation terminated. +make: *** [tests.o] Error 1 ``` When compiling with curl, be sure to include "-lcurl" at the end of the gcc/g++ command. @@ -40,6 +62,10 @@ Example: g++ GET_basic_curl.cpp -lcurl ``` +You should also use the provided Makefile for running the C++ tests. You can +modify this Makefile to run your own programs, or see the iSENSE/Teaching +repository for examples on using the C++ API. + ##MacOS X MacOS users should have curl / libcurl installed by default. You will however need to add "-lcurl" to the Other Linker Flags in Xcode or when using GCC. @@ -66,10 +92,11 @@ Confirmed working in Windows 7, 8.1 (x64), Mac OS X 10.10.1 Yosemite and Ubuntu ##NOTES: -For some of these programs, a JSON library called picojson is used to serialize and parse JSON. +The C++ API wrapper also uses a JSON library called picojson to serialize and parse JSON. [The latest PicoJSON can be found here.](https://raw.githubusercontent.com/kazuho/picojson/master/picojson.h) -It is also included it in the iSENSE API repository as a submodule, so if you git clone the API repository it will automatically git clone the picojson repository. +It is also included it in the iSENSE API repository as a submodule, so if you +git clone the API repository it will automatically git clone the picojson repository. Picojson is contained in one header file called "picojson.h". From 378d9803993a88a76df11b91085c2ee14ec038f6 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Thu, 1 Oct 2015 00:04:57 -0400 Subject: [PATCH 14/19] one test currently failing, next to fix new get function before using it --- C++/API/API.cpp | 132 +++++++++++++++++++++++------------------- C++/API/include/API.h | 17 ++++-- 2 files changed, 85 insertions(+), 64 deletions(-) diff --git a/C++/API/API.cpp b/C++/API/API.cpp index 4c245f0..82c6f2c 100644 --- a/C++/API/API.cpp +++ b/C++/API/API.cpp @@ -1,5 +1,8 @@ #include "include/API.h" +// Global so we can reuse it for all GET requests. +std::string json_str; // JSON from GET requests + iSENSE::iSENSE() { // Default constructor upload_URL = EMPTY; get_URL = EMPTY; @@ -12,8 +15,7 @@ iSENSE::iSENSE() { // Default constructor email = EMPTY; password = EMPTY; - // This should be called ONCE, and it setups libcurl. - curl_global_init(CURL_GLOBAL_ALL); + curl_global_init(CURL_GLOBAL_ALL); // Setup libcurl exactly once. } // Constructor with parameters @@ -24,15 +26,12 @@ iSENSE::iSENSE(std::string proj_ID, std::string proj_title, set_project_label(label); set_contributor_key(contr_key); - // This should be called ONCE for the entire programs lifetime. - // This is simply to setup libcurl. - curl_global_init(CURL_GLOBAL_ALL); + curl_global_init(CURL_GLOBAL_ALL); // Setup libcurl exactly once. } // Override the constructor, we need to make sure we cleanup libcurl. iSENSE::~iSENSE() { - // Make sure to cleanup libcurl exactly ONCE. - curl_global_cleanup(); + curl_global_cleanup(); // Make sure to cleanup libcurl exactly ONCE. } // Similar to the constructor with parameters, but can be called at anytime @@ -149,53 +148,31 @@ void iSENSE::push_vector(std::string field_name, std::vector data) // Searches for projects with the search term. std::vector iSENSE::get_projects_search(std::string search_term) { - std::string get_search = devURL + "/projects?utf8=true&search=" \ - + search_term + "&sort=updated_at&order=DESC"; + get_URL = devURL + "/projects?utf8=true&search=" + search_term + "&sort=updated_at&order=DESC"; std::vector project_titles; // Vector of project titles. - curl = curl_easy_init(); // get curl handle - MEMFILE* json_file = memfopen(); // Writing JSON to this file. + http_code = get_data_funct(GET_NORMAL); // get data off iSENSE. - if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, get_search.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, json_file); - - // Perform the request, res will get the return code. - res = curl_easy_perform(curl); - - // Get HTTP code for error checking. - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - } - - // Check for errors. - if(res != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } - if (http_code != HTTP_AUTHORIZED) { // Didn't get HTTP code 200. + // Check for errors. We need to get a code 200 for this method. + if (http_code != HTTP_AUTHORIZED) { std::cerr << "\nError in: get_projects_search(string search_term) \n"; std::cerr << "Project search failed.\n"; - std::cerr << "Something with either curl, your internet connection, \n"; + std::cerr << "Something with either curl, your Internet connection, \n"; std::cerr << "iSENSE or something else failed.\n"; - memfclose(json_file); - - return project_titles; + return project_titles; // Return an empty vector } - std::string errors; value projects_json; - // Parse the JSON file. - parse(projects_json, json_file->data, json_file->data + json_file->size, &errors); + // Parse the JSON file, just like the main page of PicoJSON does. + std::string errors = parse(projects_json, json_str); // If we have errors, print them out and quit. - if (errors.empty() != true) { + if ( !errors.empty() ) { std::cerr << "\nError in: get_projects_search(string search_term)"; std::cerr << "Error parsing JSON file.\n"; std::cerr << "Error was: " << errors << "\n"; - memfclose(json_file); - - return project_titles; // Return an empty vector + return project_titles; // Return an empty vector } // Convert the JSON array (projects_json) into a vector of project title strings @@ -209,21 +186,16 @@ std::vector iSENSE::get_projects_search(std::string search_term) { if (the_begin == the_end) { std::cerr << "\nError in: get_projects_search(string search_term) \n"; std::cerr << "Project title array is empty.\n"; - memfclose(json_file); - - return project_titles; // Return an empty vector + return project_titles; // Return an empty vector } for (it = projects_array.begin(); it != projects_array.end(); it++) { - object obj = it->get(); // Get the current object - std::string name = obj["name"].get(); // Grab the field name - project_titles.push_back(name); // Push the name back into the vector. + object obj = it->get(); // Get the current obj + std::string name = obj["name"].get(); // Grab the field name + project_titles.push_back(name); // Add to the vector. } - // Always cleanup - curl_easy_cleanup(curl); - memfclose(json_file); - return project_titles; // Return a vector of project titles + return project_titles; // Return a vector of project titles } bool iSENSE::get_check_user() { @@ -235,18 +207,9 @@ bool iSENSE::get_check_user() { return false; } - get_UserURL = devURL + "/users/myInfo?email=" + email + "&password=" + password; - - curl = curl_easy_init(); // get curl handle - - if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, get_UserURL.c_str()); // GET URL - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, suppress_output); // Suppress output to STDOUT - curl_easy_perform(curl); // Perform the request - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); // get HTTP code. - } + get_URL = devURL + "/users/myInfo?email=" + email + "&password=" + password; - curl_easy_cleanup(curl); // Always cleanup. + http_code = get_data_funct(GET_QUIET); // quietly get data off iSENSE. if (http_code == HTTP_AUTHORIZED) { return true; @@ -544,6 +507,41 @@ bool iSENSE::append_email_byName(std::string dataset_name) { // Below this point are helper functions. Users should only call functions // above this point, as these are all called by the API functions. +// GET data off of iSENSE using libcurl. Save the result in a MEMFILE called +// JSON data. Do some magic on this file to get it into a C++ string. +// Returns the HTTP code it gets, and stores data in a string. +int iSENSE::get_data_funct(int get_type) { + curl = curl_easy_init(); // get curl handle + + if (curl) { + // Normal GET parameters + curl_easy_setopt(curl, CURLOPT_URL, get_URL.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback); + + // For get_check_user() we stop libcurl from outputting to STDOUT. + if (get_type == GET_QUIET) { + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, suppress_output); + } + + //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //tell curl to output its progress + + // Perform the request, res will get the return code. + res = curl_easy_perform(curl); + + // Get HTTP code for error checking. + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + } + + // Check for errors. + if(res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed in get_data(): %s\n", + curl_easy_strerror(res)); + } + + return http_code; +} + + // Convert field name to field ID std::string iSENSE::get_field_ID(std::string field_name) { array::iterator it; @@ -851,6 +849,20 @@ void iSENSE::debug() { //****************************************************************************** +// This is a better write function. +// See this URL for details: http://www.cplusplus.com/forum/unices/45878/ +size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up) { + json_str.clear(); // json_str gets reused so always clear it first. + + // buf is a pointer to the data that curl has for us + // size * nmemb is the size of the buffer + for (int c = 0; (unsigned)c < size * nmemb; c++) { + json_str.push_back(buf[c]); + } + + return size * nmemb; // tell curl how many bytes we handled +} + // These are needed for picojson & libcURL. MEMFILE* memfopen() { MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE)); diff --git a/C++/API/include/API.h b/C++/API/include/API.h index 1ddb635..0814d1f 100644 --- a/C++/API/include/API.h +++ b/C++/API/include/API.h @@ -27,6 +27,10 @@ const std::string live_baseURL = "http://isenseproject.org"; const std::string devURL = "http://rsense-dev.cs.uml.edu/api/v1"; const std::string liveURL = "http://isenseproject.org/api/v1"; +// GET related constants +const int GET_NORMAL = 1; +const int GET_QUIET = 2; + // POST related constants const int POST_KEY = 1; const int APPEND_KEY = 2; @@ -45,6 +49,9 @@ const int CURL_ERROR = -1; const std::string GET_ERROR = "ERROR"; const std::string EMPTY = "-----"; +// This is required for libcurl to save the JSON from iSENSE in a C++ string. +size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up); + /* * This is from the picojson example page * I use it to save the JSON from iSENSE to memory (temporary) @@ -164,6 +171,9 @@ class iSENSE { // This formats one FIELD ID : DATA pair void format_data(std::vector *vect, array::iterator it, std::string field_ID); + // This function makes a GET request via libcurl + int get_data_funct(int get_type); + // This function makes a POST request via libcurl int post_data_function(int post_type); @@ -217,10 +227,9 @@ class iSENSE { // libcurl objects / variables. Users should ignore this. // Defined once as the libcurl tutorial says to do: // http://curl.haxx.se/libcurl/c/libcurl-tutorial.html - CURL *curl; // curl handle - CURLcode res; // curl response code - MEMFILE* json_file; // JSON file in memory. - long http_code; // HTTP status code + CURL *curl; // curl handle + CURLcode res; // curl response code + long http_code; // HTTP status code }; #endif From ae8b124edae5d49474a270fd5a2e530ec8ff909a Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Thu, 1 Oct 2015 11:27:24 -0400 Subject: [PATCH 15/19] more simplifying, GET requests basically done. Next to try and make JSON parsing easier. --- C++/API/API.cpp | 208 ++++++++++++++-------------------------------- C++/API/tests.cpp | 1 + 2 files changed, 63 insertions(+), 146 deletions(-) diff --git a/C++/API/API.cpp b/C++/API/API.cpp index 82c6f2c..888a308 100644 --- a/C++/API/API.cpp +++ b/C++/API/API.cpp @@ -148,17 +148,13 @@ void iSENSE::push_vector(std::string field_name, std::vector data) // Searches for projects with the search term. std::vector iSENSE::get_projects_search(std::string search_term) { - get_URL = devURL + "/projects?utf8=true&search=" + search_term + "&sort=updated_at&order=DESC"; + get_URL = devURL + "/projects?&search=" + search_term; std::vector project_titles; // Vector of project titles. http_code = get_data_funct(GET_NORMAL); // get data off iSENSE. // Check for errors. We need to get a code 200 for this method. - if (http_code != HTTP_AUTHORIZED) { - std::cerr << "\nError in: get_projects_search(string search_term) \n"; - std::cerr << "Project search failed.\n"; - std::cerr << "Something with either curl, your Internet connection, \n"; - std::cerr << "iSENSE or something else failed.\n"; + if( !check_http_code(http_code, "get_projects_search") ) { return project_titles; // Return an empty vector } @@ -208,7 +204,6 @@ bool iSENSE::get_check_user() { } get_URL = devURL + "/users/myInfo?email=" + email + "&password=" + password; - http_code = get_data_funct(GET_QUIET); // quietly get data off iSENSE. if (http_code == HTTP_AUTHORIZED) { @@ -225,41 +220,24 @@ bool iSENSE::get_project_fields() { } get_URL = devURL + "/projects/" + project_ID; + http_code = get_data_funct(GET_NORMAL); // get data off iSENSE. - curl = curl_easy_init(); // get curl handle - MEMFILE* json_file = memfopen(); // Writing JSON to this file. - - if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, get_URL.c_str()); // GET URL - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); // Write function. - curl_easy_setopt(curl, CURLOPT_WRITEDATA, json_file); // JSON saved here. - curl_easy_perform(curl); // peform the GET request - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); // Store HTTP Code. - } - curl_easy_cleanup(curl); // Clean up cURL - - if (http_code != HTTP_AUTHORIZED) { // Check for valid HTTP code. - std::cerr << "Error in method: get_project_fields()\n"; - std::cerr << "GET project fields failed. Project ID may be invalid.\n"; - memfclose(json_file); // Close the memfile - - return false; + // Check for errors. We need to get a code 200 for this method. + if( !check_http_code(http_code, "get_projects_fields") ) { + return project_titles; // Return an empty vector } - std::string errors; - // Parse the JSON file. - parse(get_data, json_file->data, json_file->data + json_file->size, &errors); - memfclose(json_file); // Close the memfile + // Parse the JSON file, just like the main page of PicoJSON does. + std::string errors = parse(get_data, json_str); - if (!errors.empty()) { // If we have errors, print them out and quit. + if ( !errors.empty() ) { // If we have errors, print them out and quit. std::cerr << "\nError parsing JSON file in method: get_project_fields()\n"; std::cerr << "Error was: " << errors; - return false; } + fields = get_data.get("fields"); // Save the fields to the field array fields_array = fields.get(); - return true; } @@ -275,35 +253,19 @@ bool iSENSE::get_datasets_and_mediaobjects() { // The "?recur=true" will make iSENSE return: // ALL datasets in that project and ALL media objects in that project get_URL = devURL + "/projects/" + project_ID + "?recur=true"; - - curl = curl_easy_init(); // get curl handle - MEMFILE* json_file = memfopen(); // Writing JSON to this file. - - if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, get_URL.c_str()); // GET URL - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); // Write function - curl_easy_setopt(curl, CURLOPT_WRITEDATA, json_file); // JSON saved here - curl_easy_perform(curl); // Perform the request - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); // Get HTTP Code - } - curl_easy_cleanup(curl); // Clean up cURL + http_code = get_data_funct(GET_NORMAL); // get data off iSENSE. if (http_code != HTTP_AUTHORIZED) { std::cerr << "\nError in: get_datasets_and_mediaobjects().\n"; std::cerr << "GET project fields failed.\n"; std::cerr << "Is the project ID you entered valid?\n"; - memfclose(json_file); // Close the memfile - return false; } - std::string errors; - - // Parse the JSON file. - parse(get_data, json_file->data, json_file->data + json_file->size, &errors); - memfclose(json_file); // close the memfile + // Parse the JSON file, just like the main page of PicoJSON does. + std::string errors = parse(get_data, json_str); - if (errors.empty() != true) { // If we have errors, print them out and quit. + if ( !errors.empty() ) { // If we have errors, print them out and quit. std::cerr << "\nError in method: get_datasets_and_mediaobjects()\n"; std::cerr << "Parsing JSON file failed. Error was: " << errors; return false; @@ -399,8 +361,6 @@ std::vector iSENSE::get_dataset(std::string dataset_name, return vector_data; // This should be empty, or may not contain all the data. } - - bool iSENSE::post_json_key() { if(!empty_project_check(POST_KEY, "post_json_key()")) { return false; @@ -511,7 +471,8 @@ bool iSENSE::append_email_byName(std::string dataset_name) { // JSON data. Do some magic on this file to get it into a C++ string. // Returns the HTTP code it gets, and stores data in a string. int iSENSE::get_data_funct(int get_type) { - curl = curl_easy_init(); // get curl handle + curl = curl_easy_init(); // get curl handle + json_str.clear(); // If the json string was used previously, erase it. if (curl) { // Normal GET parameters @@ -541,6 +502,51 @@ int iSENSE::get_data_funct(int get_type) { return http_code; } +// This function is called by all of the POST functions. +int iSENSE::post_data_function(int post_type) { + // Upload_URL must have already been set. Otherwise the request will fail. + if (upload_URL == EMPTY || upload_URL.empty()) { + std::cerr << "\nError in method: post_data_function()\n"; + std::cerr << "Please set a valid upload URL.\n"; + return CURL_ERROR; + } + + format_upload_string(post_type); // format the upload string + + CURL *curl = curl_easy_init(); // cURL object + long http_code = 0; // HTTP status code + + struct curl_slist *headers = NULL; // Headers for uploading via JSON + headers = curl_slist_append(headers, "Accept: application/json"); + headers = curl_slist_append(headers, "Accept-Charset: utf-8"); + headers = curl_slist_append(headers, "charsets: utf-8"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + + if (curl) { + // Get the upload JSON as a std::string + std::string upload_str = (value(upload_data).serialize()); + + // POST data + curl_easy_setopt(curl, CURLOPT_URL, upload_URL.c_str()); // URL + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, upload_str.c_str()); // JSON data + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // JSON Headers + + // Disable output from curl. + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, suppress_output); + + // Verbose debug output - turn this on if you are having problems uploading. + // std::cout << "\nrSENSE response: \n"; + // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + curl_easy_perform(curl);// Perform the request, res will get the return code + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + curl_easy_cleanup(curl); // Clean up curl. + + return http_code; // Return the HTTP code we get from curl. + } + + return CURL_ERROR; // If curl fails, return CURL_ERROR (-1). +} // Convert field name to field ID std::string iSENSE::get_field_ID(std::string field_name) { @@ -549,7 +555,7 @@ std::string iSENSE::get_field_ID(std::string field_name) { // Check and see if the fields object is empty if (fields.is() == true) { std::cerr << "\nError in method: get_field_ID()\n"; - std::cerr << "Field array wasn't set up."; + std::cerr << "Field array wasn't set up.\n"; std::cerr << "Have you pulled the fields off iSENSE?\n"; return GET_ERROR; } @@ -660,51 +666,6 @@ void iSENSE::format_data(std::vector *vect, fields_data[field_ID] = value(data); // Push the JSON array to the upload_data obj. } - -// This function is called by all of the POST functions. -int iSENSE::post_data_function(int post_type) { - // Upload_URL must have already been set. Otherwise the request will fail. - if (upload_URL == EMPTY || upload_URL.empty()) { - std::cerr << "\nError in method: post_data_function()\n"; - std::cerr << "Please set a valid upload URL.\n"; - return CURL_ERROR; - } - - format_upload_string(post_type); // format the upload string - - CURL *curl = curl_easy_init(); // cURL object - long http_code = 0; // HTTP status code - - struct curl_slist *headers = NULL; // Headers for uploading via JSON - headers = curl_slist_append(headers, "Accept: application/json"); - headers = curl_slist_append(headers, "Accept-Charset: utf-8"); - headers = curl_slist_append(headers, "charsets: utf-8"); - headers = curl_slist_append(headers, "Content-Type: application/json"); - - if (curl) { - // Get the upload JSON as a std::string - std::string upload_str = (value(upload_data).serialize()); - - // POST data - curl_easy_setopt(curl, CURLOPT_URL, upload_URL.c_str()); // URL - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, upload_str.c_str()); // JSON data - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // JSON Headers - - // Verbose debug output - turn this on if you are having problems uploading. - // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - - std::cout << "\nrSENSE response: \n"; - - curl_easy_perform(curl);// Perform the request, res will get the return code - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - curl_easy_cleanup(curl); // Clean up curl. - - return http_code; // Return the HTTP code we get from curl. - } - - return CURL_ERROR; // If curl fails, return CURL_ERROR (-1). -} - // Checks to see if the given project has been properly setup. // Shouldn't be any empty values, such as project ID, contributor key, etc. bool iSENSE::empty_project_check(int type, std::string method) { @@ -852,8 +813,6 @@ void iSENSE::debug() { // This is a better write function. // See this URL for details: http://www.cplusplus.com/forum/unices/45878/ size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up) { - json_str.clear(); // json_str gets reused so always clear it first. - // buf is a pointer to the data that curl has for us // size * nmemb is the size of the buffer for (int c = 0; (unsigned)c < size * nmemb; c++) { @@ -863,51 +822,8 @@ size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up) { return size * nmemb; // tell curl how many bytes we handled } -// These are needed for picojson & libcURL. -MEMFILE* memfopen() { - MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE)); - mf->data = NULL; - mf->size = 0; - return mf; -} - -void memfclose(MEMFILE* mf) { - if (mf == NULL) { // Double check to make sure that mf exists. - return; - } - if (mf != NULL && mf->data) { // OK to free the char array - free(mf->data); - } - free(mf); // And OK to free the structure -} - // Simple function only used by the get_check_user function to // suppress curl's output to the screen. size_t suppress_output(char* ptr, size_t size, size_t nmemb, void* stream) { return size * nmemb; } - -size_t memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) { - MEMFILE* mf = (MEMFILE*) stream; - int block = size * nmemb; - - if (!mf->data) { - mf->data = (char*) malloc(block); - } else { - mf->data = (char*) realloc(mf->data, mf->size + block); - } - if (mf->data) { - memcpy(mf->data + mf->size, ptr, block); - mf->size += block; - } - - return block; -} - -char* memfstrdup(MEMFILE* mf) { - char* buf = (char*)malloc(mf->size + 1); - memcpy(buf, mf->data, mf->size); - buf[mf->size] = 0; - - return buf; -} diff --git a/C++/API/tests.cpp b/C++/API/tests.cpp index 03d3310..88d7f18 100644 --- a/C++/API/tests.cpp +++ b/C++/API/tests.cpp @@ -117,6 +117,7 @@ BOOST_AUTO_TEST_CASE(get_projects_search) { std::vector project_titles; project_titles = test_true.get_projects_search(test_search_true); + //test_true.debug(); BOOST_REQUIRE(project_titles.empty() == false); // This should not be empty. From 42a0273560b327202176879f3730dd320fa0733e Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Thu, 1 Oct 2015 15:16:05 -0400 Subject: [PATCH 16/19] get json data is no longer global. need to fix append methods again. --- C++/API/API.cpp | 42 +++++++++++++++++++----------------------- C++/API/README.md | 2 +- C++/API/include/API.h | 35 ++++++++++++----------------------- C++/API/tests.cpp | 1 - 4 files changed, 32 insertions(+), 48 deletions(-) diff --git a/C++/API/API.cpp b/C++/API/API.cpp index 888a308..0404b25 100644 --- a/C++/API/API.cpp +++ b/C++/API/API.cpp @@ -1,8 +1,5 @@ #include "include/API.h" -// Global so we can reuse it for all GET requests. -std::string json_str; // JSON from GET requests - iSENSE::iSENSE() { // Default constructor upload_URL = EMPTY; get_URL = EMPTY; @@ -154,7 +151,7 @@ std::vector iSENSE::get_projects_search(std::string search_term) { http_code = get_data_funct(GET_NORMAL); // get data off iSENSE. // Check for errors. We need to get a code 200 for this method. - if( !check_http_code(http_code, "get_projects_search") ) { + if( !check_http_code(http_code, "get_projects_search()") ) { return project_titles; // Return an empty vector } @@ -223,8 +220,8 @@ bool iSENSE::get_project_fields() { http_code = get_data_funct(GET_NORMAL); // get data off iSENSE. // Check for errors. We need to get a code 200 for this method. - if( !check_http_code(http_code, "get_projects_fields") ) { - return project_titles; // Return an empty vector + if( !check_http_code(http_code, "get_projects_fields()") ) { + return false; // Return an empty vector } // Parse the JSON file, just like the main page of PicoJSON does. @@ -255,11 +252,9 @@ bool iSENSE::get_datasets_and_mediaobjects() { get_URL = devURL + "/projects/" + project_ID + "?recur=true"; http_code = get_data_funct(GET_NORMAL); // get data off iSENSE. - if (http_code != HTTP_AUTHORIZED) { - std::cerr << "\nError in: get_datasets_and_mediaobjects().\n"; - std::cerr << "GET project fields failed.\n"; - std::cerr << "Is the project ID you entered valid?\n"; - return false; + // Check for errors. We need to get a code 200 for this method. + if( !check_http_code(http_code, "get_datasets_and_mediaobjects()") ) { + return false; // Return an empty vector } // Parse the JSON file, just like the main page of PicoJSON does. @@ -472,16 +467,17 @@ bool iSENSE::append_email_byName(std::string dataset_name) { // Returns the HTTP code it gets, and stores data in a string. int iSENSE::get_data_funct(int get_type) { curl = curl_easy_init(); // get curl handle - json_str.clear(); // If the json string was used previously, erase it. + json_str.clear(); // If the json string was used previously, erase it. if (curl) { // Normal GET parameters curl_easy_setopt(curl, CURLOPT_URL, get_URL.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &iSENSE::writeCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &json_str); // For get_check_user() we stop libcurl from outputting to STDOUT. if (get_type == GET_QUIET) { - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, suppress_output); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &suppress_output); } //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //tell curl to output its progress @@ -532,7 +528,7 @@ int iSENSE::post_data_function(int post_type) { curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // JSON Headers // Disable output from curl. - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, suppress_output); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &suppress_output); // Verbose debug output - turn this on if you are having problems uploading. // std::cout << "\nrSENSE response: \n"; @@ -812,18 +808,18 @@ void iSENSE::debug() { //****************************************************************************** // This is a better write function. // See this URL for details: http://www.cplusplus.com/forum/unices/45878/ -size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up) { - // buf is a pointer to the data that curl has for us - // size * nmemb is the size of the buffer - for (int c = 0; (unsigned)c < size * nmemb; c++) { - json_str.push_back(buf[c]); - } +int iSENSE::writeCallback(char* data, size_t size, size_t nmemb, std::string *buffer) { + int result = 0; + if(buffer != NULL) { + buffer->append(data, size * nmemb); + result = size * nmemb; + } - return size * nmemb; // tell curl how many bytes we handled + return result; // tell curl how many bytes we handled } // Simple function only used by the get_check_user function to // suppress curl's output to the screen. -size_t suppress_output(char* ptr, size_t size, size_t nmemb, void* stream) { +int iSENSE::suppress_output(char* ptr, size_t size, size_t nmemb, void* stream) { return size * nmemb; } diff --git a/C++/API/README.md b/C++/API/README.md index 491d6c5..cf59f33 100644 --- a/C++/API/README.md +++ b/C++/API/README.md @@ -37,4 +37,4 @@ You can also use the following command to redirect all of Boost's error output t ``` ./tests.out --log_sink=fileName.log -``` \ No newline at end of file +``` diff --git a/C++/API/include/API.h b/C++/API/include/API.h index 0814d1f..d37fa80 100644 --- a/C++/API/include/API.h +++ b/C++/API/include/API.h @@ -49,26 +49,6 @@ const int CURL_ERROR = -1; const std::string GET_ERROR = "ERROR"; const std::string EMPTY = "-----"; -// This is required for libcurl to save the JSON from iSENSE in a C++ string. -size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up); - -/* - * This is from the picojson example page - * I use it to save the JSON from iSENSE to memory (temporary) - * See the following URL for an example: - * https://github.com/kazuho/picojson/blob/master/examples/github-issues.cc - */ -typedef struct { - char* data; // response data from server - size_t size; // response size of data -} MEMFILE; - -MEMFILE* memfopen(); -void memfclose(MEMFILE* mf); -size_t suppress_output(char* ptr, size_t size, size_t nmemb, void* stream); -size_t memfwrite(char* ptr, size_t size, size_t nmemb, void* stream); -char* memfstrdup(MEMFILE* mf); - class iSENSE { public: // Constructors @@ -158,6 +138,7 @@ class iSENSE { // an error occurs in one of them. Users should instead use the API functions // listed above. + // Dataset / field ID functions. Given a name, converts to ID std::string get_dataset_ID(std::string dataset_name); std::string get_field_ID(std::string field_name); @@ -177,6 +158,13 @@ class iSENSE { // This function makes a POST request via libcurl int post_data_function(int post_type); + // libcurl function for getting data. See: + // http://www.velvetcache.org/2008/10/24/better-libcurl-from-c + static int writeCallback(char* data, size_t size, size_t nmemb, std::string *buffer); + + // This is used to stop libcurl from outputting to the screen. + static int suppress_output(char* ptr, size_t size, size_t nmemb, void* stream); + protected: /* Users do not need to worry about dataset IDs. They only need to pass the @@ -227,9 +215,10 @@ class iSENSE { // libcurl objects / variables. Users should ignore this. // Defined once as the libcurl tutorial says to do: // http://curl.haxx.se/libcurl/c/libcurl-tutorial.html - CURL *curl; // curl handle - CURLcode res; // curl response code - long http_code; // HTTP status code + CURL *curl; // curl handle + CURLcode res; // curl response code + long http_code; // HTTP status code + std::string json_str; // JSON from GET requests }; #endif diff --git a/C++/API/tests.cpp b/C++/API/tests.cpp index 88d7f18..154fc9c 100644 --- a/C++/API/tests.cpp +++ b/C++/API/tests.cpp @@ -31,7 +31,6 @@ using namespace picojson; * */ - // Constants for running C++ Tests const std::string test_project_ID = "1406"; const std::string test_project_name = "C++ Testing "; From ffc3e6932e9836a53cfd88c0790cf604138dc5b0 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Fri, 2 Oct 2015 12:55:44 -0400 Subject: [PATCH 17/19] appending still failing but it should in theory work. --- C++/API/API.cpp | 5 +---- C++/API/include/API.h | 4 +++- C++/API/tests.cpp | 15 +++++++-------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/C++/API/API.cpp b/C++/API/API.cpp index 0404b25..7a86cdf 100644 --- a/C++/API/API.cpp +++ b/C++/API/API.cpp @@ -1,6 +1,6 @@ #include "include/API.h" -iSENSE::iSENSE() { // Default constructor +iSENSE::iSENSE() { // Default constructor upload_URL = EMPTY; get_URL = EMPTY; get_UserURL = EMPTY; @@ -11,7 +11,6 @@ iSENSE::iSENSE() { // Default constructor contributor_label = "label"; email = EMPTY; password = EMPTY; - curl_global_init(CURL_GLOBAL_ALL); // Setup libcurl exactly once. } @@ -22,7 +21,6 @@ iSENSE::iSENSE(std::string proj_ID, std::string proj_title, set_project_title(proj_title); set_project_label(label); set_contributor_key(contr_key); - curl_global_init(CURL_GLOBAL_ALL); // Setup libcurl exactly once. } @@ -607,7 +605,6 @@ void iSENSE::format_upload_string(int post_type) { case APPEND_KEY: upload_data["contribution_key"] = value(contributor_key); - upload_data["contributor_name"] = value(contributor_label); upload_data["id"] = value(dataset_ID); break; diff --git a/C++/API/include/API.h b/C++/API/include/API.h index d37fa80..88599ab 100644 --- a/C++/API/include/API.h +++ b/C++/API/include/API.h @@ -23,8 +23,10 @@ using namespace picojson; // Currently only rSENSE is supported. In the future, allow switching between // dev and live. const std::string dev_baseURL = "http://rsense-dev.cs.uml.edu"; -const std::string live_baseURL = "http://isenseproject.org"; const std::string devURL = "http://rsense-dev.cs.uml.edu/api/v1"; +const std::string local_baseURL = "http://localhost:3000"; +const std::string localURL = "http://localhost:3000/api/v1"; +const std::string live_baseURL = "http://isenseproject.org"; const std::string liveURL = "http://isenseproject.org/api/v1"; // GET related constants diff --git a/C++/API/tests.cpp b/C++/API/tests.cpp index 154fc9c..a761fd1 100644 --- a/C++/API/tests.cpp +++ b/C++/API/tests.cpp @@ -116,16 +116,17 @@ BOOST_AUTO_TEST_CASE(get_projects_search) { std::vector project_titles; project_titles = test_true.get_projects_search(test_search_true); - //test_true.debug(); - BOOST_REQUIRE(project_titles.empty() == false); // This should not be empty. + // This should not be empty. + BOOST_REQUIRE(project_titles.empty() == false); iSENSE test_false; project_titles.clear(); project_titles = test_false.get_projects_search(test_search_empty); - BOOST_REQUIRE(project_titles.empty() == true); // This should be empty, since its a blank search term. + // This should be empty, since its a blank search term. + BOOST_REQUIRE(project_titles.empty() == true); } @@ -271,7 +272,7 @@ BOOST_AUTO_TEST_CASE(append_withDatasetName_byEmail) { // (Contributor keys) BOOST_AUTO_TEST_CASE(append_withDatasetID_byKey) { Test test; - std::string test_dataset_name_key = "C++ Dataset Append Test Key"; + std::string test_dataset_name_key = "key test"; // Add project info / dataset info to the object test.set_project_ID(test_project_ID); @@ -293,12 +294,12 @@ BOOST_AUTO_TEST_CASE(append_withDatasetID_byKey) { // (Contributor keys) BOOST_AUTO_TEST_CASE(append_withDatasetName_byKey) { iSENSE test; - std::string test_dataset_name_key = "C++ Dataset Append Test Key"; + std::string test_dataset_name_key = "key test"; // Add project info / dataset info to the object test.set_project_ID(test_project_ID); test.set_project_title(test_project_name); - test.set_project_label(test_project_label); + test.set_project_label(test_project_key); test.set_contributor_key(test_project_key); // Push data back to the object. @@ -306,7 +307,5 @@ BOOST_AUTO_TEST_CASE(append_withDatasetName_byKey) { test.push_back("Text", "Dataset Name Test -- Key"); test.push_back("Timestamp", test.generate_timestamp()); - test.append_key_byName(test_dataset_name_key); - BOOST_REQUIRE(test.append_key_byName(test_dataset_name_key) == true); } From 8a292df743b4291a7d3bed920faa5430faba4ae2 Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Fri, 2 Oct 2015 13:54:00 -0400 Subject: [PATCH 18/19] next step, use a better JSON library for C++ --- C++/API/API.cpp | 52 ++++++++--------------------------------------- C++/API/tests.cpp | 14 ------------- 2 files changed, 9 insertions(+), 57 deletions(-) diff --git a/C++/API/API.cpp b/C++/API/API.cpp index 7a86cdf..bd1324e 100644 --- a/C++/API/API.cpp +++ b/C++/API/API.cpp @@ -1,6 +1,6 @@ #include "include/API.h" -iSENSE::iSENSE() { // Default constructor +iSENSE::iSENSE() { // Default constructor upload_URL = EMPTY; get_URL = EMPTY; get_UserURL = EMPTY; @@ -72,14 +72,13 @@ bool iSENSE::set_email_password(std::string proj_email, email = proj_email; password = proj_password; - if (!get_check_user()) { + if ( !get_check_user() ) { std::cerr << "\nError in: set_email_password()\n"; std::cerr << "Your email and password are **not** valid.\n"; std::cerr << "You also need to have created an account on iSENSE.\n"; std::cerr << "See: http://rsense-dev.cs.uml.edu/users/new \n\n"; return false; } - return true; } @@ -111,7 +110,6 @@ void iSENSE::clear_data(void) { // Resets the object and clears the map. map_data.clear(); // Clear the map_data // Clear the picojson objects - // Under the hood picojson::objects are STL maps and picojson::arrays are STL vectors. upload_data.clear(); fields_data.clear(); @@ -144,15 +142,13 @@ void iSENSE::push_vector(std::string field_name, std::vector data) // Searches for projects with the search term. std::vector iSENSE::get_projects_search(std::string search_term) { get_URL = devURL + "/projects?&search=" + search_term; - std::vector project_titles; // Vector of project titles. - + std::vector project_titles; // Vector of project titles. http_code = get_data_funct(GET_NORMAL); // get data off iSENSE. // Check for errors. We need to get a code 200 for this method. if( !check_http_code(http_code, "get_projects_search()") ) { return project_titles; // Return an empty vector } - value projects_json; // Parse the JSON file, just like the main page of PicoJSON does. @@ -165,11 +161,9 @@ std::vector iSENSE::get_projects_search(std::string search_term) { std::cerr << "Error was: " << errors << "\n"; return project_titles; // Return an empty vector } - // Convert the JSON array (projects_json) into a vector of project title strings array projects_array = projects_json.get(); array::iterator it, the_begin, the_end; - the_begin = projects_array.begin(); the_end = projects_array.end(); @@ -185,7 +179,6 @@ std::vector iSENSE::get_projects_search(std::string search_term) { std::string name = obj["name"].get(); // Grab the field name project_titles.push_back(name); // Add to the vector. } - return project_titles; // Return a vector of project titles } @@ -204,7 +197,6 @@ bool iSENSE::get_check_user() { if (http_code == HTTP_AUTHORIZED) { return true; } - return false; } @@ -263,7 +255,6 @@ bool iSENSE::get_datasets_and_mediaobjects() { std::cerr << "Parsing JSON file failed. Error was: " << errors; return false; } - fields = get_data.get("fields"); // Save the fields to the field array fields_array = fields.get(); @@ -315,7 +306,6 @@ std::vector iSENSE::get_dataset(std::string dataset_name, std::cerr << "\n\nUnable to return a vector of data.\n"; std::cerr << "Either the dataset / field names are incorrect, \n"; std::cerr << "Or the project ID is wrong.\n"; - return vector_data; // this is an empty vector } @@ -345,7 +335,6 @@ std::vector iSENSE::get_dataset(std::string dataset_name, } } } - std::cerr << "\n\nError in method: get_dataset(string, string)\n"; std::cerr << "Failed to get dataset. \n"; std::cerr << "Check the following & make sure they are correct:\n"; @@ -365,7 +354,6 @@ bool iSENSE::post_json_key() { if(!check_http_code(http_code, "post_json_key()")) { return false; } - return true; } @@ -380,7 +368,6 @@ bool iSENSE::post_json_email() { if(!check_http_code(http_code, "post_json_email()")) { return false; } - return true; } @@ -397,7 +384,6 @@ bool iSENSE::append_key_byID(std::string dataset_ID) { if(!check_http_code(http_code, "append_key_byID")) { return false; } - return true; } @@ -413,7 +399,6 @@ bool iSENSE::append_key_byName(std::string dataset_name) { if (dataset_ID != GET_ERROR) { return append_key_byID(dataset_ID); // Call append byID function. } - // If we got here, we failed to find that dataset name in the current project. std::cerr << "\nError in method: append_key_byName()\n"; std::cerr << "Failed to find the dataset name in project # " << project_ID; @@ -433,7 +418,6 @@ bool iSENSE::append_email_byID(std::string dataset_ID) { if(!check_http_code(http_code, "append_email_byID()")) { return false; } - return true; } @@ -449,7 +433,6 @@ bool iSENSE::append_email_byName(std::string dataset_name) { if (dataset_ID != GET_ERROR) { return append_email_byID(dataset_ID); // Call append byID function. } - // If we got here, we failed to find that dataset name in the current project. std::cerr << "\nError in method: append_email_byName()\n"; std::cerr << "Failed to find the dataset name in project # " << project_ID; @@ -477,9 +460,6 @@ int iSENSE::get_data_funct(int get_type) { if (get_type == GET_QUIET) { curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &suppress_output); } - - //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //tell curl to output its progress - // Perform the request, res will get the return code. res = curl_easy_perform(curl); @@ -492,7 +472,6 @@ int iSENSE::get_data_funct(int get_type) { fprintf(stderr, "curl_easy_perform() failed in get_data(): %s\n", curl_easy_strerror(res)); } - return http_code; } @@ -506,9 +485,7 @@ int iSENSE::post_data_function(int post_type) { } format_upload_string(post_type); // format the upload string - - CURL *curl = curl_easy_init(); // cURL object - long http_code = 0; // HTTP status code + curl = curl_easy_init(); // cURL object struct curl_slist *headers = NULL; // Headers for uploading via JSON headers = curl_slist_append(headers, "Accept: application/json"); @@ -535,10 +512,8 @@ int iSENSE::post_data_function(int post_type) { curl_easy_perform(curl);// Perform the request, res will get the return code curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); curl_easy_cleanup(curl); // Clean up curl. - return http_code; // Return the HTTP code we get from curl. } - return CURL_ERROR; // If curl fails, return CURL_ERROR (-1). } @@ -560,11 +535,10 @@ std::string iSENSE::get_field_ID(std::string field_name) { std::string field_ID = obj["id"].to_str(); // Grab the field ID std::string name = obj["name"].get(); // Grab the field name - if (name == field_name) { // Found the given field name - return field_ID; // So return the field ID + if (name == field_name) { // Found the given field name + return field_ID; // So return the field ID } } - std::cerr << "\nError in method: get_field_ID()\n"; std::cerr << "Unable to find the field ID for the given field name.\n"; return GET_ERROR; @@ -575,7 +549,7 @@ std::string iSENSE::get_dataset_ID(std::string dataset_name) { array::iterator it; // Check and see if the datasets object is empty - // Need to find out how to do this using picojson arrays! + // Need to find out how to do this using picojson arrays // This is similar to the get_field_ID function loop. for (it = data_sets.begin(); it != data_sets.end(); it++) { @@ -587,7 +561,6 @@ std::string iSENSE::get_dataset_ID(std::string dataset_name) { return dataset_ID; // So return the dataset ID } } - std::cerr << "\nError in method: get_dataset_ID()\n"; std::cerr << "Unable to find the dataset ID for the given dataset name.\n"; return GET_ERROR; @@ -595,7 +568,7 @@ std::string iSENSE::get_dataset_ID(std::string dataset_name) { // Format JSON Upload strings. void iSENSE::format_upload_string(int post_type) { - upload_data["title"] = value(title); // Add the title + upload_data["title"] = value(title); switch (post_type) { case POST_KEY: @@ -641,9 +614,7 @@ void iSENSE::format_upload_string(int post_type) { vect = &map_data[name]; format_data(vect, it, field_ID); } - - // Add the field_data obj to the upload_data obj - upload_data["data"] = value(fields_data); + upload_data["data"] = value(fields_data); // Add field_data obj to upload_data obj } // This makes format_upload_string() much shorter. @@ -655,7 +626,6 @@ void iSENSE::format_data(std::vector *vect, for (x = vect -> begin(); x < vect -> end(); x++) { data.push_back(value(*x)); // Push all the vector data into a JSON array. } - fields_data[field_ID] = value(data); // Push the JSON array to the upload_data obj. } @@ -675,7 +645,6 @@ bool iSENSE::empty_project_check(int type, std::string method) { return false; } } - // Check key based values, such as contributor key and label. if (type == POST_KEY || type == APPEND_KEY) { if (contributor_key == EMPTY || contributor_key.empty()) { @@ -688,7 +657,6 @@ bool iSENSE::empty_project_check(int type, std::string method) { contributor_label = "cURL"; } } - // The rest are general checks that should not be empty, since the calling // method depends on them being set properly. if (project_ID == EMPTY || project_ID.empty()) { @@ -707,7 +675,6 @@ bool iSENSE::empty_project_check(int type, std::string method) { std::cerr << "You should push some data back to this object.\n"; return false; } - return true; } @@ -811,7 +778,6 @@ int iSENSE::writeCallback(char* data, size_t size, size_t nmemb, std::string *bu buffer->append(data, size * nmemb); result = size * nmemb; } - return result; // tell curl how many bytes we handled } diff --git a/C++/API/tests.cpp b/C++/API/tests.cpp index a761fd1..9f3ef71 100644 --- a/C++/API/tests.cpp +++ b/C++/API/tests.cpp @@ -44,7 +44,6 @@ const std::string test_project_label = "Boost"; const std::string test_search_true = "test"; const std::string test_search_empty = "abcdefghig"; - /* * This is a derived class to quickly test the append_byID functions. * It is derived from iSENSE, and by doing this I can create a public function @@ -66,7 +65,6 @@ class Test: public iSENSE { } }; - // Test the Check User method. BOOST_AUTO_TEST_CASE(get_check_user) { iSENSE test; @@ -79,7 +77,6 @@ BOOST_AUTO_TEST_CASE(get_check_user) { BOOST_REQUIRE(test.set_email_password(test_email, test_password) == true); } - // Test the GET project fields method. BOOST_AUTO_TEST_CASE(get_project_fields) { @@ -92,7 +89,6 @@ BOOST_AUTO_TEST_CASE(get_project_fields) { BOOST_REQUIRE(test_false.get_project_fields() == false); } - // Test the get_datasets / media objects method BOOST_AUTO_TEST_CASE(get_datasets_and_mediaobjects) { iSENSE test_true(test_project_ID, test_project_name, test_project_label, test_project_key); @@ -109,7 +105,6 @@ BOOST_AUTO_TEST_CASE(get_datasets_and_mediaobjects) { BOOST_REQUIRE(test_false.get_datasets_and_mediaobjects() == false); } - // Test the search project method BOOST_AUTO_TEST_CASE(get_projects_search) { iSENSE test_true(test_project_ID, test_project_name, test_project_label, test_project_key); @@ -129,7 +124,6 @@ BOOST_AUTO_TEST_CASE(get_projects_search) { BOOST_REQUIRE(project_titles.empty() == true); } - // Test the get dataset method BOOST_AUTO_TEST_CASE(get_dataset) { iSENSE test_true("106", "test", "BOOST Test", "123"); @@ -147,7 +141,6 @@ BOOST_AUTO_TEST_CASE(get_dataset) { BOOST_REQUIRE(baseball_hits.empty() == true); } - // Test the get_dataset_ID method BOOST_AUTO_TEST_CASE(get_dataset_ID) { iSENSE test_true("106", "test", "BOOST Test", "123"); @@ -169,7 +162,6 @@ BOOST_AUTO_TEST_CASE(get_dataset_ID) { BOOST_REQUIRE(datasetID == GET_ERROR); } - // Test the get_field_ID method BOOST_AUTO_TEST_CASE(get_field_ID) { iSENSE test_true("106", "test", "BOOST Test", "123"); @@ -191,7 +183,6 @@ BOOST_AUTO_TEST_CASE(get_field_ID) { BOOST_REQUIRE(fieldID == GET_ERROR); } - // Test POST with Contributor keys BOOST_AUTO_TEST_CASE(post_JSON_withKey) { std::string project_title = "POST Test with Key "; @@ -208,7 +199,6 @@ BOOST_AUTO_TEST_CASE(post_JSON_withKey) { BOOST_REQUIRE(test.post_json_key() == true); } - // Test POST with Email/Password BOOST_AUTO_TEST_CASE(post_JSON_withEmail) { std::string project_title = "POST Test with Email "; @@ -227,7 +217,6 @@ BOOST_AUTO_TEST_CASE(post_JSON_withEmail) { BOOST_REQUIRE(test.post_json_email() == true); } - // Test Appending with Dataset IDs // (Email / Password) BOOST_AUTO_TEST_CASE(append_withDatasetID_byEmail) { @@ -247,7 +236,6 @@ BOOST_AUTO_TEST_CASE(append_withDatasetID_byEmail) { BOOST_REQUIRE(test.check_append_email_byID(test_dataset_ID_email) == true); } - // Test Appending with Dataset names // (Email / Password) BOOST_AUTO_TEST_CASE(append_withDatasetName_byEmail) { @@ -267,7 +255,6 @@ BOOST_AUTO_TEST_CASE(append_withDatasetName_byEmail) { BOOST_REQUIRE(test.append_email_byName(test_dataset_name_email) == true); } - // Test Appending with Dataset IDs // (Contributor keys) BOOST_AUTO_TEST_CASE(append_withDatasetID_byKey) { @@ -289,7 +276,6 @@ BOOST_AUTO_TEST_CASE(append_withDatasetID_byKey) { BOOST_REQUIRE(test.check_append_key_byID(test_dataset_ID_key) == true); } - // Test Appending with Dataset names // (Contributor keys) BOOST_AUTO_TEST_CASE(append_withDatasetName_byKey) { From 686666ebe255552869cae01ca3cdae5ea48c64dd Mon Sep 17 00:00:00 2001 From: Jason Downing Date: Mon, 5 Oct 2015 13:20:07 -0400 Subject: [PATCH 19/19] fixes for live (https) and made email/password null. --- C++/API/include/API.h | 7 +++---- C++/API/tests.cpp | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/C++/API/include/API.h b/C++/API/include/API.h index 88599ab..d62a80f 100644 --- a/C++/API/include/API.h +++ b/C++/API/include/API.h @@ -20,14 +20,13 @@ // For picojson using namespace picojson; -// Currently only rSENSE is supported. In the future, allow switching between -// dev and live. +// Currently only rSENSE is supported. In the future, allow switching between dev and live. const std::string dev_baseURL = "http://rsense-dev.cs.uml.edu"; const std::string devURL = "http://rsense-dev.cs.uml.edu/api/v1"; const std::string local_baseURL = "http://localhost:3000"; const std::string localURL = "http://localhost:3000/api/v1"; -const std::string live_baseURL = "http://isenseproject.org"; -const std::string liveURL = "http://isenseproject.org/api/v1"; +const std::string live_baseURL = "https://isenseproject.org"; +const std::string liveURL = "https://isenseproject.org/api/v1"; // GET related constants const int GET_NORMAL = 1; diff --git a/C++/API/tests.cpp b/C++/API/tests.cpp index 9f3ef71..c0e0bfe 100644 --- a/C++/API/tests.cpp +++ b/C++/API/tests.cpp @@ -37,8 +37,8 @@ const std::string test_project_name = "C++ Testing "; const std::string test_dataset_name = "Testing "; const std::string test_dataset_ID_email = "11366"; const std::string test_dataset_ID_key = "11367"; -const std::string test_email = "j@j.j"; -const std::string test_password = "j"; +const std::string test_email = ""; +const std::string test_password = ""; const std::string test_project_key = "123"; const std::string test_project_label = "Boost"; const std::string test_search_true = "test";