Skip to content

Commit 9e5b371

Browse files
authored
[QC-652] Add a method to update the metadata of an object. (#7653)
1 parent eb32968 commit 9e5b371

3 files changed

Lines changed: 111 additions & 8 deletions

File tree

CCDB/include/CCDB/CcdbApi.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,15 @@ class CcdbApi //: public DatabaseInterface
180180
*/
181181
void deleteObject(std::string const& path, long timestamp = -1) const;
182182

183+
/**
184+
* Update the metadata of the object defined by the provided timestamp, and id if provided.
185+
* @param path Path to the object to update
186+
* @param metadata The metadata to update
187+
* @param timestamp The timestamp to select the object
188+
* @param id The id, if any, to select the object
189+
*/
190+
void updateMetadata(std::string const& path, std::map<std::string, std::string> const& metadata, long timestamp, std::string const& id = "");
191+
183192
/**
184193
* Return the listing of objects, and in some cases subfolders, matching this path.
185194
* The path can contain sql patterns (correctly encoded) or regexps.

CCDB/src/CcdbApi.cxx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,5 +1339,44 @@ TClass* CcdbApi::tinfo2TClass(std::type_info const& tinfo)
13391339
return cl;
13401340
}
13411341

1342+
void CcdbApi::updateMetadata(std::string const& path, std::map<std::string, std::string> const& metadata, long timestamp, std::string const& id)
1343+
{
1344+
CURL* curl;
1345+
CURLcode res;
1346+
stringstream fullUrl;
1347+
fullUrl << mUrl << "/" << path << "/" << timestamp;
1348+
if (!id.empty()) {
1349+
fullUrl << "/" << id;
1350+
}
1351+
fullUrl << "?";
1352+
1353+
curl = curl_easy_init();
1354+
1355+
for (auto& kv : metadata) {
1356+
string mfirst = kv.first;
1357+
string msecond = kv.second;
1358+
// same trick for the metadata as for the object type
1359+
char* mfirstEncoded = curl_easy_escape(curl, mfirst.c_str(), mfirst.size());
1360+
char* msecondEncoded = curl_easy_escape(curl, msecond.c_str(), msecond.size());
1361+
fullUrl << string(mfirstEncoded) + "=" + string(msecondEncoded) + "&";
1362+
curl_free(mfirstEncoded);
1363+
curl_free(msecondEncoded);
1364+
}
1365+
1366+
if (curl != nullptr) {
1367+
curl_easy_setopt(curl, CURLOPT_URL, fullUrl.str().c_str());
1368+
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); // make sure we use PUT
1369+
1370+
curlSetSSLOptions(curl);
1371+
1372+
// Perform the request, res will get the return code
1373+
res = curl_easy_perform(curl);
1374+
if (res != CURLE_OK) {
1375+
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
1376+
}
1377+
curl_easy_cleanup(curl);
1378+
}
1379+
}
1380+
13421381
} // namespace ccdb
13431382
} // namespace o2

CCDB/test/testCcdbApi.cxx

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,14 @@
2424
#include "CCDB/CCDBTimeStampUtils.h"
2525
#include <boost/test/unit_test.hpp>
2626
#include <filesystem>
27-
#include <cstdio>
28-
#include <cassert>
2927
#include <iostream>
30-
#include <cstdio>
31-
#include <curl/curl.h>
32-
#include <sys/stat.h>
33-
#include <fcntl.h>
3428
#include <TH1F.h>
3529
#include <chrono>
3630
#include <CommonUtils/StringUtils.h>
37-
#include <TMessage.h>
3831
#include <TStreamerInfo.h>
3932
#include <TGraph.h>
4033
#include <TTree.h>
4134
#include <TString.h>
42-
#include <sys/types.h>
4335
#include <unistd.h>
4436

4537
#include <boost/property_tree/json_parser.hpp>
@@ -479,3 +471,66 @@ BOOST_AUTO_TEST_CASE(TestRetrieveHeaders, *utf::precondition(if_reachable()))
479471
}
480472
BOOST_CHECK_EQUAL(headers.size(), 0);
481473
}
474+
475+
BOOST_AUTO_TEST_CASE(TestUpdateMetadata, *utf::precondition(if_reachable()))
476+
{
477+
test_fixture f;
478+
479+
// upload an object
480+
TH1F h1("object1", "object1", 100, 0, 99);
481+
cout << "storing object 1 in " << basePath << "Test" << endl;
482+
map<string, string> metadata;
483+
metadata["custom"] = "whatever";
484+
metadata["id"] = "first";
485+
f.api.storeAsTFile(&h1, basePath + "Test", metadata);
486+
487+
// retrieve the headers just to be sure
488+
std::map<std::string, std::string> headers = f.api.retrieveHeaders(basePath + "Test", metadata);
489+
BOOST_CHECK(headers.count("custom") > 0);
490+
BOOST_CHECK(headers.at("custom") == "whatever");
491+
string firstID = headers.at("ETag");
492+
firstID.erase(std::remove(firstID.begin(), firstID.end(), '"'), firstID.end());
493+
494+
map<string, string> newMetadata;
495+
newMetadata["custom"] = "somethingelse";
496+
497+
// update the metadata and check
498+
f.api.updateMetadata(basePath + "Test", newMetadata, o2::ccdb::getCurrentTimestamp());
499+
headers = f.api.retrieveHeaders(basePath + "Test", newMetadata);
500+
BOOST_CHECK(headers.count("custom") > 0);
501+
BOOST_CHECK(headers.at("custom") == "somethingelse");
502+
503+
// add a second object
504+
cout << "storing object 2 in " << basePath << "Test" << endl;
505+
metadata.clear();
506+
metadata["custom"] = "whatever";
507+
metadata["id"] = "second";
508+
f.api.storeAsTFile(&h1, basePath + "Test", metadata);
509+
510+
// get id
511+
cout << "get id" << endl;
512+
headers = f.api.retrieveHeaders(basePath + "Test", metadata);
513+
string secondID = headers.at("ETag");
514+
secondID.erase(std::remove(secondID.begin(), secondID.end(), '"'), secondID.end());
515+
516+
// update the metadata by id
517+
cout << "update the metadata by id" << endl;
518+
newMetadata.clear();
519+
newMetadata["custom"] = "first";
520+
f.api.updateMetadata(basePath + "Test", newMetadata, o2::ccdb::getCurrentTimestamp(), firstID);
521+
newMetadata.clear();
522+
newMetadata["custom"] = "second";
523+
f.api.updateMetadata(basePath + "Test", newMetadata, o2::ccdb::getCurrentTimestamp(), secondID);
524+
525+
// check
526+
metadata.clear();
527+
metadata["id"] = "first";
528+
headers = f.api.retrieveHeaders(basePath + "Test", metadata);
529+
BOOST_CHECK(headers.count("custom") > 0);
530+
BOOST_CHECK(headers.at("custom") == "first");
531+
metadata.clear();
532+
metadata["id"] = "second";
533+
headers = f.api.retrieveHeaders(basePath + "Test", metadata);
534+
BOOST_CHECK(headers.count("custom") > 0);
535+
BOOST_CHECK(headers.at("custom") == "second");
536+
}

0 commit comments

Comments
 (0)