Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CCDB/include/CCDB/CcdbApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,15 @@ class CcdbApi //: public DatabaseInterface
*/
void deleteObject(std::string const& path, long timestamp = -1) const;

/**
* Update the metadata of the object defined by the provided timestamp, and id if provided.
* @param path Path to the object to update
* @param metadata The metadata to update
* @param timestamp The timestamp to select the object
* @param id The id, if any, to select the object
*/
void updateMetadata(std::string const& path, std::map<std::string, std::string> const& metadata, long timestamp, std::string const& id = "");

/**
* Return the listing of objects, and in some cases subfolders, matching this path.
* The path can contain sql patterns (correctly encoded) or regexps.
Expand Down
39 changes: 39 additions & 0 deletions CCDB/src/CcdbApi.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1339,5 +1339,44 @@ TClass* CcdbApi::tinfo2TClass(std::type_info const& tinfo)
return cl;
}

void CcdbApi::updateMetadata(std::string const& path, std::map<std::string, std::string> const& metadata, long timestamp, std::string const& id)
{
CURL* curl;
CURLcode res;
stringstream fullUrl;
fullUrl << mUrl << "/" << path << "/" << timestamp;
if (!id.empty()) {
fullUrl << "/" << id;
}
fullUrl << "?";

curl = curl_easy_init();

for (auto& kv : metadata) {
string mfirst = kv.first;
string msecond = kv.second;
// same trick for the metadata as for the object type
char* mfirstEncoded = curl_easy_escape(curl, mfirst.c_str(), mfirst.size());
char* msecondEncoded = curl_easy_escape(curl, msecond.c_str(), msecond.size());
fullUrl << string(mfirstEncoded) + "=" + string(msecondEncoded) + "&";
curl_free(mfirstEncoded);
curl_free(msecondEncoded);
}

if (curl != nullptr) {
curl_easy_setopt(curl, CURLOPT_URL, fullUrl.str().c_str());
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); // make sure we use PUT

curlSetSSLOptions(curl);

// Perform the request, res will get the return code
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
}

} // namespace ccdb
} // namespace o2
71 changes: 63 additions & 8 deletions CCDB/test/testCcdbApi.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,14 @@
#include "CCDB/CCDBTimeStampUtils.h"
#include <boost/test/unit_test.hpp>
#include <filesystem>
#include <cstdio>
#include <cassert>
#include <iostream>
#include <cstdio>
#include <curl/curl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <TH1F.h>
#include <chrono>
#include <CommonUtils/StringUtils.h>
#include <TMessage.h>
#include <TStreamerInfo.h>
#include <TGraph.h>
#include <TTree.h>
#include <TString.h>
#include <sys/types.h>
#include <unistd.h>

#include <boost/property_tree/json_parser.hpp>
Expand Down Expand Up @@ -479,3 +471,66 @@ BOOST_AUTO_TEST_CASE(TestRetrieveHeaders, *utf::precondition(if_reachable()))
}
BOOST_CHECK_EQUAL(headers.size(), 0);
}

BOOST_AUTO_TEST_CASE(TestUpdateMetadata, *utf::precondition(if_reachable()))
{
test_fixture f;

// upload an object
TH1F h1("object1", "object1", 100, 0, 99);
cout << "storing object 1 in " << basePath << "Test" << endl;
map<string, string> metadata;
metadata["custom"] = "whatever";
metadata["id"] = "first";
f.api.storeAsTFile(&h1, basePath + "Test", metadata);

// retrieve the headers just to be sure
std::map<std::string, std::string> headers = f.api.retrieveHeaders(basePath + "Test", metadata);
BOOST_CHECK(headers.count("custom") > 0);
BOOST_CHECK(headers.at("custom") == "whatever");
string firstID = headers.at("ETag");
firstID.erase(std::remove(firstID.begin(), firstID.end(), '"'), firstID.end());

map<string, string> newMetadata;
newMetadata["custom"] = "somethingelse";

// update the metadata and check
f.api.updateMetadata(basePath + "Test", newMetadata, o2::ccdb::getCurrentTimestamp());
headers = f.api.retrieveHeaders(basePath + "Test", newMetadata);
BOOST_CHECK(headers.count("custom") > 0);
BOOST_CHECK(headers.at("custom") == "somethingelse");

// add a second object
cout << "storing object 2 in " << basePath << "Test" << endl;
metadata.clear();
metadata["custom"] = "whatever";
metadata["id"] = "second";
f.api.storeAsTFile(&h1, basePath + "Test", metadata);

// get id
cout << "get id" << endl;
headers = f.api.retrieveHeaders(basePath + "Test", metadata);
string secondID = headers.at("ETag");
secondID.erase(std::remove(secondID.begin(), secondID.end(), '"'), secondID.end());

// update the metadata by id
cout << "update the metadata by id" << endl;
newMetadata.clear();
newMetadata["custom"] = "first";
f.api.updateMetadata(basePath + "Test", newMetadata, o2::ccdb::getCurrentTimestamp(), firstID);
newMetadata.clear();
newMetadata["custom"] = "second";
f.api.updateMetadata(basePath + "Test", newMetadata, o2::ccdb::getCurrentTimestamp(), secondID);

// check
metadata.clear();
metadata["id"] = "first";
headers = f.api.retrieveHeaders(basePath + "Test", metadata);
BOOST_CHECK(headers.count("custom") > 0);
BOOST_CHECK(headers.at("custom") == "first");
metadata.clear();
metadata["id"] = "second";
headers = f.api.retrieveHeaders(basePath + "Test", metadata);
BOOST_CHECK(headers.count("custom") > 0);
BOOST_CHECK(headers.at("custom") == "second");
}