diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..915eb91 --- /dev/null +++ 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/API.cpp b/C++/API/API.cpp index d6fb4e0..bd1324e 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; @@ -13,9 +11,9 @@ iSENSE::iSENSE() { contributor_label = "label"; email = EMPTY; password = EMPTY; + curl_global_init(CURL_GLOBAL_ALL); // Setup libcurl exactly once. } - // Constructor with parameters iSENSE::iSENSE(std::string proj_ID, std::string proj_title, std::string label, std::string contr_key) { @@ -23,8 +21,13 @@ 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. } +// Override the constructor, we need to make sure we cleanup libcurl. +iSENSE::~iSENSE() { + curl_global_cleanup(); // Make sure to cleanup libcurl exactly ONCE. +} // 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, @@ -35,7 +38,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,314 +46,160 @@ 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) { +void iSENSE::clear_data(void) { // Resets the object and clears the map. 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; - */ - - // Under the hood picojson::objects are STL maps and - // picojson::arrays are STL vectors. + // Clear the picojson objects + // 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) { + 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. - std::string get_search = devURL + "/projects?utf8=true&search=" \ - + search_term + "&sort=updated_at&order=DESC"; - // 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 - - 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_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. - */ - - // 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_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; + // 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 } - - // 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(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"; - - // 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; - the_begin = projects_array.begin(); the_end = projects_array.end(); // 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 obj + std::string name = obj["name"].get(); // Grab the field name + project_titles.push_back(name); // Add to 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; - - // 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); - } + get_URL = devURL + "/users/myInfo?email=" + email + "&password=" + password; + http_code = get_data_funct(GET_QUIET); // quietly get data off iSENSE. - /* 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"; @@ -359,81 +207,27 @@ bool iSENSE::get_project_fields() { } get_URL = devURL + "/projects/" + project_ID; + http_code = get_data_funct(GET_NORMAL); // get data off iSENSE. - // 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 - - 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); - } - - /* The iSENSE API gives us one response code to check against: - * Success: 200 OK - * Failure: 404 Not Found - */ - - if (http_code != HTTP_AUTHORIZED) { - 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); - - 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 false; // Return an empty vector } - // 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; + // Parse the JSON file, just like the main page of PicoJSON does. + std::string errors = parse(get_data, json_str); - // This will parse the JSON file. - parse(get_data, json_file->data, - json_file->data + json_file->size, &errors); - - // 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. @@ -444,96 +238,38 @@ 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"; + http_code = get_data_funct(GET_NORMAL); // get data off iSENSE. - // 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 - - 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(); - } - - /* 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); - - 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 } - // 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, just like the main page of PicoJSON does. + std::string errors = parse(get_data, json_str); - // close the memfile - memfclose(json_file); - - // If we have errors, print them out and quit. - if (errors.empty() != true) { + 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; } - - // 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; @@ -547,21 +283,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 @@ -575,31 +306,13 @@ 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; + 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) { @@ -607,592 +320,256 @@ 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; -} - - -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"; - 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++) { - // 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(); - - // 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; - } - } - - 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 - 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(); - - // 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; - } - } - - 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; + return vector_data; // This should be empty, or may not contain all the data. } - -// 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(POST_KEY, "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"; + 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; } + return true; +} - // If a label wasn't set, automatically set it to "cURL" - if (contributor_label == "label" || contributor_label.empty()) { - contributor_label = "cURL"; - } - - // 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(POST_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"; + 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_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); - - /* - * 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"; + set_dataset_ID(dataset_ID); // Set the dataset_ID + upload_URL = devURL + "/data_sets/append"; // Set the append API URL + http_code = post_data_function(APPEND_KEY); // Call helper function. - // 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_KEY, "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 + 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) { - append_key_byID(dataset_ID); - return true; + 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; - std::cerr << "\nMake sure to type the exact name, as it appears on iSENSE.\n"; 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"; +// Append using email and password +bool iSENSE::append_email_byID(std::string dataset_ID) { + if(!empty_project_check(APPEND_EMAIL,"append_email_byID")) { return false; } - if (email == EMPTY || email.empty()) { - std::cerr << "\nError in method: post_json_email()\n"; - std::cerr << "Please set an email address!\n"; + + set_dataset_ID(dataset_ID); // Set the dataset_ID + upload_URL = devURL + "/data_sets/append"; // Set the API URL + http_code = post_data_function(APPEND_EMAIL); // Call helper function. + + if(!check_http_code(http_code, "append_email_byID()")) { return false; } - if (password == EMPTY || password.empty()) { - std::cerr << "\nError in method: post_json_email()\n"; - std::cerr << "Please set a password!\n"; + return true; +} + +// Appends to a dataset using its dataset name. Calls append_email_byID +bool iSENSE::append_email_byName(std::string dataset_name) { + if(!empty_project_check(APPEND_EMAIL,"append_email_byName")) { 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; + + 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 (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; + return false; +} - // Make sure to set the upload URL! - upload_URL = devURL + "/projects/" + project_ID + "/jsonDataUpload"; +//****************************************************************************** +// Below this point are helper functions. Users should only call functions +// above this point, as these are all called by the API functions. - // Call the POST function, give it type 3 - // since this is upload JSON by email & password. - int http_code = post_data_function(POST_EMAIL); +// 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 + json_str.clear(); // If the json string was used previously, erase it. - /* - * 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 (curl) { + // Normal GET parameters + curl_easy_setopt(curl, CURLOPT_URL, get_URL.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &iSENSE::writeCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &json_str); - 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"; + // For get_check_user() we stop libcurl from outputting to STDOUT. + if (get_type == GET_QUIET) { + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &suppress_output); + } + // Perform the request, res will get the return code. + res = curl_easy_perform(curl); - // 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"; + // Get HTTP code for error checking. + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); } - return false; + // 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; } - -// Post 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"; - return false; +// 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; } - set_dataset_ID(dataset_ID); // Set the dataset_ID + format_upload_string(post_type); // format the upload string + curl = curl_easy_init(); // cURL object - // Change the upload URL to append rather than create a new dataset. - upload_URL = devURL + "/data_sets/append"; + 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"); - // Call the POST function with type 4 for appending via email. - int http_code = post_data_function(APPEND_EMAIL); + if (curl) { + // Get the upload JSON as a std::string + std::string upload_str = (value(upload_data).serialize()); - /* 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.) - */ + // 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 - 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; - } + // Disable output from curl. + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &suppress_output); - std::cerr << "\nError in method: append_email_byID()\n"; - std::cerr << "POST request **failed**\n"; - std::cerr << "HTTP Response Code was: " << http_code << "\n"; + // Verbose debug output - turn this on if you are having problems uploading. + // std::cout << "\nrSENSE response: \n"; + // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - // 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"; + 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 false; + 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) { + array::iterator it; -/* 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. - */ -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; + // 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.\n"; + std::cerr << "Have you pulled the fields off iSENSE?\n"; + return GET_ERROR; } - 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"; - return false; + + // 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; +} - // We can now find the dataset ID by looking at all datasets in this project. - get_datasets_and_mediaobjects(); +// Convert dataset name to dataset ID +std::string iSENSE::get_dataset_ID(std::string dataset_name) { + array::iterator it; - // Call the get_dataset_ID function - std::string dataset_ID = get_dataset_ID(dataset_name); + // Check and see if the datasets object is empty + // Need to find out how to do this using picojson arrays - // 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; - } + // 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 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"; - return false; + 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; } - -// 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); - // 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); @@ -1216,14 +593,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) { @@ -1235,133 +606,113 @@ 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(); - - // 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(); + 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 - // 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 - upload_data["data"] = value(fields_data); + upload_data["data"] = value(fields_data); // Add field_data obj to upload_data obj } - -// 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. - */ -int iSENSE::post_data_function(int post_type) { - // Upload_URL must have already been set. - // Otherwise the POST request will fail unexpectedly. - 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; +// 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; +} - // 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. - */ - - // CURL object and response code. - CURL *curl = curl_easy_init(); // cURL object - long http_code = 0; // HTTP status code - - // 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; - 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()); - - // 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. 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. - // 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_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); +// 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. + } - // Clean up curl. - curl_easy_cleanup(curl); - curl_global_cleanup(); + // 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"; - // Return the HTTP code we get from curl. - return http_code; + 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 curl fails for some reason, return CURL_ERROR (-1). - return CURL_ERROR; + 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() { std::cout << "\nProject Title: " << title << "\n"; @@ -1418,63 +769,20 @@ 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; - mf->size = 0; - return mf; -} - - -void memfclose(MEMFILE* mf) { - // Double check to make sure that mf exists. - if (mf == NULL) { - return; +//****************************************************************************** +// This is a better write function. +// See this URL for details: http://www.cplusplus.com/forum/unices/45878/ +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; } - - // OK to free the char array - if (mf != NULL && mf->data) { - free(mf->data); - } - - // And OK to free the structure - free(mf); + 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; } - - -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/Makefile b/C++/API/Makefile index 533e0a1..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.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.h +API.o: API.cpp include/API.h $(CC) -c API.cpp $(CFLAGS) clean: diff --git a/C++/API/README.md b/C++/API/README.md index 34e2d90..cf59f33 100644 --- a/C++/API/README.md +++ b/C++/API/README.md @@ -1,38 +1,40 @@ -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. + +You can also use the following command to redirect all of Boost's error output to a file: + +``` +./tests.out --log_sink=fileName.log +``` diff --git a/C++/API/include/API.h b/C++/API/include/API.h index 68e7c4b..d62a80f 100644 --- a/C++/API/include/API.h +++ b/C++/API/include/API.h @@ -10,35 +10,27 @@ #include #endif -#include "picojson.h" -#include "memfile.h" -#include +#include "picojson/picojson.h" #include #include #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. - */ +#include // 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"; -const std::string liveURL = "http://isenseproject.org/api/v1"; +const std::string local_baseURL = "http://localhost:3000"; +const std::string localURL = "http://localhost:3000/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; +const int GET_QUIET = 2; // POST related constants const int POST_KEY = 1; @@ -60,60 +52,53 @@ 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); + // 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, 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. // 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: + * 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. * * 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,72 +114,68 @@ 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. + + // 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); -/* 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 + // 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); + + // 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); - // Post media objects - bool post_media_objects_email(); - bool post_media_objects_key(); + // This function makes a GET request via libcurl + int get_data_funct(int get_type); - 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 function makes a POST request via libcurl + int post_data_function(int post_type); - // For debugging, this method dumps all the data. - void debug(); + // 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 * 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 @@ -202,38 +183,43 @@ 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 + + // 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 + std::string json_str; // JSON from GET requests }; #endif diff --git a/C++/API/include/memfile.h b/C++/API/include/memfile.h deleted file mode 100644 index 54d8fa0..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++/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 diff --git a/C++/API/tests.cpp b/C++/API/tests.cpp index 8132439..c0e0bfe 100644 --- a/C++/API/tests.cpp +++ b/C++/API/tests.cpp @@ -31,6 +31,18 @@ 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 = ""; +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"; +const std::string test_search_empty = "abcdefghig"; /* * This is a derived class to quickly test the append_byID functions. @@ -53,43 +65,36 @@ 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); @@ -100,29 +105,25 @@ 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("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); + // This should not be empty. BOOST_REQUIRE(project_titles.empty() == false); 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); + // This should be empty, since its a blank search term. 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"); @@ -140,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"); @@ -151,7 +151,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; @@ -162,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"); @@ -184,13 +183,13 @@ BOOST_AUTO_TEST_CASE(get_field_ID) { BOOST_REQUIRE(fieldID == GET_ERROR); } - -// 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()); @@ -200,14 +199,15 @@ BOOST_AUTO_TEST_CASE(post_JSON_withKey) { BOOST_REQUIRE(test.post_json_key() == true); } - -// 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()); @@ -217,111 +217,81 @@ 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) { 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); } - // Test Appending with Dataset names // (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); } - // Test Appending with Dataset IDs // (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 = "key test"; // 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(); - - test.push_back("Number", "999999"); + // 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()); - BOOST_REQUIRE(test.check_append_key_byID(dataset_ID) == true); + 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) { 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 = "key test"; // 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_key); + 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); } - diff --git a/C++/README.md b/C++/README.md index 60c6157..d1d06be 100644 --- a/C++/README.md +++ b/C++/README.md @@ -1,31 +1,57 @@ -C++ Example Code +C++ API 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 code does work in Visual Studios but it is not trivial to set up. +The API code works in windows but it is not as easy to setup and requires using +Visual Studios. -Setup ------- -Some of these examples use cURL through the libcURL library for C. +The C++ API uses the libcurl library for C / C++. +Below is the setup guide for various operating systems. -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: +If you do not install libcurl, 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 +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 you do not install the boost unit testing library, you will see the following +error: + +``` +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. @@ -36,49 +62,41 @@ 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/) +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 ------ +##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 +[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. -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. -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) +To download Visual Studios 2013, [go to the following website.](http://www.visualstudio.com/) -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. +##Testing +Confirmed working in Windows 7, 8.1 (x64), Mac OS X 10.10.1 Yosemite and Ubuntu 14.04LTS (x64). -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++. -[The one header file you need can be found here.](https://raw.githubusercontent.com/kazuho/picojson/master/picojson.h) +##NOTES: +The C++ API wrapper also uses a JSON library called picojson to serialize and parse JSON. -You can also just git clone the repository using the following command: +[The latest PicoJSON can be found here.](https://raw.githubusercontent.com/kazuho/picojson/master/picojson.h) -``` -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. -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".