From b121219ced3d643dad4030e4185ab10d648821d7 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Tue, 1 Apr 2025 17:39:34 +0200
Subject: [PATCH 01/33] Add RunLumi dto for CTP

---
 CrestApi/RunLumiDto.h | 60 +++++++++++++++++++++++++++++++++++++++++++
 src/RunLumiDto.cxx    | 27 +++++++++++++++++++
 test/CMakeLists.txt   |  2 +-
 test/test-json.cxx    |  5 +++-
 4 files changed, 92 insertions(+), 2 deletions(-)
 create mode 100644 CrestApi/RunLumiDto.h
 create mode 100644 src/RunLumiDto.cxx

diff --git a/CrestApi/RunLumiDto.h b/CrestApi/RunLumiDto.h
new file mode 100644
index 0000000..bbd61e8
--- /dev/null
+++ b/CrestApi/RunLumiDto.h
@@ -0,0 +1,60 @@
+/*
+   Copyright (C) 2019-2024 CERN for the benefit of the ATLAS collaboration
+ */
+
+#ifndef CREST_RUN_LUMI_DTO_HPP
+#define CREST_RUN_LUMI_DTO_HPP
+
+#include <CrestApi/CrestException.h>
+#include <_types/_uint64_t.h>
+
+#include <nlohmann/json.hpp>
+#include <optional>
+#include <string>
+
+using json = nlohmann::json;
+
+namespace Crest {
+
+/**
+ * @brief The RunLumiDto class
+ * It contains CTP information of run,lb,start and end time of each lumi block.
+ *
+ * @param runNumber The run number.
+ * @param lb The lumi block number.
+ * @param starttime The start time of the lumi block, in milliseconds since
+ * epoch.
+ * @param endtime The end time of the lumi block, in milliseconds since epoch.
+ */
+
+class RunLumiDto {
+ private:
+  uint64_t starttime;
+  uint64_t endtime;
+  uint64_t runNumber;
+  uint64_t lb;
+
+ public:
+  // Ctor
+  RunLumiDto(uint64_t runNumber, uint64_t lb, uint64_t starttime,
+             uint64_t endtime)
+      : runNumber(runNumber), lb(lb), starttime(starttime), endtime(endtime) {};
+  // Default Ctor
+  RunLumiDto();
+  const uint64_t &getRunNumber() const { return runNumber; }
+  const uint64_t &getLumiBlock() const { return lb; }
+  const uint64_t &getStartTime() const { return starttime; }
+  const uint64_t &getEndTime() const { return endtime; }
+  // Define setters
+  void setRunNumber(uint64_t runNumber) { this->runNumber = runNumber; }
+  void setLumiBlock(uint64_t lb) { this->lb = lb; }
+  void setStartTime(uint64_t starttime) { this->starttime = starttime; }
+  void setEndTime(uint64_t endtime) { this->endtime = endtime; }
+
+  json toJson() const;
+  static RunLumiDto fromJson(const json &j);
+};
+
+}  // namespace Crest
+
+#endif  // CREST_RUN_LUMI_DTO_HPP
diff --git a/src/RunLumiDto.cxx b/src/RunLumiDto.cxx
new file mode 100644
index 0000000..e89e0b0
--- /dev/null
+++ b/src/RunLumiDto.cxx
@@ -0,0 +1,27 @@
+/*
+  Copyright (C) 2020-2024 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <CrestApi/RunLumiDto.h>
+
+namespace Crest {
+
+json RunLumiDto::toJson() const {
+  json rl = {};
+  rl["runNumber"] = runNumber;
+  rl["lb"] = lb;
+  rl["starttime"] = starttime;
+  rl["endtime"] = endtime;
+  return rl;
+}
+
+RunLumiDto RunLumiDto::fromJson(const json &j) {
+  RunLumiDto rl;
+  rl.runNumber = j.value<uint64_t>("runNumber", 0);
+  rl.lb = j.value<uint64_t>("lb", 0);
+  rl.starttime = j.value<uint64_t>("starttime", 0);
+  rl.endtime = j.value<uint64_t>("endtime", 0);
+  return rl;
+}
+
+}  // namespace Crest
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index e12cbee..b3da9ac 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -19,4 +19,4 @@ endfunction()
 crestapi_add_test(CrestApi_test)
 crestapi_add_test(CrestApiFs_test)
 crestapi_add_test(json_parse)
-#crestapi_add_test(test-json)
+crestapi_add_test(test-json)
diff --git a/test/test-json.cxx b/test/test-json.cxx
index bfd3495..a93618a 100644
--- a/test/test-json.cxx
+++ b/test/test-json.cxx
@@ -79,10 +79,11 @@ int main() {
   std::cout << std::endl;
 
   // Fill a json object with data
+  bool test_boolean = true;
   json j4;
   j4["a_char"] = new char('c');
   j4["a_string"] = "string";
-  j4["a_bool"] = true;
+  j4["a_bool"] = test_boolean;
   j4["a_int"] = 42;
   j4["a_float"] = 3.141;
   j4["a_null"] = nullptr;
@@ -90,6 +91,8 @@ int main() {
   j4["an_unsigned_long"] = 42ul;
   j4["an_unsigned_long_long"] = 42ull;
 
+  // Print the json object
+  std::cout << "Print JSON object j4:\n";
   std::cout << j4 << '\n';
 
   // Read back data using iterators
-- 
GitLab


From 816e3248a6f902845688f5759277b933e16bc01b Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Tue, 1 Apr 2025 17:45:07 +0200
Subject: [PATCH 02/33] Add run lumi set

---
 CMakeLists.txt           |  4 ++++
 CrestApi/RunLumiSetDto.h | 45 ++++++++++++++++++++++++++++++++++++++++
 src/RunLumiSetDto.cxx    | 30 +++++++++++++++++++++++++++
 3 files changed, 79 insertions(+)
 create mode 100644 CrestApi/RunLumiSetDto.h
 create mode 100644 src/RunLumiSetDto.cxx

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c555d5b..78148b0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -71,6 +71,8 @@ set(HEADERS
     "CrestApi/TagMetaDto.h"
     "CrestApi/TagMetaSetDto.h"
     "CrestApi/TagSetDto.h"
+    "CrestApi/RunLumiDto.h"
+    "CrestApi/RunLumiSetDto.h"
     "${CRESTAPI_VERSION_FILE_DIR}/version.h")
 
 set(SOURCES
@@ -103,6 +105,8 @@ set(SOURCES
     "src/StoreSetDto.cxx"
     "src/TagInfoDto.cxx"
     "src/TagSetDto.cxx"
+    "src/RunLumiDto.cxx"
+    "src/RunLumiSetDto.cxx"
 )
 
 # Set up the build of the main library of the project.
diff --git a/CrestApi/RunLumiSetDto.h b/CrestApi/RunLumiSetDto.h
new file mode 100644
index 0000000..186217a
--- /dev/null
+++ b/CrestApi/RunLumiSetDto.h
@@ -0,0 +1,45 @@
+/*
+   Copyright (C) 2019-2024 CERN for the benefit of the ATLAS collaboration
+ */
+
+#ifndef CREST_RUN_LUMI_SET_DTO_HPP
+#define CREST_RUN_LUMI_SET_DTO_HPP
+
+#include <CrestApi/CrestBaseResponse.h>
+#include <CrestApi/CrestDetail.h>
+#include <CrestApi/CrestException.h>
+#include <CrestApi/RunLumiDto.h>
+
+#include <string>
+#include <vector>
+
+#include "nlohmann/json.hpp"
+
+using json = nlohmann::json;
+
+namespace Crest {
+
+class RunLumiSetDto : public CrestBaseResponse {
+ private:
+  inline static const std::string s_dtoType = "RunLumiSetDto";
+  std::vector<RunLumiDto> resources;
+
+ public:
+  RunLumiSetDto(const std::vector<RunLumiDto> &resources)
+      : resources(resources) {}
+  RunLumiSetDto() : resources() {}
+  const std::string &getFormat() const { return s_dtoType; }
+
+  size_t size() const { return resources.size(); }
+
+  void add(const RunLumiDto &rl) { resources.push_back(rl); }
+
+  const std::vector<RunLumiDto> &getResources() const { return resources; }
+
+  json toJson() const;
+  static RunLumiSetDto fromJson(const json &j);
+};
+
+}  // namespace Crest
+
+#endif  // CREST_RUN_LUMI_SET_DTO_HPP
diff --git a/src/RunLumiSetDto.cxx b/src/RunLumiSetDto.cxx
new file mode 100644
index 0000000..a6499b1
--- /dev/null
+++ b/src/RunLumiSetDto.cxx
@@ -0,0 +1,30 @@
+/*
+  Copyright (C) 2020-2024 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <CrestApi/RunLumiSetDto.h>
+
+namespace Crest {
+
+json RunLumiSetDto::toJson() const {
+  json baseJson = CrestBaseResponse::toJson();
+  json jsonResources = json::array();
+  for (const auto &resource : resources) {
+    jsonResources.push_back(((RunLumiDto)resource).toJson());
+  }
+  baseJson["resources"] = jsonResources;
+  return baseJson;
+}
+
+RunLumiSetDto RunLumiSetDto::fromJson(const json &j) {
+  RunLumiSetDto rlSet;
+  rlSet.loadFromJson(j);
+  json jsonResources = j.value("resources", json::array());
+  for (auto it = jsonResources.begin(); it != jsonResources.end(); ++it) {
+    rlSet.resources.push_back(RunLumiDto::fromJson(*it));
+  }
+
+  return rlSet;
+}
+
+}  // namespace Crest
-- 
GitLab


From 19a259127b0c016254fbca35e9324356eacdaf32 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Tue, 1 Apr 2025 18:03:05 +0200
Subject: [PATCH 03/33] Add function to retrieve run lumi

---
 CrestApi/CrestApi.h   | 17 +++++++++++++++++
 CrestApi/RunLumiDto.h |  4 ++--
 src/CrestApi.cxx      | 41 +++++++++++++++++++++++++++++++++++++++++
 src/RunLumiDto.cxx    |  2 ++
 4 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/CrestApi/CrestApi.h b/CrestApi/CrestApi.h
index 6caa33f..5ed9713 100644
--- a/CrestApi/CrestApi.h
+++ b/CrestApi/CrestApi.h
@@ -34,6 +34,7 @@
 #include <CrestApi/PayloadDto.h>
 #include <CrestApi/PayloadSetDto.h>
 #include <CrestApi/PayloadTagInfoSetDto.h>
+#include <CrestApi/RunLumiSetDto.h>
 #include <CrestApi/StoreSetDto.h>
 #include <CrestApi/TagMetaSetDto.h>
 #include <CrestApi/TagSetDto.h>
@@ -548,6 +549,22 @@ class CrestApi : public CrestApiBase {
    */
   PayloadTagInfoSetDto listPayloadTagInfo(const std::string &tagname, int size,
                                           int page, const std::string &sort);
+
+  /**
+   * This method retrieves run lumi information.
+   * @param since - since time (the beginning of the time interval),
+   * @param until - until time (end of the time interval),
+   * @param format - time format for since/until ([iso, number, run-lumi]),
+   * @param mode - the mode parameter is used to determine the meaning of
+   * since/until [daterange, runrange].
+   * @param size - page size,
+   * @param page - page number,
+   * @param sort - sorting order (runNumber:ASC or runNumber:DESC),
+   * @return JSON run lumi info list as a RunLumiSetDto.<br>
+   */
+  RunLumiSetDto listRunInfo(const std::string &since, const std::string &until,
+                            const std::string format, const std::string mode,
+                            int size, int page, const std::string &sort);
 };
 
 }  // namespace Crest
diff --git a/CrestApi/RunLumiDto.h b/CrestApi/RunLumiDto.h
index bbd61e8..10c49dc 100644
--- a/CrestApi/RunLumiDto.h
+++ b/CrestApi/RunLumiDto.h
@@ -29,10 +29,10 @@ namespace Crest {
 
 class RunLumiDto {
  private:
-  uint64_t starttime;
-  uint64_t endtime;
   uint64_t runNumber;
   uint64_t lb;
+  uint64_t starttime;
+  uint64_t endtime;
 
  public:
   // Ctor
diff --git a/src/CrestApi.cxx b/src/CrestApi.cxx
index 2aad225..a8df227 100644
--- a/src/CrestApi.cxx
+++ b/src/CrestApi.cxx
@@ -761,4 +761,45 @@ void CrestApi::checkHash(const std::string &hash, const std::string &str,
   return;
 }
 
+RunLumiSetDto CrestApi::listRunInfo(const std::string &since,
+                                    const std::string &until,
+                                    const std::string format,
+                                    const std::string mode, int size, int page,
+                                    const std::string &sort) {
+  std::string current_path = m_PATH;
+  current_path += urlPaths[UrlPathIndex::RUNINFO];
+  current_path += "?since=";
+  current_path += since;
+  current_path += "&until=";
+  current_path += until;
+  current_path += "&format=";
+  current_path += format;
+  current_path += "&mode=";
+  current_path += mode;
+
+  if (size != -1) {
+    current_path += "&size=";
+    current_path += std::to_string(size);
+  }
+  if (page != -1) {
+    current_path += "&page=";
+    current_path += std::to_string(page);
+  }
+  if (sort != "") {
+    current_path += "&sort=";
+    current_path += sort;
+  }
+
+  std::string retv;
+
+  nlohmann::json js = nullptr;
+  retv = m_request.performRequest(current_path, Action::GET, js);
+  nlohmann::json response = StringUtils::getJson(retv);
+
+  RunLumiSetDto dto = RunLumiSetDto::fromJson(response);
+
+  return dto;
+}
+//==============================================================================================================
+
 }  // namespace Crest
diff --git a/src/RunLumiDto.cxx b/src/RunLumiDto.cxx
index e89e0b0..2b1e5d6 100644
--- a/src/RunLumiDto.cxx
+++ b/src/RunLumiDto.cxx
@@ -6,6 +6,8 @@
 
 namespace Crest {
 
+RunLumiDto::RunLumiDto() : runNumber(0), lb(0), starttime(0), endtime(0) {}
+
 json RunLumiDto::toJson() const {
   json rl = {};
   rl["runNumber"] = runNumber;
-- 
GitLab


From 03c46c76dad87af2b032068cd94c0b464771dbb2 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Wed, 2 Apr 2025 21:38:48 +0200
Subject: [PATCH 04/33] add crest resolver example for ACTS

---
 test/CMakeLists.txt        |   1 +
 test/CrestFileResolver.cxx | 151 +++++++++++++++++++++++++++++++++++++
 2 files changed, 152 insertions(+)
 create mode 100644 test/CrestFileResolver.cxx

diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index b3da9ac..a3aef12 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -20,3 +20,4 @@ crestapi_add_test(CrestApi_test)
 crestapi_add_test(CrestApiFs_test)
 crestapi_add_test(json_parse)
 crestapi_add_test(test-json)
+crestapi_add_test(CrestFileResolver)
diff --git a/test/CrestFileResolver.cxx b/test/CrestFileResolver.cxx
new file mode 100644
index 0000000..f0da3d5
--- /dev/null
+++ b/test/CrestFileResolver.cxx
@@ -0,0 +1,151 @@
+
+/**
+ * @file CrestApi/test/CrestApi_test.cxx
+ * @brief Some tests for server methods.
+ */
+
+#include "CrestApi/GlobalTagMapDto.h"
+#include "CrestApi/GlobalTagMapSetDto.h"
+#include "CrestApi/IovSetDto.h"
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MAIN
+#define BOOST_TEST_MODULE TEST_CRESTAPI
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "../CrestApi/CrestApi.h"
+#include "../CrestApi/StringUtils.h"
+
+using namespace Crest;
+
+const std::string crest_server = "http://atlaf-alma9-02.cern.ch:8080/api-v6.0";
+const std::string global_tag = "TEST-GEOMETRY-01";
+
+class CrestFileResolver {
+ private:
+  std::string m_crest_server;
+  CrestApi m_crestApi;
+  std::string geometry_global_tag;
+  std::string mat_tag = "none";
+  std::string mat_label = "/Material/TrackingGeo";
+  std::string m_file_path;
+
+ public:
+  CrestFileResolver(const std::string &crest_server)
+      : m_crest_server(crest_server),
+        m_crestApi(crest_server),
+        geometry_global_tag("TEST-GEOMETRY-01"),
+        m_file_path("/tmp/crest_test/") {
+    // Check CrestApi version
+    if (m_crestApi.getCrestVersion() != "v6.0") {
+      std::cerr << "CrestApi version mismatch!" << std::endl;
+    }
+  }
+
+  CrestFileResolver(const std::string &crest_server,
+                    const std::string &file_path,
+                    const std::string &geometry_gtag, const std::string &label)
+      : m_crest_server(crest_server),
+        m_crestApi(crest_server),
+        geometry_global_tag(geometry_gtag),
+        mat_tag("none"),
+        mat_label(label),
+        m_file_path(file_path) {
+    // Check CrestApi version
+    if (m_crestApi.getCrestVersion() != "v6.0") {
+      std::cerr << "CrestApi version mismatch!" << std::endl;
+    }
+  }
+
+  std::string resolveFilePath(const std::string &geometry_gtag,
+                              const std::string &file_name) {
+    // Here you can implement the logic to resolve the file path.
+    // Use crestapi to resolve mappings and detect the tag.
+    std::cout << "Load mappings for " << geometry_global_tag << std::endl;
+    GlobalTagMapSetDto mapset =
+        m_crestApi.findGlobalTagMap(geometry_global_tag, "Trace");
+    std::cout << "test: listTagsInGlobalTag (Trace) =" << std::endl;
+    std::cout << mapset.toJson().dump(4) << std::endl;
+
+    if (mapset.getResources().size() < 1) {
+      std::cerr << "No mapping found for the global tag: "
+                << geometry_global_tag << std::endl;
+      return "";
+    }
+    // Get the mapping (geometry tag name)
+    GlobalTagMapDto map = mapset.getResources()[0];
+    std::cout << "Found map " << map.toJson().dump(4) << std::endl;
+    std::cout << "Compare label " << this->mat_label << " with "
+              << map.getLabel() << std::endl;
+    // Check that the label is correct
+    if (this->mat_label != map.getLabel()) {
+      std::cerr << "Label mismatch: expected " << mat_label << ", got "
+                << map.getLabel() << std::endl;
+      return "";
+    }
+    this->mat_tag = map.getTagName();
+    std::cout << "Load iovs for tag " << this->mat_tag << std::endl;
+    // Now get the IOV.
+    IovSetDto iovset =
+        m_crestApi.selectIovs(this->mat_tag, 0, 10, 0, 10, 0, "id.since:ASC");
+    if (iovset.getResources().size() < 1) {
+      std::cerr << "No IOV found for the tag: " << this->mat_tag << std::endl;
+      return "";
+    }
+    // Get the IOV
+    IovDto iov = iovset.getResources()[0];
+    std::cout << "Found IOV " << iov.toJson().dump(4) << std::endl;
+    // Get the payload using the iov hash
+    std::string hash = iov.getPayloadHash();
+    std::string payload = m_crestApi.getPayload(hash);
+    // Dump the payload to a file
+    std::string file_path = m_file_path + file_name;
+    // Write the payload to the file
+    std::cout << "Dump payload to file " << file_path << std::endl;
+    dumpToFile(file_path, payload);
+    return file_path;
+  }
+
+  void dumpToFile(const std::string &filePath, const std::string &content) {
+    // Open the file in write mode
+    std::ofstream outFile(filePath);
+
+    // Check if the file was successfully opened
+    if (!outFile) {
+      std::cerr << "Error: Could not open file " << filePath << " for writing."
+                << std::endl;
+      return;
+    }
+
+    // Write the content to the file
+    outFile << content;
+
+    // Close the file
+    outFile.close();
+
+    std::cout << "Content successfully written to " << filePath << std::endl;
+  }
+};
+
+int main() {
+  std::string geoglobal_tag = "TEST-GEOMETRY-01";
+  std::cout << "Global tag for geometry: " << geoglobal_tag << std::endl;
+
+  try {
+    // Create an instance of CrestFileResolver
+    CrestFileResolver crestFileResolver(crest_server, "/tmp/crest_test/",
+                                        geoglobal_tag, "/Material/TrackingGeo");
+    // Resolve the file path
+    std::string file_path =
+        crestFileResolver.resolveFilePath(geoglobal_tag, "test_file.json");
+    // Print the parsed inner JSON
+    std::cout << "Dumped JSON from Geometry Global Tag for tracking material: "
+              << file_path << std::endl;
+  } catch (const std::exception &e) {
+    std::cerr << "Error parsing JSON: " << e.what() << std::endl;
+  }
+
+  return 0;
+}
-- 
GitLab


From 3b2212d56ebbc9bfc6bb5a7f0461cb205e60fc91 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Wed, 2 Apr 2025 22:07:58 +0200
Subject: [PATCH 05/33] cherry pick from Mikhail

---
 CrestApi/CrestApiFs.h        | 12 ++++++++++++
 CrestApi/CrestBaseResponse.h |  1 +
 CrestApi/RespPage.h          | 16 ++++++++++++++++
 src/CrestApiFs.cxx           | 31 ++++++++++++++++++++++++++++++-
 4 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/CrestApi/CrestApiFs.h b/CrestApi/CrestApiFs.h
index 0408d2a..715bf53 100644
--- a/CrestApi/CrestApiFs.h
+++ b/CrestApi/CrestApiFs.h
@@ -591,6 +591,18 @@ class CrestApiFs : public CrestApiBase {
                         const std::string &compressionType,
                         const std::string &version,
                         const std::string &streamerInfo);
+
+  /**
+   * This method retrieves monitoring information on payload as a list of
+   * payload tag information.
+   * @param tagname - tag name pattern, "%" can be used for any symbols,
+   * @param size - page size,
+   * @param page - page number,
+   * @param sort - sorting order (name:ASC or name:DESC),
+   * @return JSON payload tag info list as a PayloadTagInfoSetDto.<br>
+   */
+  PayloadTagInfoSetDto listPayloadTagInfo(const std::string &tagname, int size,
+                                          int page, const std::string &sort);
 };
 
 }  // namespace Crest
diff --git a/CrestApi/CrestBaseResponse.h b/CrestApi/CrestBaseResponse.h
index 8cf2e76..281e872 100644
--- a/CrestApi/CrestBaseResponse.h
+++ b/CrestApi/CrestBaseResponse.h
@@ -31,6 +31,7 @@ class CrestBaseResponse {
   std::optional<RespPage> getRespPage() const { return page; }
   std::optional<GenericMap> getFilter() const { return filter; }
 
+  void setRespPage(const RespPage &page) { this->page = page; }
   void setDataType(const std::string &dtype) { datatype = dtype; }
 
   json toJson() const;
diff --git a/CrestApi/RespPage.h b/CrestApi/RespPage.h
index c27c131..c3daa49 100644
--- a/CrestApi/RespPage.h
+++ b/CrestApi/RespPage.h
@@ -23,11 +23,27 @@ class RespPage {
   int number;
 
  public:
+  RespPage() : size(0), totalElements(0), totalPages(0), number(0) {}
+
+  RespPage(int size, int64_t totalElements, int totalPages, int number)
+      : size(size),
+        totalElements(totalElements),
+        totalPages(totalPages),
+        number(number) {}
+
   int getSize() const { return size; }
   int64_t getTotalElements() const { return totalElements; }
   int getTotalPages() const { return totalPages; }
   int getNumber() const { return number; }
 
+  // Define setters
+  void setSize(int size) { this->size = size; }
+  void setTotalElements(int64_t totalElements) {
+    this->totalElements = totalElements;
+  }
+  void setTotalPages(int totalPages) { this->totalPages = totalPages; }
+  void setNumber(int number) { this->number = number; }
+
   json toJson() const;
   static RespPage fromJson(const json &j);
 };
diff --git a/src/CrestApiFs.cxx b/src/CrestApiFs.cxx
index 476505b..a414e22 100644
--- a/src/CrestApiFs.cxx
+++ b/src/CrestApiFs.cxx
@@ -160,6 +160,7 @@ GlobalTagSetDto CrestApiFs::listGlobalTags(const std::string &name, int size,
         "ERROR in CrestApiFs::listTags: wrong sort parameter." + sort);
   }
 
+  int totalItems = 0;  // total global tag number
   try {
     std::vector<std::string> taglist = nameList(folder, ascending);
     std::vector<std::string> clearedTaglist;
@@ -178,6 +179,8 @@ GlobalTagSetDto CrestApiFs::listGlobalTags(const std::string &name, int size,
       }
     }
 
+    totalItems = clearedTaglist.size();  // total global tag number
+
     taglist = getVectorPage(clearedTaglist, size, page);
     for (const std::string &tag : taglist) {
       std::string file_name = folder + "/" + tag + "/" + s_FS_GLOBALTAG_FILE;
@@ -190,6 +193,11 @@ GlobalTagSetDto CrestApiFs::listGlobalTags(const std::string &name, int size,
         "ERROR in CrestApiFs::listGlobalTags: cannot get the tag list.");
   }
 
+  // add RespPage:
+  int tPages = std::ceil((double)totalItems / size);
+  RespPage respp(tagSet.size(), totalItems, tPages, page);
+  tagSet.setRespPage(respp);
+
   return tagSet;
 }
 
@@ -268,6 +276,7 @@ TagSetDto CrestApiFs::listTags(const std::string &name, int size, int page,
         "ERROR in CrestApiFs::listTags: wrong sort parameter." + sort);
   }
 
+  int totalItems = 0;  // total tag number
   try {
     std::vector<std::string> taglist = nameList(folder, ascending);
     std::vector<std::string> clearedTaglist;
@@ -286,6 +295,8 @@ TagSetDto CrestApiFs::listTags(const std::string &name, int size, int page,
       }
     }
 
+    totalItems = clearedTaglist.size();  // total tag number
+
     taglist = getVectorPage(clearedTaglist, size, page);
     for (const std::string &tag : taglist) {
       std::string file_name = folder + "/" + tag + "/" + s_FS_TAG_FILE;
@@ -298,6 +309,11 @@ TagSetDto CrestApiFs::listTags(const std::string &name, int size, int page,
         "ERROR in CrestApiFs::listTags: cannot get the tag list.");
   }
 
+  // add RespPage:
+  int tPages = std::ceil((double)totalItems / size);
+  RespPage respp(tagSet.size(), totalItems, tPages, page);
+  tagSet.setRespPage(respp);
+
   return tagSet;
 }
 
@@ -477,6 +493,13 @@ GlobalTagMapSetDto CrestApiFs::findGlobalTagMap(
 
   GlobalTagMapSetDto dto;
   dto = GlobalTagMapSetDto::fromFsJson(js);
+
+  // add RespPage:
+  int totalItems = dto.size();
+  RespPage respp(totalItems, totalItems, 1,
+                 0);  // full global tag map in one response
+  dto.setRespPage(respp);
+
   return dto;
 }
 
@@ -489,10 +512,11 @@ IovSetDto CrestApiFs::selectIovs(
   IovSetDto dto;
 
   nlohmann::json js = nlohmann::json::array();
+  int niovs = 0;
 
   try {
     nlohmann::json iovList = findAllIovs(name);
-    int niovs = iovList.size();
+    niovs = iovList.size();
 
     for (int i = 0; i < niovs; i++) {
       if (iovList[i].find("since") != iovList[i].end()) {
@@ -529,6 +553,11 @@ IovSetDto CrestApiFs::selectIovs(
   nlohmann::json ext = getPage(sorted, size, page);
   dto = IovSetDto::fromFsJson(ext);
 
+  // add RespPage:
+  int tPages = std::ceil((double)niovs / size);
+  RespPage respp(dto.size(), niovs, tPages, page);
+  dto.setRespPage(respp);
+
   return dto;
 }
 
-- 
GitLab


From 8f85ea30f0731638ed73ac556814d7a629009dd5 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Wed, 2 Apr 2025 22:19:24 +0200
Subject: [PATCH 06/33] cherry pick (second part)

---
 CrestApi/CrestApiBase.h | 13 +++++++++++++
 src/CrestApi.cxx        | 19 +++++++++++++++++++
 src/CrestApiFs.cxx      |  7 +++++++
 3 files changed, 39 insertions(+)

diff --git a/CrestApi/CrestApiBase.h b/CrestApi/CrestApiBase.h
index 0734311..488980d 100644
--- a/CrestApi/CrestApiBase.h
+++ b/CrestApi/CrestApiBase.h
@@ -479,6 +479,19 @@ class CrestApiBase {
    * @return CrestApi library version.
    */
   std::string getClientVersion();
+
+  /**
+   * This method retrieves monitoring information on payload as a list of
+   * payload tag information.
+   * @param tagname - tag name pattern, "%" can be used for any symbols,
+   * @param size - page size,
+   * @param page - page number,
+   * @param sort - sorting order (name:ASC or name:DESC),
+   * @return JSON payload tag info list as a PayloadTagInfoSetDto.<br>
+   */
+  virtual PayloadTagInfoSetDto listPayloadTagInfo(const std::string &tagname,
+                                                  int size, int page,
+                                                  const std::string &sort) = 0;
 };
 
 }  // namespace Crest
diff --git a/src/CrestApi.cxx b/src/CrestApi.cxx
index a8df227..c39d93c 100644
--- a/src/CrestApi.cxx
+++ b/src/CrestApi.cxx
@@ -482,6 +482,25 @@ void CrestApi::removeGlobalTagMap(const std::string &name,
   nlohmann::json js = nullptr;
   retv = m_request.performRequest(current_path, Action::DELETE, js);
 }
+std::string retv;
+nlohmann::json js = nullptr;
+retv = m_request.performRequest(current_path, Action::DELETE, js);
+
+nlohmann::json response = StringUtils::getJson(retv);
+GlobalTagMapSetDto dto = GlobalTagMapSetDto::fromJson(response);
+if (dto.size() != 0) {
+  GlobalTagMapDto gtmap = dto.getResources()[0];
+  if (name != gtmap.getGlobalTagName() || tagname != gtmap.getTagName() ||
+      label != gtmap.getLabel()) {
+    throw CrestException(
+        "ERROR in CrestApi::removeGlobalTagMap Cannot delete global tag map.");
+  }
+} else {
+  throw CrestException(
+      "ERROR in CrestApi::removeGlobalTagMap Cannot delete global tag map.");
+}
+
+}
 
 //==============================================================================================================
 // Iovs methods:
diff --git a/src/CrestApiFs.cxx b/src/CrestApiFs.cxx
index a414e22..3ff2d3c 100644
--- a/src/CrestApiFs.cxx
+++ b/src/CrestApiFs.cxx
@@ -1018,4 +1018,11 @@ std::string CrestApiFs::getCrestVersion() {
       "CREST server version for file storage.");
 }
 
+PayloadTagInfoSetDto CrestApiFs::listPayloadTagInfo(const std::string &tagname,
+                                                    int size, int page,
+                                                    const std::string &sort) {
+  checkFsException("CrestApiFs::listPayloadTagInfo");
+  return PayloadTagInfoSetDto();
+}
+
 }  // namespace Crest
-- 
GitLab


From 6f83209349a40686108cfca005645d48a8abab15 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Wed, 2 Apr 2025 22:23:25 +0200
Subject: [PATCH 07/33] cherry pick (second part)

---
 CrestApi/CrestApi.h   |  3 ++-
 CrestApi/CrestApiFs.h |  3 ++-
 src/CrestApi.cxx      | 26 +++++++++++---------------
 3 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/CrestApi/CrestApi.h b/CrestApi/CrestApi.h
index 5ed9713..6446578 100644
--- a/CrestApi/CrestApi.h
+++ b/CrestApi/CrestApi.h
@@ -548,7 +548,8 @@ class CrestApi : public CrestApiBase {
    * @return JSON payload tag info list as a PayloadTagInfoSetDto.<br>
    */
   PayloadTagInfoSetDto listPayloadTagInfo(const std::string &tagname, int size,
-                                          int page, const std::string &sort);
+                                          int page,
+                                          const std::string &sort) override;
 
   /**
    * This method retrieves run lumi information.
diff --git a/CrestApi/CrestApiFs.h b/CrestApi/CrestApiFs.h
index 715bf53..8890596 100644
--- a/CrestApi/CrestApiFs.h
+++ b/CrestApi/CrestApiFs.h
@@ -602,7 +602,8 @@ class CrestApiFs : public CrestApiBase {
    * @return JSON payload tag info list as a PayloadTagInfoSetDto.<br>
    */
   PayloadTagInfoSetDto listPayloadTagInfo(const std::string &tagname, int size,
-                                          int page, const std::string &sort);
+                                          int page,
+                                          const std::string &sort) override;
 };
 
 }  // namespace Crest
diff --git a/src/CrestApi.cxx b/src/CrestApi.cxx
index c39d93c..89460a0 100644
--- a/src/CrestApi.cxx
+++ b/src/CrestApi.cxx
@@ -481,25 +481,21 @@ void CrestApi::removeGlobalTagMap(const std::string &name,
   std::string retv;
   nlohmann::json js = nullptr;
   retv = m_request.performRequest(current_path, Action::DELETE, js);
-}
-std::string retv;
-nlohmann::json js = nullptr;
-retv = m_request.performRequest(current_path, Action::DELETE, js);
 
-nlohmann::json response = StringUtils::getJson(retv);
-GlobalTagMapSetDto dto = GlobalTagMapSetDto::fromJson(response);
-if (dto.size() != 0) {
-  GlobalTagMapDto gtmap = dto.getResources()[0];
-  if (name != gtmap.getGlobalTagName() || tagname != gtmap.getTagName() ||
-      label != gtmap.getLabel()) {
+  nlohmann::json response = StringUtils::getJson(retv);
+  GlobalTagMapSetDto dto = GlobalTagMapSetDto::fromJson(response);
+  if (dto.size() != 0) {
+    GlobalTagMapDto gtmap = dto.getResources()[0];
+    if (name != gtmap.getGlobalTagName() || tagname != gtmap.getTagName() ||
+        label != gtmap.getLabel()) {
+      throw CrestException(
+          "ERROR in CrestApi::removeGlobalTagMap Cannot delete global tag "
+          "map.");
+    }
+  } else {
     throw CrestException(
         "ERROR in CrestApi::removeGlobalTagMap Cannot delete global tag map.");
   }
-} else {
-  throw CrestException(
-      "ERROR in CrestApi::removeGlobalTagMap Cannot delete global tag map.");
-}
-
 }
 
 //==============================================================================================================
-- 
GitLab


From c00538a86ca467b93807e15f6d6083545662fd30 Mon Sep 17 00:00:00 2001
From: Mikhail Mineev <Mikhail.Mineev@cern.ch>
Date: Thu, 3 Apr 2025 15:04:42 +0200
Subject: [PATCH 08/33] CREST server url corrected

---
 CrestApi/RunLumiDto.h  | 1 -
 src/StringUtils.cxx    | 2 +-
 test/CrestApi_test.cxx | 2 +-
 3 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/CrestApi/RunLumiDto.h b/CrestApi/RunLumiDto.h
index 10c49dc..5e78f72 100644
--- a/CrestApi/RunLumiDto.h
+++ b/CrestApi/RunLumiDto.h
@@ -6,7 +6,6 @@
 #define CREST_RUN_LUMI_DTO_HPP
 
 #include <CrestApi/CrestException.h>
-#include <_types/_uint64_t.h>
 
 #include <nlohmann/json.hpp>
 #include <optional>
diff --git a/src/StringUtils.cxx b/src/StringUtils.cxx
index bcf4196..f6f3e1d 100644
--- a/src/StringUtils.cxx
+++ b/src/StringUtils.cxx
@@ -63,7 +63,7 @@ ParsedUrl Crest::StringUtils::parseUrl(std::string_view url) {
 
   // The remaining part is the apipath
   if (!url.empty() && url[0] == '/') {
-    result.apipath = "";
+    result.apipath = url;
   }
 
   return result;
diff --git a/test/CrestApi_test.cxx b/test/CrestApi_test.cxx
index 9e67512..fcb0812 100644
--- a/test/CrestApi_test.cxx
+++ b/test/CrestApi_test.cxx
@@ -20,7 +20,7 @@ using namespace Crest;
 
 BOOST_AUTO_TEST_SUITE(CrestApiTest)
 
-const std::string crest_server = "https://atlaf-alma9-01.cern.ch/api-v6.0";
+const std::string crest_server = "http://atlaf-alma9-02.cern.ch:8080/api-v6.0";
 const std::string tagname = "test_ctest_tag_01";
 const std::string global_tag = "TEST_GLOBAL_TAG_01";
 
-- 
GitLab


From 67fe7b2d76ca657b8e8703d78308203d53d35b5a Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Thu, 3 Apr 2025 22:05:31 +0200
Subject: [PATCH 09/33] add acts file resolver example

---
 CMakeLists.txt                  |   1 +
 acts/CMakeLists.txt             |  54 ++++++++++++
 acts/CrestFileResolver.cxx      |  80 +++++++++++++++++
 acts/CrestFileResolver.h        |  68 ++++++++++++++
 test/CMakeLists.txt             |   4 +-
 test/CrestFileResolver.cxx      | 151 --------------------------------
 test/CrestFileResolver_test.cxx |  46 ++++++++++
 7 files changed, 252 insertions(+), 152 deletions(-)
 create mode 100644 acts/CMakeLists.txt
 create mode 100644 acts/CrestFileResolver.cxx
 create mode 100644 acts/CrestFileResolver.h
 delete mode 100644 test/CrestFileResolver.cxx
 create mode 100644 test/CrestFileResolver_test.cxx

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 78148b0..111d154 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -154,6 +154,7 @@ option(CRESTAPI_BUILD_EXAMPLES
     "Flag for building the examples" TRUE)
 if(CRESTAPI_BUILD_EXAMPLES)
     add_subdirectory(examples)
+    ## add_subdirectory(acts)
 endif()
 
 # Export the project.
diff --git a/acts/CMakeLists.txt b/acts/CMakeLists.txt
new file mode 100644
index 0000000..5f8152e
--- /dev/null
+++ b/acts/CMakeLists.txt
@@ -0,0 +1,54 @@
+# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
+# Set the name of the package.
+cmake_minimum_required(VERSION 3.12)
+project(CrestResolver VERSION 6.1 LANGUAGES CXX)
+
+# Find the necessary externals.
+find_package(CrestApi REQUIRED)
+
+
+# Set the C++ standard to use.
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL
+    "Whether to enable compiler-specific C++ extensions")
+
+# Set the default library type to build.
+option(BUILD_SHARED_LIBS
+    "Flag for building shared/static libraries" TRUE)
+
+# Use the standard GNU installation directories.
+include(GNUInstallDirs)
+set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/CrestResolver")
+
+# Turn on all warnings with GCC and Clang.
+if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
+endif()
+
+# Declare the header and source file names.
+set(HEADERS
+    "./CrestFileResolver.h"
+)
+
+set(SOURCES
+    "./CrestFileResolver.cxx"
+)
+
+# Set up the build of the main library of the project.
+add_library(CrestResolverLib ${SOURCES} ${HEADERS})
+target_link_libraries(CrestResolverLib
+    PUBLIC
+        CURL::libcurl
+        Boost::boost
+        nlohmann_json::nlohmann_json
+        CrestApi::CrestApiLib
+    PRIVATE
+        OpenSSL::SSL
+)
+target_include_directories(CrestResolverLib
+    PUBLIC
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
diff --git a/acts/CrestFileResolver.cxx b/acts/CrestFileResolver.cxx
new file mode 100644
index 0000000..a625d14
--- /dev/null
+++ b/acts/CrestFileResolver.cxx
@@ -0,0 +1,80 @@
+
+/**
+ * @file CrestApi/test/CrestApi_test.cxx
+ * @brief Some tests for server methods.
+ */
+
+#include "CrestFileResolver.h"
+
+using namespace Crest;
+
+std::string CrestFileResolver::resolveFilePath(const std::string &geometry_gtag,
+                                               const std::string &label,
+                                               const std::string &file_name) {
+  // Here you can implement the logic to resolve the file path.
+  // Use crestapi to resolve mappings and detect the tag.
+  std::cout << "Load mappings for " << geometry_gtag << std::endl;
+  GlobalTagMapSetDto mapset =
+      m_crestApi.findGlobalTagMap(geometry_gtag, "Trace");
+  std::cout << "test: listTagsInGlobalTag (Trace) =" << std::endl;
+  std::cout << mapset.toJson().dump(4) << std::endl;
+
+  if (mapset.getResources().size() < 1) {
+    std::cerr << "No mapping found for the global tag: " << geometry_gtag
+              << std::endl;
+    return "";
+  }
+  // Get the mapping (geometry tag name)
+  GlobalTagMapDto map = mapset.getResources()[0];
+  std::cout << "Found map " << map.toJson().dump(4) << std::endl;
+  std::cout << "Compare label " << this->mat_label << " with " << map.getLabel()
+            << std::endl;
+  // Check that the label is correct
+  if (this->mat_label != map.getLabel()) {
+    std::cerr << "Label mismatch: expected " << mat_label << ", got "
+              << map.getLabel() << std::endl;
+    return "";
+  }
+  std::string mat_tag = map.getTagName();
+  std::cout << "Load iovs for tag " << mat_tag << std::endl;
+  // Now get the IOV.
+  IovSetDto iovset =
+      m_crestApi.selectIovs(mat_tag, 0, 10, 0, 10, 0, "id.since:ASC");
+  if (iovset.getResources().size() < 1) {
+    std::cerr << "No IOV found for the tag: " << mat_tag << std::endl;
+    return "";
+  }
+  // Get the IOV
+  IovDto iov = iovset.getResources()[0];
+  std::cout << "Found IOV " << iov.toJson().dump(4) << std::endl;
+  // Get the payload using the iov hash
+  std::string hash = iov.getPayloadHash();
+  std::string payload = m_crestApi.getPayload(hash);
+  // Dump the payload to a file
+  std::string file_path = m_file_path + file_name;
+  // Write the payload to the file
+  std::cout << "Dump payload to file " << file_path << std::endl;
+  dumpToFile(file_path, payload);
+  return file_path;
+}
+
+void CrestFileResolver::dumpToFile(const std::string &filePath,
+                                   const std::string &content) {
+  // Open the file in write mode
+  std::ofstream outFile(filePath);
+
+  // Check if the file was successfully opened
+  if (!outFile) {
+    std::cerr << "Error: Could not open file " << filePath << " for writing."
+              << std::endl;
+    return;
+  }
+
+  // Write the content to the file
+  outFile << content;
+
+  // Close the file
+  outFile.close();
+
+  std::cout << "Content successfully written to " << filePath << std::endl;
+}
diff --git a/acts/CrestFileResolver.h b/acts/CrestFileResolver.h
new file mode 100644
index 0000000..128bf0e
--- /dev/null
+++ b/acts/CrestFileResolver.h
@@ -0,0 +1,68 @@
+
+/**
+ * @file CrestApi/test/CrestApi_test.cxx
+ * @brief Some tests for server methods.
+ */
+#ifndef CRESTAPI_CRESTRESOLVER_H
+#define CRESTAPI_CRESTRESOLVER_H
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "CrestApi/CrestApi.h"
+#include "CrestApi/GlobalTagMapDto.h"
+#include "CrestApi/GlobalTagMapSetDto.h"
+#include "CrestApi/IovSetDto.h"
+#include "CrestApi/StringUtils.h"
+
+using namespace Crest;
+
+/**
+ * Class to resolve a geometry tracking file.
+ * These files are linked to a geometry global tag, and contain only one IOV
+ * (since=0).
+ */
+class CrestFileResolver {
+ private:
+  std::string m_crest_server;
+  CrestApi m_crestApi;
+  // This should be somehow linked to the type of geometry tag (ITK, ...)
+  std::string mat_label = "ITK";
+  // Base directoy where the file will be dumped.
+  std::string m_file_path;
+
+ public:
+  CrestFileResolver(const std::string &crest_server)
+      : m_crest_server(crest_server),
+        m_crestApi(crest_server),
+        m_file_path("/tmp/crest_test/") {
+    // Check CrestApi version.
+    if (m_crestApi.getCrestVersion() != "v6.0") {
+      std::cerr << "CrestApi version mismatch!" << std::endl;
+    }
+  }
+
+  CrestFileResolver(const std::string &crest_server,
+                    const std::string &file_path, const std::string &label)
+      : m_crest_server(crest_server),
+        m_crestApi(crest_server),
+        mat_label(label),
+        m_file_path(file_path) {
+    // Check CrestApi version
+    if (m_crestApi.getCrestVersion() != "v6.0") {
+      std::cerr << "CrestApi version mismatch!" << std::endl;
+    }
+  }
+  // Destructor
+  ~CrestFileResolver() = default;
+
+  // Resolve the file path for a given geometry global tag.
+  std::string resolveFilePath(const std::string &geometry_gtag,
+                              const std::string &label,
+                              const std::string &file_name);
+  // Dump the payload to a file.
+  void dumpToFile(const std::string &filePath, const std::string &content);
+};
+
+#endif  // CRESTAPI_CRESTRESOLVER_H
\ No newline at end of file
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index a3aef12..68e1cb7 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -8,6 +8,7 @@ function(crestapi_add_test TESTNAME)
             Boost::unit_test_framework
             nlohmann_json::nlohmann_json
             CrestApiLib
+            CrestResolverLib
     )
     add_test(
         NAME ${TESTNAME}
@@ -15,9 +16,10 @@ function(crestapi_add_test TESTNAME)
     )
 endfunction()
 
+
 # Declare the tests.
 crestapi_add_test(CrestApi_test)
 crestapi_add_test(CrestApiFs_test)
 crestapi_add_test(json_parse)
 crestapi_add_test(test-json)
-crestapi_add_test(CrestFileResolver)
+crestapi_add_test(CrestFileResolver_test)
diff --git a/test/CrestFileResolver.cxx b/test/CrestFileResolver.cxx
deleted file mode 100644
index f0da3d5..0000000
--- a/test/CrestFileResolver.cxx
+++ /dev/null
@@ -1,151 +0,0 @@
-
-/**
- * @file CrestApi/test/CrestApi_test.cxx
- * @brief Some tests for server methods.
- */
-
-#include "CrestApi/GlobalTagMapDto.h"
-#include "CrestApi/GlobalTagMapSetDto.h"
-#include "CrestApi/IovSetDto.h"
-#define BOOST_TEST_DYN_LINK
-#define BOOST_TEST_MAIN
-#define BOOST_TEST_MODULE TEST_CRESTAPI
-
-#include <fstream>
-#include <iostream>
-#include <string>
-
-#include "../CrestApi/CrestApi.h"
-#include "../CrestApi/StringUtils.h"
-
-using namespace Crest;
-
-const std::string crest_server = "http://atlaf-alma9-02.cern.ch:8080/api-v6.0";
-const std::string global_tag = "TEST-GEOMETRY-01";
-
-class CrestFileResolver {
- private:
-  std::string m_crest_server;
-  CrestApi m_crestApi;
-  std::string geometry_global_tag;
-  std::string mat_tag = "none";
-  std::string mat_label = "/Material/TrackingGeo";
-  std::string m_file_path;
-
- public:
-  CrestFileResolver(const std::string &crest_server)
-      : m_crest_server(crest_server),
-        m_crestApi(crest_server),
-        geometry_global_tag("TEST-GEOMETRY-01"),
-        m_file_path("/tmp/crest_test/") {
-    // Check CrestApi version
-    if (m_crestApi.getCrestVersion() != "v6.0") {
-      std::cerr << "CrestApi version mismatch!" << std::endl;
-    }
-  }
-
-  CrestFileResolver(const std::string &crest_server,
-                    const std::string &file_path,
-                    const std::string &geometry_gtag, const std::string &label)
-      : m_crest_server(crest_server),
-        m_crestApi(crest_server),
-        geometry_global_tag(geometry_gtag),
-        mat_tag("none"),
-        mat_label(label),
-        m_file_path(file_path) {
-    // Check CrestApi version
-    if (m_crestApi.getCrestVersion() != "v6.0") {
-      std::cerr << "CrestApi version mismatch!" << std::endl;
-    }
-  }
-
-  std::string resolveFilePath(const std::string &geometry_gtag,
-                              const std::string &file_name) {
-    // Here you can implement the logic to resolve the file path.
-    // Use crestapi to resolve mappings and detect the tag.
-    std::cout << "Load mappings for " << geometry_global_tag << std::endl;
-    GlobalTagMapSetDto mapset =
-        m_crestApi.findGlobalTagMap(geometry_global_tag, "Trace");
-    std::cout << "test: listTagsInGlobalTag (Trace) =" << std::endl;
-    std::cout << mapset.toJson().dump(4) << std::endl;
-
-    if (mapset.getResources().size() < 1) {
-      std::cerr << "No mapping found for the global tag: "
-                << geometry_global_tag << std::endl;
-      return "";
-    }
-    // Get the mapping (geometry tag name)
-    GlobalTagMapDto map = mapset.getResources()[0];
-    std::cout << "Found map " << map.toJson().dump(4) << std::endl;
-    std::cout << "Compare label " << this->mat_label << " with "
-              << map.getLabel() << std::endl;
-    // Check that the label is correct
-    if (this->mat_label != map.getLabel()) {
-      std::cerr << "Label mismatch: expected " << mat_label << ", got "
-                << map.getLabel() << std::endl;
-      return "";
-    }
-    this->mat_tag = map.getTagName();
-    std::cout << "Load iovs for tag " << this->mat_tag << std::endl;
-    // Now get the IOV.
-    IovSetDto iovset =
-        m_crestApi.selectIovs(this->mat_tag, 0, 10, 0, 10, 0, "id.since:ASC");
-    if (iovset.getResources().size() < 1) {
-      std::cerr << "No IOV found for the tag: " << this->mat_tag << std::endl;
-      return "";
-    }
-    // Get the IOV
-    IovDto iov = iovset.getResources()[0];
-    std::cout << "Found IOV " << iov.toJson().dump(4) << std::endl;
-    // Get the payload using the iov hash
-    std::string hash = iov.getPayloadHash();
-    std::string payload = m_crestApi.getPayload(hash);
-    // Dump the payload to a file
-    std::string file_path = m_file_path + file_name;
-    // Write the payload to the file
-    std::cout << "Dump payload to file " << file_path << std::endl;
-    dumpToFile(file_path, payload);
-    return file_path;
-  }
-
-  void dumpToFile(const std::string &filePath, const std::string &content) {
-    // Open the file in write mode
-    std::ofstream outFile(filePath);
-
-    // Check if the file was successfully opened
-    if (!outFile) {
-      std::cerr << "Error: Could not open file " << filePath << " for writing."
-                << std::endl;
-      return;
-    }
-
-    // Write the content to the file
-    outFile << content;
-
-    // Close the file
-    outFile.close();
-
-    std::cout << "Content successfully written to " << filePath << std::endl;
-  }
-};
-
-int main() {
-  std::string geoglobal_tag = "TEST-GEOMETRY-01";
-  std::cout << "Global tag for geometry: " << geoglobal_tag << std::endl;
-
-  try {
-    // Create an instance of CrestFileResolver
-    CrestFileResolver crestFileResolver(crest_server, "/tmp/crest_test/",
-                                        geoglobal_tag, "/Material/TrackingGeo");
-    // Resolve the file path
-    std::string file_path =
-        crestFileResolver.resolveFilePath(geoglobal_tag, "test_file.json");
-    // Print the parsed inner JSON
-    std::cout << "Dumped JSON from Geometry Global Tag for tracking material: "
-              << file_path << std::endl;
-  } catch (const std::exception &e) {
-    std::cerr << "Error parsing JSON: " << e.what() << std::endl;
-  }
-
-  return 0;
-}
diff --git a/test/CrestFileResolver_test.cxx b/test/CrestFileResolver_test.cxx
new file mode 100644
index 0000000..abb1b41
--- /dev/null
+++ b/test/CrestFileResolver_test.cxx
@@ -0,0 +1,46 @@
+
+/**
+ * @file CrestApi/test/CrestApi_test.cxx
+ * @brief Some tests for server methods.
+ */
+
+#include "CrestApi/GlobalTagMapDto.h"
+#include "CrestApi/GlobalTagMapSetDto.h"
+#include "CrestApi/IovSetDto.h"
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MAIN
+#define BOOST_TEST_MODULE TEST_CRESTAPI
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "../CrestApi/CrestApi.h"
+#include "../CrestApi/StringUtils.h"
+#include "../acts/CrestFileResolver.h"
+
+using namespace Crest;
+
+const std::string crest_server = "http://atlaf-alma9-02.cern.ch:8080/api-v6.0";
+const std::string global_tag = "TEST-GEOMETRY-01";
+
+int main() {
+  std::string geoglobal_tag = "TEST-GEOMETRY-01";
+  std::cout << "Global tag for geometry: " << geoglobal_tag << std::endl;
+
+  try {
+    // Create an instance of CrestFileResolver
+    CrestFileResolver crestFileResolver(crest_server, "/tmp/crest_test/",
+                                        "/Material/TrackingGeo");
+    // Resolve the file path
+    std::string file_path = crestFileResolver.resolveFilePath(
+        geoglobal_tag, "/Material/TrackingGeo", "test_file.json");
+    // Print the parsed inner JSON
+    std::cout << "Dumped JSON from Geometry Global Tag for tracking material: "
+              << file_path << std::endl;
+  } catch (const std::exception &e) {
+    std::cerr << "Error parsing JSON: " << e.what() << std::endl;
+  }
+
+  return 0;
+}
-- 
GitLab


From bed2fbf27bf4043a9ef7d7be30cc996f9df39f8d Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Fri, 4 Apr 2025 15:50:24 +0200
Subject: [PATCH 10/33] remove CrestFileResolver from cmake and tests

---
 test/CMakeLists.txt | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 68e1cb7..37d9c7b 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -8,7 +8,7 @@ function(crestapi_add_test TESTNAME)
             Boost::unit_test_framework
             nlohmann_json::nlohmann_json
             CrestApiLib
-            CrestResolverLib
+            # this is for ACTS: CrestResolverLib
     )
     add_test(
         NAME ${TESTNAME}
@@ -21,5 +21,5 @@ endfunction()
 crestapi_add_test(CrestApi_test)
 crestapi_add_test(CrestApiFs_test)
 crestapi_add_test(json_parse)
-crestapi_add_test(test-json)
-crestapi_add_test(CrestFileResolver_test)
+## crestapi_add_test(test-json)
+## crestapi_add_test(CrestFileResolver_test)
-- 
GitLab


From 042b5f5b21223ef0522d5cf7da2c3c42d8839f20 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Fri, 4 Apr 2025 16:47:07 +0200
Subject: [PATCH 11/33] fix CI

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f51a594..b85569c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,5 +1,5 @@
 variables:
-    VERSION: "5.2"
+    VERSION: "6.2"
     IMAGE: "crestapi"
      # Overriding the variables from the template files.
     TESTS_DIR: test
-- 
GitLab


From 0717448571a78fe3d28dc8bd11f0169144945a7c Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Fri, 4 Apr 2025 16:52:44 +0200
Subject: [PATCH 12/33] fix CI

---
 .gitlab-ci.yml | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b85569c..593a525 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -72,8 +72,6 @@ build_container_job:
     CONTEXT_DIR: ""
     PUSH_IMAGE: "true"
     DOCKER_FILE_NAME: "Dockerfile"
-  rules:
-    - if: '$CI_COMMIT_REF_NAME !~ /^auto-fix-.*/'
 
 apitest:
   stage: test
@@ -107,6 +105,6 @@ apitest:
 crestcmd:  # or any other appropriate stage
   stage: compile-crestcmd
   script:
-    - curl -X POST -F token=$CRESTCMD_TOKEN -F ref=release-5.1-CR2 https://gitlab.cern.ch/api/v4/projects/144796/trigger/pipeline
+    - curl -X POST -F token=$CRESTCMD_TOKEN -F ref=release-6.1-CR1 https://gitlab.cern.ch/api/v4/projects/144796/trigger/pipeline
   only:
     - release-5.1-CR2  # or whichever branch in Project 1 should trigger Project 2
\ No newline at end of file
-- 
GitLab


From bd8345e5eb9cb0620f64676d8acbcde074782050 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Fri, 4 Apr 2025 16:55:32 +0200
Subject: [PATCH 13/33] fix CI

---
 .gitlab-ci.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 593a525..2e74b11 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -63,8 +63,6 @@ auto-fix:
     - merge_requests  # Optionally, run this job only on merge requests
 
 build_container_job:
-  rules:
-    - if: $DO_DOCKER_IMAGE == "yes" && $CI_PIPELINE_SOURCE != 'merge_request_event'
   stage: build_docker_image
   extends: .build_kaniko
   variables:
@@ -72,6 +70,8 @@ build_container_job:
     CONTEXT_DIR: ""
     PUSH_IMAGE: "true"
     DOCKER_FILE_NAME: "Dockerfile"
+  rules:
+    - if: '$CI_COMMIT_REF_NAME !~ /^auto-fix-.*/'
 
 apitest:
   stage: test
-- 
GitLab


From 1ea504ec777712379a75cfbf356f0b63a8076039 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Fri, 4 Apr 2025 21:59:06 +0200
Subject: [PATCH 14/33] fix doc

---
 CRESTAPI_USAGE.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CRESTAPI_USAGE.md b/CRESTAPI_USAGE.md
index db8670c..731ca6b 100644
--- a/CRESTAPI_USAGE.md
+++ b/CRESTAPI_USAGE.md
@@ -38,7 +38,7 @@ Here a set of code snippets represting this workflow.
     {"name", tagname},
     {"timeType", "time"},
     {"description", "test"},
-    {"synchronization", "none"},
+    {"synchronization", "ALL"},
     {"insertionTime", "2018-12-18T11:32:58.081+0000"},
     {"modificationTime", "2018-12-18T11:32:57.952+0000"},
     {"payloadSpec", "JSON"}, // For athena reading you need to be careful to this field [crest-json-single-iov | crest-json-multi-iov]
-- 
GitLab


From de6f1c27f9756be4324fa2ae675ea2d89545d577 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Fri, 4 Apr 2025 21:59:35 +0200
Subject: [PATCH 15/33] fix doc

---
 examples/crest_example_fs.cxx     | 26 +++++++++++++-------------
 examples/crest_example_server.cxx |  2 +-
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/examples/crest_example_fs.cxx b/examples/crest_example_fs.cxx
index 63492cc..4a9d906 100644
--- a/examples/crest_example_fs.cxx
+++ b/examples/crest_example_fs.cxx
@@ -41,7 +41,7 @@ void testCreateGlobalTag_FS(const std::string &tagname) {
   std::cout << std::endl << "test file system: createGlobalTag" << std::endl;
   CrestApiFs myCrestApi = CrestApiFs(true, "/tmp/crest");
 
-  /* 
+  /*
 
   nlohmann::json js = {
       {"name", tagname},
@@ -107,14 +107,14 @@ void testCreateTag_FS(const std::string &tagname) {
   */
 
   // the tag innitialization with the constructor:
-  
+
   TagDto dto(tagname, "time", "test");
   dto.setSynchronization("ALL");
   dto.setObjectType("JSON");
   dto.setStatus("UNLOCKED");
   dto.setLastValidatedTime(0.0);
   dto.setEndOfValidity(0.0);
-  
+
   try {
     myCrestApi.createTag(dto);
     std::cout << std::endl << "test: createTag FS (success)" << std::endl;
@@ -272,16 +272,16 @@ void testStorePayloads_FS(const std::string &tagname) {
   TagDto dto = TagDto();
   dto = dto.fromJson(tag_js);
   */
-  
+
   // the tag innitialization with the constructor:
-  
+
   TagDto dto(tagname, "time", "test");
   dto.setSynchronization("ALL");
   dto.setObjectType("JSON");
   dto.setStatus("UNLOCKED");
   dto.setLastValidatedTime(0.0);
   dto.setEndOfValidity(0.0);
-  
+
   try {
     myCrestApi.createTag(dto);
     std::cout << std::endl
@@ -298,7 +298,7 @@ void testStorePayloads_FS(const std::string &tagname) {
 
   /*
   // OLD variant, StoreSetDto defined via JSON
-  
+
   // Your JSON string
   std::string jsonString = R"(
       {
@@ -325,7 +325,7 @@ void testStorePayloads_FS(const std::string &tagname) {
   */
 
   // New version:
-  
+
   StoreSetDto storeSetDto;
 
   StoreDto dto1(1000, "", "Sample data");
@@ -349,7 +349,7 @@ void testStorePayloads_FS(const std::string &tagname) {
   }
 
   /*
-  
+
   // Your JSON string
   std::string jsonString2 = R"(
       {
@@ -376,7 +376,7 @@ void testStorePayloads_FS(const std::string &tagname) {
   */
 
   // New version:
-  
+
   StoreSetDto storeSetDto2;
 
   StoreDto dto3(3000, "", "Sample data 3");
@@ -766,7 +766,7 @@ void createNTags_FS(const std::string &tagname, int n) {
     */
 
     // the tag innitialization with the constructor:
-    
+
     TagDto dto(tg, "time", "test");
     dto.setSynchronization("ALL");
     dto.setObjectType("JSON");
@@ -912,14 +912,14 @@ void createNGlobalTags_FS(const std::string &tagname, int n) {
     GlobalTagDto dto = GlobalTagDto();
     dto = dto.fromJson(js);
     */
-    
+
     // global tag innitialization with the constructor:
 
     GlobalTagDto dto(tg, "test", "1", "M");
     dto.setValidity(0);
     dto.setType("N");
     dto.setScenario("test");
-    
+
     try {
       myCrestApi.createGlobalTag(dto);
       std::cout << std::endl << "global tag " << tg << " created" << std::endl;
diff --git a/examples/crest_example_server.cxx b/examples/crest_example_server.cxx
index 4e84887..0ac1067 100644
--- a/examples/crest_example_server.cxx
+++ b/examples/crest_example_server.cxx
@@ -371,7 +371,7 @@ void testStorePayloads(const std::string &tagname) {
   */
 
   // New version:
-  
+
   StoreSetDto storeSetDto;
 
   StoreDto dto1(1000, "", "Sample data");
-- 
GitLab


From c11c3a173d12add12b51a5cf57eac6dad4a78546 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Mon, 7 Apr 2025 10:26:44 +0200
Subject: [PATCH 16/33] create separate library for CrestContainer.

---
 CMakeLists.txt                            |   3 +-
 tools/CMakeLists.txt                      |  90 +++++
 tools/CrestContainer.cxx                  | 411 +++++++++++++---------
 tools/CrestContainer.h                    | 109 +++---
 tools/cmake/CrestContainerConfig.cmake.in |  29 ++
 5 files changed, 416 insertions(+), 226 deletions(-)
 create mode 100644 tools/CMakeLists.txt
 create mode 100644 tools/cmake/CrestContainerConfig.cmake.in

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 111d154..cd45973 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -154,7 +154,8 @@ option(CRESTAPI_BUILD_EXAMPLES
     "Flag for building the examples" TRUE)
 if(CRESTAPI_BUILD_EXAMPLES)
     add_subdirectory(examples)
-    ## add_subdirectory(acts)
+    add_subdirectory(acts)
+    add_subdirectory(tools)
 endif()
 
 # Export the project.
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..694bb88
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,90 @@
+# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
+# Set the name of the package.
+cmake_minimum_required(VERSION 3.12)
+project(CrestContainer VERSION 6.1 LANGUAGES CXX)
+
+# Find the necessary externals.
+find_package(CrestApi REQUIRED)
+
+
+# Set the C++ standard to use.
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL
+    "Whether to enable compiler-specific C++ extensions")
+
+# Set the default library type to build.
+option(BUILD_SHARED_LIBS
+    "Flag for building shared/static libraries" TRUE)
+
+# Use the standard GNU installation directories.
+include(GNUInstallDirs)
+set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/CrestResolver")
+
+# Turn on all warnings with GCC and Clang.
+if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
+endif()
+
+# Declare the header and source file names.
+set(HEADERS
+    "./CrestContainer.h"
+)
+
+set(SOURCES
+    "./CrestContainer.cxx"
+)
+
+# Set up the build of the main library of the project.
+add_library(CrestContainerLib ${SOURCES} ${HEADERS})
+target_link_libraries(CrestContainerLib
+    PUBLIC
+        CURL::libcurl
+        Boost::boost
+        nlohmann_json::nlohmann_json
+        CrestApi::CrestApiLib
+    PRIVATE
+        OpenSSL::SSL
+)
+target_include_directories(CrestContainerLib
+    PUBLIC
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+set_target_properties(CrestContainerLib PROPERTIES
+    PUBLIC_HEADER "${HEADERS}"
+    VERSION "${PROJECT_VERSION}"
+)
+
+# Install the library.
+install(TARGETS CrestContainerLib
+    EXPORT CrestContainerTargets
+    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+    LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+    ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+    PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/CrestContainer"
+)
+
+# Export the project.
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+    "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CrestContainerConfigVersion.cmake"
+    VERSION "${PROJECT_VERSION}"
+    COMPATIBILITY AnyNewerVersion
+)
+configure_package_config_file(
+    "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CrestContainerConfig.cmake.in"
+    "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CrestContainerConfig.cmake"
+    INSTALL_DESTINATION "${CMAKE_INSTALL_CMAKEDIR}"
+    PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_LIBDIR
+)
+install(FILES
+    "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CrestContainerConfigVersion.cmake"
+    "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CrestContainerConfig.cmake"
+    DESTINATION "${CMAKE_INSTALL_CMAKEDIR}"
+)
+install(EXPORT CrestContainerTargets
+    NAMESPACE CrestApi::
+    DESTINATION "${CMAKE_INSTALL_CMAKEDIR}"
+)
diff --git a/tools/CrestContainer.cxx b/tools/CrestContainer.cxx
index 2e31ab0..a7de28d 100644
--- a/tools/CrestContainer.cxx
+++ b/tools/CrestContainer.cxx
@@ -17,29 +17,13 @@
 
 using json = nlohmann::json;
 
-Crest::CrestContainer::CrestContainer() : m_modeId(ModeId::Standard) {
-  initStringToType();
+Crest::CrestContainer::CrestContainer(Crest::ModeId mode) : m_modeId(mode) {
+  ;
 }
 Crest::CrestContainer::~CrestContainer() {
   flush();
 }
 
-void Crest::CrestContainer::initStringToType() {
-  // Loop over key value pairs in s_typeToString map and fill s_StringToType
-  for (const auto &[key, value] : s_typeToString) {
-    s_StringToType[value] = key;
-  }
-}
-
-void Crest::CrestContainer::addStreamerInfoField(const std::string &key,
-                                                 const std::string &value) {
-  m_streamer_info_data[key] = value;
-}
-
-void Crest::CrestContainer::cleanStreamerInfo() {
-  m_streamer_info_data.clear();
-}
-
 bool Crest::CrestContainer::isVectorPayload() {
   return m_isVectorPayload;
 }
@@ -49,11 +33,21 @@ void Crest::CrestContainer::setVectorPayload(bool isVectorPayload) {
 
 Crest::TypeId Crest::CrestContainer::stringToTypeId(
     const std::string &type) const {
-  auto it = s_StringToType.find(type);
-  if (it == s_StringToType.end()) {
-    throw CommonCrestException("The type of parameter is not defined.");
+  for (auto &t : s_typeToString) {
+    if (t.second.compare(type) == 0)
+      return t.first;
   }
-  return it->second;
+  throw CommonCrestException("The type of parameter is not defined.");
+}
+
+bool Crest::CrestContainer::compareStrTimestamp(std::string as,
+                                                std::string bs) {
+  auto a = std::stol(as);
+  auto b = std::stol(bs);
+
+  // first parameter should be greater than
+  // second one (for decreasing order)
+  return a < b;
 }
 
 // Function to convert an integer to a TypeId
@@ -64,8 +58,8 @@ Crest::TypeId Crest::CrestContainer::intToTypeId(int value) const {
   return static_cast<Crest::TypeId>(value);
 }
 
-std::string Crest::CrestContainer::base64Encode(const uint8_t *data,
-                                                unsigned int len) {
+std::string Crest::CrestContainer::base64_encode(const uint8_t *data,
+                                                 unsigned int len) {
   using namespace boost::archive::iterators;
   typedef base64_from_binary<transform_width<const char *, 6, 8>>
       base64_encoder;
@@ -81,7 +75,7 @@ std::string Crest::CrestContainer::base64Encode(const uint8_t *data,
 }
 
 // Decode base64 data to binary
-std::vector<unsigned char> Crest::CrestContainer::base64Decode(
+std::vector<unsigned char> Crest::CrestContainer::base64_decode(
     const std::string &encodedData) {
   using namespace boost::archive::iterators;
   typedef transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>
@@ -160,10 +154,8 @@ void Crest::CrestContainer::addRecord(const char *name, int number, ...) {
       case TypeId::String4k:
       case TypeId::String64k:
       case TypeId::String16M:
-      case TypeId::String128M:
       case TypeId::Blob64k:
       case TypeId::Blob16M:
-      case TypeId::Blob128M:
         m_row[name] = std::string(va_arg(ap, const char *));
         break;
       default:
@@ -179,22 +171,21 @@ void Crest::CrestContainer::addData(const char *channel_id) {
 
 void Crest::CrestContainer::addExternalData(const char *channel_id,
                                             const nlohmann::json &data) {
-
-  validatePayloadSize(data);
+  // std::cerr<<"addExternalData channel_id="<<channel_id<<"
+  // json="<<data.dump()<<std::endl;
   nlohmann::json arr_data =
       m_isVectorPayload ? nlohmann::json::array() : nlohmann::json();
-
-  for (const auto &data_row : data)  // m_vector_data)
-  {
-    auto row_arr_data = createRowArray(data_row);
-
-    if (m_isVectorPayload) {
+  if (m_isVectorPayload) {
+    for (const auto &data_row : data)  // m_vector_data)
+    {
+      auto row_arr_data = createRowArray(data_row);
       arr_data.push_back(row_arr_data);
-    } else {
-      arr_data = row_arr_data;
     }
+    m_payload[channel_id] = arr_data;
+  } else {
+    auto row_arr_data = createRowArray(data);
+    m_payload[channel_id] = row_arr_data;
   }
-  m_payload[channel_id] = arr_data;
   m_vector_data.clear();
   m_row.clear();
 }
@@ -203,12 +194,12 @@ void Crest::CrestContainer::validatePayloadSize(
     const nlohmann::json &data) const {
   if (!data.is_array())
     throw CommonCrestException(
-        "The format of data is wrong. It should be the "
-        "vector (size 1 for non vector payload)");
+        "The format of data is wrong. It should be the vector (size 1 for non "
+        "vector payload)");
   if (!m_isVectorPayload && m_vector_data.size() > 1) {
     throw CommonCrestException(
-        "The payload is not a vector, but the size "
-        "seems to be larger than 1....");
+        "The payload is not a vector, but the size seems to be larger than "
+        "1....");
   }
 }
 
@@ -228,13 +219,28 @@ nlohmann::json Crest::CrestContainer::createRowArray(
 void Crest::CrestContainer::addIov(const uint64_t since) {
   m_iov_data["since"] = since;
   m_iov_data["data"] = m_payload;
-  m_full_data[std::to_string(since)] = m_payload;
+  m_full_data[since] = m_payload;
   m_payload.clear();
 }
 
 void Crest::CrestContainer::selectIov(const uint64_t since) {
+  // std::cerr<<"Crest::CrestContainer::selectIov start"<<std::endl;
   m_iov_data["since"] = since;
-  m_iov_data["data"] = m_full_data[std::to_string(since)];
+  if (m_full_data.contains(since)) {
+    m_iov_data["data"] = m_full_data[since];
+    m_payload = m_full_data[since];
+  } else {
+    m_iov_data["data"] = {};
+    m_payload = {};
+  }
+  // std::cerr<<"Crest::CrestContainer::selectIov end"<<std::endl;
+}
+
+uint64_t Crest::CrestContainer::getFirstTime() {
+  if (m_full_data.size() == 0) {
+    throw CommonCrestException("No IOV in CrestContainer");
+  }
+  return m_full_data.begin()->first;
 }
 
 std::vector<std::string> Crest::CrestContainer::channelIds() {
@@ -242,6 +248,7 @@ std::vector<std::string> Crest::CrestContainer::channelIds() {
   for (auto &x : m_iov_data["data"].items()) {
     chs.push_back(x.key());
   }
+  sort(chs.begin(), chs.end(), Crest::CrestContainer::compareStrTimestamp);
   return chs;
 }
 const std::vector<std::pair<std::string, Crest::TypeId>> &
@@ -249,7 +256,7 @@ Crest::CrestContainer::getMPayloadSpec() {
   return m_payload_spec;
 }
 
-const nlohmann::json &Crest::CrestContainer::getPayloadChannel(
+const nlohmann::json Crest::CrestContainer::getPayloadChannel(
     const char *channel_id) {
   if (m_payload.empty()) {
     m_payload = m_iov_data["data"];
@@ -297,8 +304,7 @@ void Crest::CrestContainer::putRow2Vector() {
 const nlohmann::json &Crest::CrestContainer::getRow() {
   if (m_isVectorPayload)
     return m_vector_data;
-  m_vector_data.push_back(m_row);
-  return m_vector_data;
+  return m_row;
 }
 
 const nlohmann::json &Crest::CrestContainer::getPayload() {
@@ -336,27 +342,6 @@ std::string Crest::CrestContainer::getJsonIovData() {
   return m_iov_data.dump();
 }
 
-nlohmann::json Crest::CrestContainer::getStoreSetDto() {
-  nlohmann::json j;
-  nlohmann::json resources = nlohmann::json::array();
-
-  nlohmann::json storeDto;
-  // Loop over the iov list container to add the data to the storeDto
-  // Iterating over the keys of m_full_data
-  for (auto &[key, data] : m_full_data.items()) {
-    storeDto["since"] = (uint64_t)std::stoull(key);
-    storeDto["data"] = data.dump();
-    storeDto["streamerInfo"] = m_streamer_info_data.dump();
-    resources.push_back(storeDto);
-  }
-
-  j["resources"] = resources;
-  j["format"] = "StoreSetDto";
-  j["datatype"] = "data";
-  j["size"] = resources.size();
-  return j;
-}
-
 json Crest::CrestContainer::getPayloadSpec() {
   json pspec_data = json::array();
   for (auto &column : m_payload_spec) {
@@ -523,17 +508,120 @@ void Crest::CrestContainer::parseOldFormat(std::string &colName,
       }
       default: {
         throw std::runtime_error("UNTREATED TYPE!");
-        break;
       }
     }
+
   } catch (json::exception &e) {
     std::cerr << e.what() << std::endl;
     throw std::runtime_error(e.what());
   }
 }
-
-void Crest::CrestContainer::fromJson(const uint64_t since,
-                                     const nlohmann::json &j_in) {
+void Crest::CrestContainer::parseData(const nlohmann::json &values) {
+  if (values.is_array() && values.size() == m_payload_spec.size()) {
+    for (size_t i = 0; i < values.size(); ++i) {
+      const auto &spec = m_payload_spec[i];
+      std::string colName = spec.first;
+      TypeId colType = spec.second;
+      if (values[i].is_string() &&
+          (colType == TypeId::UChar || colType == TypeId::Bool ||
+           colType == TypeId::Int16 || colType == TypeId::UInt16 ||
+           colType == TypeId::Int32 || colType == TypeId::UInt32 ||
+           colType == TypeId::Int64 || colType == TypeId::UInt63 ||
+           colType == TypeId::Float || colType == TypeId::Double)) {
+        parseOldFormat(colName, colType, values[i]);
+        continue;
+      }
+      if (values[i].is_null()) {
+        m_row[colName] = nullptr;
+        continue;
+      }
+      switch (colType) {
+        case TypeId::Bool:
+          m_row[colName] = values[i].get<bool>();
+          break;
+        case TypeId::UChar:
+          m_row[colName] = values[i].get<unsigned char>();
+          break;
+        case TypeId::Int16:
+        case TypeId::Int32:
+          m_row[colName] = values[i].get<int>();
+          break;
+        case TypeId::UInt16:
+        case TypeId::UInt32:
+          m_row[colName] = values[i].get<unsigned int>();
+          break;
+        case TypeId::UInt63:
+          m_row[colName] = values[i].get<uint64_t>();
+          break;
+        case TypeId::Int64:
+          m_row[colName] = values[i].get<int64_t>();
+          break;
+        case TypeId::Float:
+        case TypeId::Double:
+          m_row[colName] = values[i].get<double>();
+          break;
+        case TypeId::String255:
+        case TypeId::String4k:
+        case TypeId::String64k:
+        case TypeId::String16M:
+        case TypeId::String128M:
+          m_row[colName] = values[i].get<std::string>();
+          break;
+        case TypeId::Blob128M:
+        case TypeId::Blob64k:
+        case TypeId::Blob16M:
+          m_row[colName] = values[i].get<std::string>();
+          break;
+        default:
+          throw CommonCrestException(
+              "CrestContainer::parseData: Unsupported column type.");
+      }
+    }
+  } else {
+    if (!values.is_array())
+      std::cerr << "CrestContainer::parseData values is not array" << std::endl;
+    else
+      std::cerr << "CrestContainer::parseData values number=" << values.size()
+                << " m_payload_spec.size()=" << m_payload_spec.size()
+                << std::endl;
+    throw CommonCrestException(
+        "CrestContainer::parseData: Mismatch in number of values.");
+  }
+}
+std::vector<uint64_t> Crest::CrestContainer::fromJson(
+    uint64_t since, const nlohmann::json &j_in) {
+  if (m_modeId == Crest::ModeId::Standard) {
+    std::cerr << "CrestContainer::fromJson mode=Standard" << std::endl;
+    nlohmann::json j = j_in;
+    if (j.is_string()) {
+      std::istringstream ss(to_string(j));
+      std::string st;
+      ss >> std::quoted(st);
+      j = json::parse(st);
+    }
+    // Accessing the "data" object
+    if (j.contains("data") && j["data"].is_object()) {
+      readCommonType(since, j_in);
+    } else if (j.is_object()) {
+      readCommonType(since, j);
+    } else {
+      std::cerr << "CrestContainer::fromJson json:" << j << std::endl;
+      throw CommonCrestException(
+          "CrestContainer::fromJson: JSON  is not a JSON object.");
+    }
+    std::vector<uint64_t> ret;
+    ret.push_back(since);
+    return ret;
+  } else if (m_modeId == Crest::ModeId::DCS_FULL) {
+    std::cerr << "CrestContainer::fromJson mode=DCS_FULL" << std::endl;
+    return readDcsFullType(j_in);
+  } else {
+    throw CommonCrestException(
+        "CrestContainer::fromJson: Unsupported type of payload.");
+  }
+}
+std::vector<uint64_t> Crest::CrestContainer::readDcsFullType(
+    const nlohmann::json &j_in) {
   nlohmann::json j = j_in;
   if (j.is_string()) {
     std::istringstream ss(to_string(j));
@@ -541,96 +629,101 @@ void Crest::CrestContainer::fromJson(const uint64_t since,
     ss >> std::quoted(st);
     j = json::parse(st);
   }
-  if (m_modeId != ModeId::Standard)
-    throw CommonCrestException("Unsupported type of payload.");
-  // Accessing the "data" object
+  // std::cerr<<"readDcsFullType: "<<j<<std::endl;
+  nlohmann::json dcs_data;
   if (j.contains("data") && j["data"].is_object()) {
-    const auto &data = j["data"];
-
-    // Loop over each channel in the data
-    for (const auto &channel : data.items()) {
-      std::string channelKey = channel.key();
-      const auto &data_ch = channel.value();
-      nlohmann::json vecJson(json::value_t::array);
-      if (m_isVectorPayload)
-        vecJson = data_ch;
+    // std::cerr<<"readDcsFullType: "<<j<<std::endl;
+    dcs_data = j["data"];
+  } else
+    dcs_data = j;
+  std::vector<uint64_t> ret;
+  if (j.is_object()) {
+    for (const auto &[key, value] : dcs_data.items()) {
+      uint64_t since = std::stoull(key);
+      // std::cerr<<"read since="<<since<<std::endl;
+      readCommonType(since, value);
+      ret.push_back(since);
+      /*if(data.contains("since"))
+        since=data["since"].template get<std::int64_t>();
       else
-        vecJson.push_back(data_ch);
-
-      for (const auto &values : vecJson) {
-        // TODO: this will not work for vector payloads
-        // Check if the number of values in the array matches the expected
-        // number of columns
-        if (values.is_array() && values.size() == m_payload_spec.size()) {
-          for (size_t i = 0; i < values.size(); ++i) {
-            const auto &spec = m_payload_spec[i];
-            std::string colName = spec.first;
-            TypeId colType = spec.second;
-            if (values[i].is_string() &&
-                (colType == TypeId::Bool || colType == TypeId::Int16 ||
-                 colType == TypeId::UInt16 || colType == TypeId::Int32 ||
-                 colType == TypeId::UInt32 || colType == TypeId::Int64 ||
-                 colType == TypeId::UInt63 || colType == TypeId::Float ||
-                 colType == TypeId::Double)) {
-              parseOldFormat(colName, colType, values[i]);
-              continue;
-            }
-            switch (colType) {
-              case TypeId::Bool:
-                m_row[colName] = values[i].get<bool>();
-                break;
-              case TypeId::UChar:
-                m_row[colName] = values[i].get<unsigned char>();
-                break;
-              case TypeId::Int16:
-              case TypeId::Int32:
-                m_row[colName] = values[i].get<int>();
-                break;
-              case TypeId::UInt16:
-              case TypeId::UInt32:
-                m_row[colName] = values[i].get<unsigned int>();
-                break;
-              case TypeId::UInt63:
-                m_row[colName] = values[i].get<uint64_t>();
-                break;
-              case TypeId::Int64:
-                m_row[colName] = values[i].get<int64_t>();
-                break;
-              case TypeId::Float:
-              case TypeId::Double:
-                m_row[colName] = values[i].get<double>();
-                break;
-              case TypeId::String255:
-              case TypeId::String4k:
-              case TypeId::String64k:
-              case TypeId::String16M:
-              case TypeId::String128M:
-                m_row[colName] = values[i].get<std::string>();
-                break;
-              case TypeId::Blob128M:
-              case TypeId::Blob64k:
-              case TypeId::Blob16M:
-                m_row[colName] = values[i].get<std::string>();
-                break;
-              default:
-                throw CommonCrestException("Unsupported column type.");
-            }
-          }
-        } else {
-          std::cerr << "CrestContainer::fromJson: Mismatch in number of values "
-                       "for channel "
-                    << channelKey << std::endl;
-        }
-        if (m_isVectorPayload)
-          putRow2Vector();
-      }
-      addData(channelKey.c_str());
+        throw CommonCrestException("CrestContainer::readDcsFullType: Wrong
+      payload format."); readCommonType(since,data);*/
     }
-  } else {
-    std::cerr << "CrestContainer::fromJson: JSON does not contain a 'data' "
-                 "object or it is not a JSON object."
-              << std::endl;
-    std::cerr << "CrestContainer::fromJson json:" << j << std::endl;
+  }
+  return ret;
+}
+void Crest::CrestContainer::readCommonType(uint64_t since,
+                                           const nlohmann::json &j_in) {
+  // std::cerr<<"readCommonType j_in="<<j_in.dump()<<std::endl;
+  //  Loop over each channel in the data
+  for (const auto &channel : j_in.items()) {
+    std::string channelKey = channel.key();
+    const auto &data_ch = channel.value();
+    nlohmann::json vecJson(json::value_t::array);
+    if (m_isVectorPayload)
+      vecJson = data_ch;
+    else
+      vecJson.push_back(data_ch);
+
+    for (const auto &values : vecJson) {
+      parseData(values);
+      if (m_isVectorPayload)
+        putRow2Vector();
+    }
+    addData(channelKey.c_str());
   }
   addIov(since);
+  // std::cout << "       m_iov_data: " << m_iov_data.dump(2) << std::endl;
+  //  std::cout << "CondContainer: from json method has filled internal
+  //  attributes" << std::endl;
+}
+
+nlohmann::json Crest::CrestContainer::getStoreSetDto(uint64_t period) {
+  nlohmann::json j;
+  nlohmann::json resources = nlohmann::json::array();
+  if (m_modeId == Crest::ModeId::Standard) {
+    nlohmann::json storeDto;
+    // Loop over the iov list container to add the data to the storeDto
+    for (const auto &[key, value] : m_full_data) {
+      storeDto["since"] = key;
+      storeDto["data"] = value.dump();
+      storeDto["streamerInfo"] = "";  // m_streamer_info_data.dump();
+      resources.push_back(storeDto);
+    }
+  } else if (m_modeId == Crest::ModeId::DCS_FULL) {
+    uint64_t since = getFirstTime();
+    //  std::cerr<<"Number of IOV="<<m_full_data.size()<<std::endl;
+    nlohmann::json storeDto;
+    nlohmann::json dcs_payload;
+    nlohmann::json curPayload = m_full_data[since];
+    // std::cerr<<"Number of chs="<<curPayload.size()<<std::endl;
+    // for (std::pair<uint64_t, uint64_t> pair : m_iovs) {
+    for (const auto &[key, value] : m_full_data) {
+      curPayload.update(value);
+      dcs_payload[std::to_string(key)] = curPayload;
+      if (period > 0 && (key - since) > period) {
+        storeDto["since"] = since;
+        storeDto["data"] = dcs_payload.dump();
+        storeDto["streamerInfo"] = "";
+        resources.push_back(storeDto);
+        since = key;
+        dcs_payload = {};
+      }
+    }
+    // nlohmann::json obj={};
+    // obj["iovs"]=iovs;
+    // obj["obj"]=dcs_payload;
+    if (dcs_payload.size() > 0) {
+      storeDto["since"] = since;
+      storeDto["data"] = dcs_payload.dump();
+      storeDto["streamerInfo"] = "";
+      resources.push_back(storeDto);
+    }
+  }
+  j["resources"] = resources;
+  j["format"] = "StoreSetDto";
+  j["datatype"] = "data";
+  j["size"] = resources.size();
+
+  return j;
 }
diff --git a/tools/CrestContainer.h b/tools/CrestContainer.h
index f5315e3..793b326 100644
--- a/tools/CrestContainer.h
+++ b/tools/CrestContainer.h
@@ -68,50 +68,22 @@ const static std::map<TypeId, std::string> s_typeToString = {
 enum class ModeId { Standard, DCS, DCS_FULL };
 class CrestContainer {
  private:
-  std::map<std::string, TypeId> s_StringToType;
   std::vector<std::pair<std::string, TypeId>> m_payload_spec;
   nlohmann::json m_payload = {};
   nlohmann::json m_row = {};
   nlohmann::json m_iov_data = {};
-  nlohmann::json m_streamer_info_data = {};
   nlohmann::json m_vector_data = nlohmann::json::array();
-  // nlohmann::json m_iov_list=nlohmann::json::array();
-
-  nlohmann::json m_full_data = {};
+  std::map<uint64_t, nlohmann::json> m_full_data;
   ModeId m_modeId;
   bool m_isVectorPayload = false;
-  void initStringToType();
 
-  void validatePayloadSize(const nlohmann::json &data) const;
-  nlohmann::json createRowArray(const nlohmann::json &data_row) const;
-  const nlohmann::json &getRow();
+  void validatePayloadSize(const nlohmann::json& data) const;
+  nlohmann::json createRowArray(const nlohmann::json& data_row) const;
+  const nlohmann::json& getRow();
 
  public:
-  /**
-   * Constructor and destructor.
-   * The container is used to store the payload and the iov data, helping in the
-   * definition of the payload structure. The payload is a json object that is
-   * added to the iov data. It contains a set of channels, each one with its own
-   * data. Once the container has been filled, it can be converted to a json
-   * object. When consumed, it is necessary to flush it in order to reuse it.
-   */
-  CrestContainer();
+  CrestContainer(ModeId mode = ModeId::Standard);
   ~CrestContainer();
-
-  /**
-   * Utility function to add a field to the streamer info.
-   * The streamer info is a json object that is added to the payload.
-   * It is used to store metadata information about the payload.
-   * @param key The key of the field.
-   * @param value The value of the field.
-   */
-  void addStreamerInfoField(const std::string &key, const std::string &value);
-
-  /**
-   * Utility function to clean the streamer info.
-   */
-  void cleanStreamerInfo();
-
   /**
    * @brief It adds a column to the payload specification.
    * @param name The name of the column.
@@ -121,9 +93,9 @@ class CrestContainer {
    * m_payload_spec. The methods with a different signature are just overloads
    * to make it easier to use.
    */
-  void addColumn(const std::string &name, TypeId type);
-  void addColumn(const std::string &name, const char *type);
-  void addColumn(const std::string &name, uint32_t type);
+  void addColumn(const std::string& name, TypeId type);
+  void addColumn(const std::string& name, const char* type);
+  void addColumn(const std::string& name, uint32_t type);
 
   /**
    * @brief It sets the mode of the container.
@@ -131,7 +103,7 @@ class CrestContainer {
   bool isVectorPayload();
   void setVectorPayload(bool isVectorPayload);
 
-  Crest::TypeId stringToTypeId(const std::string &type) const;
+  Crest::TypeId stringToTypeId(const std::string& type) const;
   Crest::TypeId intToTypeId(int value) const;
 
   /**
@@ -148,7 +120,7 @@ class CrestContainer {
    *
    * This method adds a null to the payload. It fills the json object m_row.
    */
-  void addNullRecord(const char *name);
+  void addNullRecord(const char* name);
 
   /**
    * @brief It adds a record to the payload.
@@ -158,7 +130,7 @@ class CrestContainer {
    *
    * This method adds a record to the payload. It fills the json object m_row.
    */
-  void addRecord(const char *name, int number, ...);
+  void addRecord(const char* name, int number, ...);
   /**
    * @brief It associate the payload row to a channel_id.
    * @param channel_id The channel_id to associate the payload row.
@@ -166,7 +138,7 @@ class CrestContainer {
    * The method adds the current payload row to the json object m_payload. The
    * row is associated with the channel_id.
    */
-  void addData(const char *channel_id);
+  void addData(const char* channel_id);
   /**
    * @brief It associate the payload row to a channel_id.
    * @param channel_id The channel_id to associate the payload row.
@@ -176,7 +148,7 @@ class CrestContainer {
    * associated with the channel_id. The data is the data to be associated with
    * the channel_id, and they belong to a previously filled payload row.
    */
-  void addExternalData(const char *channel_id, const nlohmann::json &data);
+  void addExternalData(const char* channel_id, const nlohmann::json& data);
   /**
    * @brief It adds an IOV to the json object m_iov_data.
    * @param since The since value of the IOV.
@@ -186,15 +158,15 @@ class CrestContainer {
    */
   void addIov(const uint64_t since);
 
-  const std::vector<std::pair<std::string, TypeId>> &getMPayloadSpec();
+  const std::vector<std::pair<std::string, TypeId>>& getMPayloadSpec();
 
   template <typename T>
-  T getRecord(const std::string &name) {
-    for (const auto &column : m_payload_spec) {
+  T getRecord(const std::string& name) {
+    for (const auto& column : m_payload_spec) {
       if (column.first == name) {
         try {
           return m_row.at(name).get<T>();
-        } catch (nlohmann::json::exception &e) {
+        } catch (nlohmann::json::exception& e) {
           throw std::runtime_error("JSON exception for key: " + name);
         }
       }
@@ -202,33 +174,33 @@ class CrestContainer {
     throw std::runtime_error("Column name not found or type mismatch.");
   }
 
-  const nlohmann::json &getPayload();
-  const nlohmann::json &getIovData();
+  const nlohmann::json& getPayload();
+  const nlohmann::json& getIovData();
 
-  const nlohmann::json &getPayloadChannel(const char *channel_id);
+  const nlohmann::json getPayloadChannel(const char* channel_id);
 
-  void setIovData(const nlohmann::json &j);
-  void setPayload(const nlohmann::json &j);
+  void setIovData(const nlohmann::json& j);
+  void setPayload(const nlohmann::json& j);
   // return the payload spec as a copy
   nlohmann::json getPayloadSpec();
-  void setPayloadSpec(const nlohmann::json &j);
+  void setPayloadSpec(const nlohmann::json& j);
 
   /**
    * @brief It encodes the input string to base64.
    */
-  static std::string base64Encode(const uint8_t *bytes_to_encode,
-                                  unsigned int in_len);
+  static std::string base64_encode(const uint8_t* bytes_to_encode,
+                                   unsigned int in_len);
   /**
    * @brief It decodes the input string from base64.
    */
-  static std::vector<unsigned char> base64Decode(
-      const std::string &encodedData);
+  static std::vector<unsigned char> base64_decode(
+      const std::string& encodedData);
   /**
    * @brief It returns the index of the column with the given name.
    * @param name The name of the column.
    * It checks the payload spec array to get the index back.
    */
-  int getColumnIndex(const std::string &name);
+  int getColumnIndex(const std::string& name);
   /**
    * @brief It reinitializes the containers.
    */
@@ -244,26 +216,31 @@ class CrestContainer {
    * @brief It returns the json representation of the container.
    */
   std::string getJsonIovData();
-  /**
-   * @brief It returns the StoreSetDto json representation of the container.
-   */
-  nlohmann::json getStoreSetDto();
   /**
    * @brief It creates a file with the json representation of the container.
    */
-  void dumpJsonToFile(const nlohmann::json &j, const std::string &filename);
+  void dumpJsonToFile(const nlohmann::json& j, const std::string& filename);
   /**
    * @brief It reads a json file and returns the json object.
    */
-  nlohmann::json readJsonFromFile(const std::string &filename,
-                                  const std::string &spec_filename);
+  nlohmann::json readJsonFromFile(const std::string& filename,
+                                  const std::string& spec_filename);
   /**
    * @brief It reads a json object to fill the containers.
    */
-  void fromJson(const uint64_t since, const nlohmann::json &j);
+  std::vector<uint64_t> fromJson(uint64_t since, const nlohmann::json& j);
+
+  void parseOldFormat(std::string& colName, TypeId& typespec,
+                      const nlohmann::json& j);
+
+  nlohmann::json getStoreSetDto(uint64_t period = -1);
+
+  uint64_t getFirstTime();
 
-  void parseOldFormat(std::string &colName, TypeId &typespec,
-                      const nlohmann::json &j);
+  void parseData(const nlohmann::json& values);
+  std::vector<uint64_t> readDcsFullType(const nlohmann::json& j_in);
+  void readCommonType(uint64_t since, const nlohmann::json& j_in);
+  static bool compareStrTimestamp(std::string as, std::string bs);
 };
 }  // namespace Crest
 #endif
diff --git a/tools/cmake/CrestContainerConfig.cmake.in b/tools/cmake/CrestContainerConfig.cmake.in
new file mode 100644
index 0000000..9a06914
--- /dev/null
+++ b/tools/cmake/CrestContainerConfig.cmake.in
@@ -0,0 +1,29 @@
+# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
+
+# Avoid hard-coding absolute paths
+# This calculates ${PACKAGE_PREFIX_DIR} at run-time based on the
+# relative locations of the CMAKE_INSTALL_PREFIX and this config file
+# after installation
+@PACKAGE_INIT@
+
+# Set up useful variables.
+set_and_check(CrestContainer_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
+set_and_check(CrestContainer_LIBRARY_DIR "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
+
+# Find dependencies
+include(CMakeFindDependencyMacro)
+find_dependency(CURL)
+find_dependency(OpenSSL)
+find_dependency(nlohmann_json 3.2.0)
+find_dependency(Boost)
+find_dependency(CrestApi)
+
+# Use the cmake generated target
+include("${CMAKE_CURRENT_LIST_DIR}/CrestContainerTargets.cmake")
+set(CrestContainer_LIBRARIES CrestApi::CrestContainerLib)
+
+# Print a standard information message about the package being found.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(CrestContainer
+   REQUIRED_VARS CMAKE_CURRENT_LIST_FILE
+   VERSION_VAR CrestContainer_VERSION )
-- 
GitLab


From 65fe92d6c2da272fc074f570823d4b732d988d4a Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Mon, 7 Apr 2025 10:46:33 +0200
Subject: [PATCH 17/33] remove additional projects from default cmake

---
 CMakeLists.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index cd45973..eaa3a4a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -154,8 +154,8 @@ option(CRESTAPI_BUILD_EXAMPLES
     "Flag for building the examples" TRUE)
 if(CRESTAPI_BUILD_EXAMPLES)
     add_subdirectory(examples)
-    add_subdirectory(acts)
-    add_subdirectory(tools)
+##    add_subdirectory(acts)
+##    add_subdirectory(tools)
 endif()
 
 # Export the project.
-- 
GitLab


From eaeee35ce6464445828c8fd0910106e59daf87af Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Mon, 7 Apr 2025 11:38:25 +0200
Subject: [PATCH 18/33] remove additional projects from default cmake

---
 CMakeLists.txt       | 2 +-
 tools/CMakeLists.txt | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index eaa3a4a..c5a5e1d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -155,7 +155,7 @@ option(CRESTAPI_BUILD_EXAMPLES
 if(CRESTAPI_BUILD_EXAMPLES)
     add_subdirectory(examples)
 ##    add_subdirectory(acts)
-##    add_subdirectory(tools)
+    add_subdirectory(tools)
 endif()
 
 # Export the project.
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 694bb88..560ec53 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -19,7 +19,7 @@ option(BUILD_SHARED_LIBS
 
 # Use the standard GNU installation directories.
 include(GNUInstallDirs)
-set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/CrestResolver")
+set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/CrestContainer")
 
 # Turn on all warnings with GCC and Clang.
 if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
-- 
GitLab


From 5a939019dd5ab5933f824e92f882a4055c8d5356 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Mon, 7 Apr 2025 13:28:40 +0200
Subject: [PATCH 19/33] deal with include dirs

---
 tools/CMakeLists.txt                      | 8 +++++++-
 tools/cmake/CrestContainerConfig.cmake.in | 6 +++++-
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 560ec53..640e66d 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.12)
 project(CrestContainer VERSION 6.1 LANGUAGES CXX)
 
 # Find the necessary externals.
-find_package(CrestApi REQUIRED)
+find_package(CrestApi 6.1 REQUIRED)
 
 
 # Set the C++ standard to use.
@@ -34,6 +34,12 @@ set(HEADERS
 set(SOURCES
     "./CrestContainer.cxx"
 )
+# Use RPATH for build tree
+set(CMAKE_SKIP_BUILD_RPATH FALSE)
+# Don't use RPATH for install tree when building
+set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
+# Add the automatically determined parts of the RPATH
+set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
 
 # Set up the build of the main library of the project.
 add_library(CrestContainerLib ${SOURCES} ${HEADERS})
diff --git a/tools/cmake/CrestContainerConfig.cmake.in b/tools/cmake/CrestContainerConfig.cmake.in
index 9a06914..c1153fe 100644
--- a/tools/cmake/CrestContainerConfig.cmake.in
+++ b/tools/cmake/CrestContainerConfig.cmake.in
@@ -10,6 +10,10 @@
 set_and_check(CrestContainer_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
 set_and_check(CrestContainer_LIBRARY_DIR "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
 
+# Set the include directories that dependent projects will need
+set(CrestContainerLib_INCLUDE_DIRS "${CrestContainer_INCLUDE_DIR}")
+set(CrestContainer_INCLUDE_DIRS "${CrestContainer_INCLUDE_DIR}")  # For backward compatibility
+
 # Find dependencies
 include(CMakeFindDependencyMacro)
 find_dependency(CURL)
@@ -25,5 +29,5 @@ set(CrestContainer_LIBRARIES CrestApi::CrestContainerLib)
 # Print a standard information message about the package being found.
 include(FindPackageHandleStandardArgs)
 find_package_handle_standard_args(CrestContainer
-   REQUIRED_VARS CMAKE_CURRENT_LIST_FILE
+   REQUIRED_VARS CMAKE_CURRENT_LIST_FILE CrestContainer_INCLUDE_DIR
    VERSION_VAR CrestContainer_VERSION )
-- 
GitLab


From 047565482995fc441728294b2a0a18443a8deb13 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Mon, 7 Apr 2025 13:40:36 +0200
Subject: [PATCH 20/33] add channel method

---
 tools/CrestContainer.cxx | 18 ++++++++++++++++++
 tools/CrestContainer.h   |  9 +++++++++
 2 files changed, 27 insertions(+)

diff --git a/tools/CrestContainer.cxx b/tools/CrestContainer.cxx
index a7de28d..56d8c7d 100644
--- a/tools/CrestContainer.cxx
+++ b/tools/CrestContainer.cxx
@@ -727,3 +727,21 @@ nlohmann::json Crest::CrestContainer::getStoreSetDto(uint64_t period) {
 
   return j;
 }
+
+void Crest::CrestContainer::addChannel(const std::string &channelId,
+                                       const std::string &channelName) {
+  // Check if the channel is already in the map
+  if (m_chanMap.find(channelId) == m_chanMap.end()) {
+    if (channelName.c_str() == nullptr) {
+      m_chanMap[channelId] = "";
+    } else {
+      m_chanMap[channelId] = channelName;
+    }
+    std::cout << "CrestContainer: added to m_chanMap " << channelId << " = "
+              << m_chanMap[channelId] << std::endl;
+    return;
+  }
+  std::cout << "CrestContainer: ChannelID " << channelId
+            << " found in map ... don't do anything - size is "
+            << m_chanMap.size() << std::endl;
+}
diff --git a/tools/CrestContainer.h b/tools/CrestContainer.h
index 793b326..5a8cc30 100644
--- a/tools/CrestContainer.h
+++ b/tools/CrestContainer.h
@@ -74,6 +74,10 @@ class CrestContainer {
   nlohmann::json m_iov_data = {};
   nlohmann::json m_vector_data = nlohmann::json::array();
   std::map<uint64_t, nlohmann::json> m_full_data;
+  // Utility map to store the channel_id and the index of the channel
+  std::map<std::string, std::string> m_chanMap;
+  std::map<std::string, std::string> m_chanIdxMap;
+
   ModeId m_modeId;
   bool m_isVectorPayload = false;
 
@@ -122,6 +126,11 @@ class CrestContainer {
    */
   void addNullRecord(const char* name);
 
+  /**
+   * @brief It adds a channel to the channels map.
+   */
+  void addChannel(const std::string& channelId, const std::string& channelName);
+
   /**
    * @brief It adds a record to the payload.
    * @param name The name of the column.
-- 
GitLab


From 272c06a063a5cdbdc006a7193f200bf930aa8ecf Mon Sep 17 00:00:00 2001
From: Mikhail Mineev <Mikhail.Mineev@cern.ch>
Date: Tue, 8 Apr 2025 16:10:41 +0200
Subject: [PATCH 21/33] errors in CrestContainer corrected

---
 tools/CMakeLists.txt     | 4 ++--
 tools/CrestContainer.cxx | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 640e66d..4dcd648 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.12)
 project(CrestContainer VERSION 6.1 LANGUAGES CXX)
 
 # Find the necessary externals.
-find_package(CrestApi 6.1 REQUIRED)
+#find_package(CrestApi 6.1 REQUIRED)
 
 
 # Set the C++ standard to use.
@@ -48,7 +48,7 @@ target_link_libraries(CrestContainerLib
         CURL::libcurl
         Boost::boost
         nlohmann_json::nlohmann_json
-        CrestApi::CrestApiLib
+	CrestApiLib
     PRIVATE
         OpenSSL::SSL
 )
diff --git a/tools/CrestContainer.cxx b/tools/CrestContainer.cxx
index 56d8c7d..2db77c3 100644
--- a/tools/CrestContainer.cxx
+++ b/tools/CrestContainer.cxx
@@ -13,7 +13,7 @@
 #include <fstream>
 #include <iomanip>
 
-#include "CrestApi/CrestCondException.h"
+#include <CrestApi/CrestCondException.h>
 
 using json = nlohmann::json;
 
-- 
GitLab


From 0f4c4d1023d2d8e421662ed63bd602cd490388a3 Mon Sep 17 00:00:00 2001
From: GitLab CI <noreply@cern.ch>
Date: Wed, 9 Apr 2025 08:02:33 +0000
Subject: [PATCH 22/33] Auto-fix: Apply formatting corrections

---
 tools/CrestContainer.cxx | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/tools/CrestContainer.cxx b/tools/CrestContainer.cxx
index 2db77c3..747c29d 100644
--- a/tools/CrestContainer.cxx
+++ b/tools/CrestContainer.cxx
@@ -4,6 +4,7 @@
 
 #include "CrestContainer.h"
 
+#include <CrestApi/CrestCondException.h>
 #include <stdarg.h>
 
 #include <boost/algorithm/string.hpp>
@@ -13,8 +14,6 @@
 #include <fstream>
 #include <iomanip>
 
-#include <CrestApi/CrestCondException.h>
-
 using json = nlohmann::json;
 
 Crest::CrestContainer::CrestContainer(Crest::ModeId mode) : m_modeId(mode) {
-- 
GitLab


From 5eb8da19d1a17bb3d0812dfce7f8207373e6f39a Mon Sep 17 00:00:00 2001
From: Mikhail Mineev <Mikhail.Mineev@cern.ch>
Date: Wed, 7 May 2025 13:09:59 +0200
Subject: [PATCH 23/33] methods for streamer info added

---
 CrestApi/CrestApi.h               |  7 +++++
 CrestApi/CrestApiBase.h           |  7 +++++
 CrestApi/CrestApiFs.h             | 15 +++++++++
 examples/crest_example_fs.cxx     | 29 +++++++++++++++++
 examples/crest_example_server.cxx |  4 ++-
 src/CrestApi.cxx                  | 12 +++++++
 src/CrestApiFs.cxx                | 52 ++++++++++++++++++++++++++++++-
 7 files changed, 124 insertions(+), 2 deletions(-)

diff --git a/CrestApi/CrestApi.h b/CrestApi/CrestApi.h
index 6446578..fd3eb9b 100644
--- a/CrestApi/CrestApi.h
+++ b/CrestApi/CrestApi.h
@@ -532,6 +532,13 @@ class CrestApi : public CrestApiBase {
    */
   PayloadDto getPayloadMeta(const std::string &hash) override;
 
+  /**
+   *  This method finds a streamer info for the hash.
+   * @param hash - hash.
+   * @return streamer info.
+   */
+  std::string getStreamerInfo(const std::string &hash);
+
   /**
    * This method returns the full CREST Server version.
    * @return CREST server version.
diff --git a/CrestApi/CrestApiBase.h b/CrestApi/CrestApiBase.h
index 488980d..935b4aa 100644
--- a/CrestApi/CrestApiBase.h
+++ b/CrestApi/CrestApiBase.h
@@ -468,6 +468,13 @@ class CrestApiBase {
    */
   virtual PayloadDto getPayloadMeta(const std::string &hash) = 0;
 
+  /**
+   *  This method finds a streamer info for the hash.
+   * @param hash - hash.
+   * @return streamer info.
+   */
+  virtual std::string getStreamerInfo(const std::string &hash) = 0;
+
   /**
    * This method returns the full CREST Server version.
    * @return CREST server version.
diff --git a/CrestApi/CrestApiFs.h b/CrestApi/CrestApiFs.h
index 8890596..50c65a7 100644
--- a/CrestApi/CrestApiFs.h
+++ b/CrestApi/CrestApiFs.h
@@ -97,6 +97,14 @@ class CrestApiFs : public CrestApiBase {
    */
   void flush();
 
+   /**
+   * Auxiliary method to store the streamer info on the file storage.
+   * @param hash - the payload hash.
+   * @param streamerInfo - the payload streamer info.
+   *
+   */ 
+  void createStreamerInfo(const std::string &hash, const std::string streamerInfo);
+  
  public:
   /**
    * CrestApiFs constructor.
@@ -502,6 +510,13 @@ class CrestApiFs : public CrestApiBase {
    */
   PayloadDto getPayloadMeta(const std::string &hash) override;
 
+  /**
+   *  This method finds a streamer info for the hash.
+   * @param hash - hash.
+   * @return streamer info.
+   */
+  std::string getStreamerInfo(const std::string &hash);
+
   /**
    * This method returns the full CREST Server version.
    * @return CREST server version.
diff --git a/examples/crest_example_fs.cxx b/examples/crest_example_fs.cxx
index 4a9d906..c37fe8c 100644
--- a/examples/crest_example_fs.cxx
+++ b/examples/crest_example_fs.cxx
@@ -550,6 +550,34 @@ void testGetPayload_FS(const std::string &hash) {
   }
 }
 
+void testGetPayload_FS_2(const std::string &tagname) {
+  std::cout << std::endl << "test: getPayload 2" << std::endl;
+  CrestApiFs myCrestApi = CrestApiFs(true, "/tmp/crest");
+
+  try {
+    IovSetDto dto =
+        myCrestApi.selectIovs(tagname, 0, -1, 0, 1000, 0, "id.since:ASC");
+    std::cout << std::endl
+              << "test: listIovs (result) = " << std::endl
+              << dto.toJson().dump(4) << std::endl;
+    std::vector<IovDto> iovs = dto.getResources();
+    for (auto iov : iovs) {
+      std::cout << "iov.since = " << iov.getSince() << std::endl;
+      std::cout << "iov.payloadHash = " << iov.getPayloadHash() << std::endl;
+      std::string payload = myCrestApi.getPayload(iov.getPayloadHash());
+      std::cout << "payload = " << payload.substr(0, 100) << std::endl;
+      PayloadDto payloadDto = myCrestApi.getPayloadMeta(iov.getPayloadHash());
+      std::cout << "payloadDto = " << payloadDto.toJson().dump(4) << std::endl;
+      std::cout << "      size = " << payloadDto.size() << std::endl;
+      std::string streamer = myCrestApi.getStreamerInfo(iov.getPayloadHash());
+      std::cout << "streamerInfo = " << streamer << std::endl;
+    }
+  } catch (const std::exception &e) {
+    std::cout << std::endl << "test: getPayload 2 (failed)" << std::endl;
+    std::cout << e.what() << std::endl;
+  }
+}
+
 void testGetPayloadMeta_FS(const std::string &hash) {
   std::cout << std::endl << "test: getPayloadMeta FS" << std::endl;
 
@@ -1111,6 +1139,7 @@ int main() {
   testFindTag_FS(tagname);
   testStorePayloads_FS(tagname);
   testGetSize_FS(tagname);
+  testGetPayload_FS_2(tagname);
   */
 
   /*
diff --git a/examples/crest_example_server.cxx b/examples/crest_example_server.cxx
index 0ac1067..f0446b2 100644
--- a/examples/crest_example_server.cxx
+++ b/examples/crest_example_server.cxx
@@ -471,6 +471,8 @@ void testGetPayload(const std::string &tagname) {
       PayloadDto payloadDto = myCrestApi.getPayloadMeta(iov.getPayloadHash());
       std::cout << "payloadDto = " << payloadDto.toJson().dump(4) << std::endl;
       std::cout << "      size = " << payloadDto.size() << std::endl;
+      std::string streamer = myCrestApi.getStreamerInfo(iov.getPayloadHash());
+      std::cout << "streamerInfo = " << streamer << std::endl;
     }
   } catch (const std::exception &e) {
     std::cout << std::endl << "test: listIovs (failed)" << std::endl;
@@ -855,7 +857,7 @@ int main(int argc, char *argv[]) {
     testGetSize(tagname);
     testRemoveTag(tagname);
 
-    testFindExistingTagMeta("SCTDAQConfigChipSlim-HEAD");
+    // testFindExistingTagMeta("SCTDAQConfigChipSlim-HEAD");
 
     // testGetVersion();
 
diff --git a/src/CrestApi.cxx b/src/CrestApi.cxx
index 89460a0..2768c8e 100644
--- a/src/CrestApi.cxx
+++ b/src/CrestApi.cxx
@@ -776,6 +776,18 @@ void CrestApi::checkHash(const std::string &hash, const std::string &str,
   return;
 }
 
+std::string CrestApi::getStreamerInfo(const std::string &hash) {
+  std::string current_path = m_PATH;
+  current_path += urlPaths[UrlPathIndex::PAYLOADS];
+  current_path += urlPaths[UrlPathIndex::DATA];
+  current_path += "?format=STREAMER&hash=";
+  current_path += hash;
+  std::string retv;
+  nlohmann::json js = nullptr;
+  retv = m_request.performRequest(current_path, Action::GET, js);
+  return retv;
+}
+
 RunLumiSetDto CrestApi::listRunInfo(const std::string &since,
                                     const std::string &until,
                                     const std::string format,
diff --git a/src/CrestApiFs.cxx b/src/CrestApiFs.cxx
index 3ff2d3c..85c8640 100644
--- a/src/CrestApiFs.cxx
+++ b/src/CrestApiFs.cxx
@@ -737,7 +737,6 @@ void CrestApiFs::storePayloadDump(const std::string &tag, uint64_t since,
                         {"objectType", objectType},
                         {"version", version},
                         {"size", js.size()},
-                        {"streamerInfo", streamerInfo},
                         {"compressionType", compressionType},
                         {"insertionTime", getDateAndTime()}};
 
@@ -748,6 +747,9 @@ void CrestApiFs::storePayloadDump(const std::string &tag, uint64_t since,
   outFile << jsn.dump();
   outFile.close();
 
+  // streamer info:
+  createStreamerInfo(hashCode,streamerInfo);
+
   // check if data exists
 
   if (m_data.find(tag) == m_data.end()) {
@@ -844,6 +846,54 @@ PayloadDto CrestApiFs::getPayloadMeta(const std::string &hash) {
   return dto;
 }
 
+std::string CrestApiFs::getStreamerInfo(const std::string &hash) {
+  std::string workDir = m_data_folder;
+  workDir += '/';
+  workDir += getFirstLetters(hash);
+  workDir += '/';
+  workDir += hash;
+  std::string filePath = workDir + "/streamer.json";
+  std::string res = "";
+
+  try {
+    if (std::filesystem::exists(filePath)) {
+      res = getFileString(filePath);
+    } else {
+      throw CrestException("streamer info with hash " + hash + " does not exist.");
+    }
+  } catch (const std::exception &e) {
+    std::string message = e.what();
+    throw CrestException(
+        "ERROR in CrestApiFs::getStreamerInfo cannot get the "
+        "streamer info form file storage, " +
+        message);
+  }
+
+  return res;
+}
+ 
+void CrestApiFs::createStreamerInfo(const std::string &hash, const std::string streamerInfo) {
+  std::string workDir = m_data_folder;
+  workDir += '/';
+  workDir += getFirstLetters(hash);
+  workDir += '/';
+  workDir += hash;
+  std::string filePath = workDir + "/streamer.json";
+
+  if (m_isRewrite) {
+    if (std::filesystem::exists(std::filesystem::path(filePath))) {
+      std::filesystem::remove(std::filesystem::path(filePath));
+    }
+
+    std::ofstream outFile;
+
+    outFile.open(filePath.c_str());
+    outFile << streamerInfo;
+    outFile.close();
+  }
+}
+  //
+
 nlohmann::json CrestApiFs::getPage(nlohmann::json data, int size, int page) {
   nlohmann::json js = nlohmann::json::array();
   int dataSize = data.size();
-- 
GitLab


From dc41e02005c744c35767cc910f7e0d26dc58ce8e Mon Sep 17 00:00:00 2001
From: mavogel <mavogel@cern.ch>
Date: Mon, 12 May 2025 15:45:14 +0200
Subject: [PATCH 24/33] Refactored into new CrestAuth class

---
 CMakeLists.txt          |   2 +
 CrestApi/CrestAuth.h    |  46 ++++++++++
 CrestApi/CrestRequest.h |   3 +
 src/CrestAuth.cxx       | 186 ++++++++++++++++++++++++++++++++++++++++
 src/CrestRequest.cxx    |  13 +++
 5 files changed, 250 insertions(+)
 create mode 100644 CrestApi/CrestAuth.h
 create mode 100644 src/CrestAuth.cxx

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c5a5e1d..8473ff0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -44,6 +44,7 @@ set(HEADERS
     "CrestApi/CrestApiFs.h"
     "CrestApi/CrestLogger.h"
     "CrestApi/CrestRequest.h"
+    "CrestApi/CrestAuth.h"
     "CrestApi/CrestApiExt.h"
     "CrestApi/CrestException.h"
     "CrestApi/CrestCondException.h"
@@ -78,6 +79,7 @@ set(HEADERS
 set(SOURCES
     "src/CrestApi.cxx"
     "src/CrestRequest.cxx"
+    "src/CrestAuth.cxx"
     "src/CrestLogger.cxx"
     "src/StringUtils.cxx"
     "src/CrestApiFs.cxx"
diff --git a/CrestApi/CrestAuth.h b/CrestApi/CrestAuth.h
new file mode 100644
index 0000000..ef40c13
--- /dev/null
+++ b/CrestApi/CrestAuth.h
@@ -0,0 +1,46 @@
+// CrestAuth.h
+#ifndef CRESTAPI_AUTH_H
+#define CRESTAPI_AUTH_H
+
+#include <string>
+#include <ctime>
+#include <nlohmann/json.hpp>
+#include <curl/curl.h>
+
+namespace Crest {
+
+class CrestAuth {
+ private:
+  std::string m_tokenFilePath;
+  std::string m_tokenEndpoint;
+  std::string m_clientId;
+  std::string m_audience;
+
+  std::string m_exchangeToken;
+  std::string m_refreshToken;
+  std::string m_finalAccessToken;
+
+  std::time_t m_exchangeTokenExpiresAt;
+  std::time_t m_finalAccessExpiresAt;
+
+  bool saveTokens() const;
+  std::string postRequest(const std::string &url, const std::string &postFields) const;
+
+  static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp);
+
+ public:
+  CrestAuth();
+
+  bool loadTokens();
+  bool refreshExchangeToken();
+  bool exchangeTokenForAccess();
+
+  bool isExchangeTokenExpired() const;
+  bool hasValidAccessToken() const;
+  std::string getAccessToken();
+};
+
+}  // namespace Crest
+
+#endif  // CRESTAPI_AUTH_H
+
diff --git a/CrestApi/CrestRequest.h b/CrestApi/CrestRequest.h
index a6cbd67..2e77d19 100644
--- a/CrestApi/CrestRequest.h
+++ b/CrestApi/CrestRequest.h
@@ -13,6 +13,7 @@
 #include <nlohmann/json.hpp>
 #include <source_location>
 #include <string>
+#include <CrestApi/CrestAuth.h>
 
 namespace Crest {
 
@@ -49,6 +50,8 @@ class CrestRequest {
   std::string m_port;
   std::string m_url;
 
+  CrestAuth m_auth;
+
   char *m_CREST_PROXY = NULL;
   const char *m_CREST_PROXY_VAR = "SOCKS_PROXY";
 
diff --git a/src/CrestAuth.cxx b/src/CrestAuth.cxx
new file mode 100644
index 0000000..abbb90c
--- /dev/null
+++ b/src/CrestAuth.cxx
@@ -0,0 +1,186 @@
+// CrestAuth.cxx
+#include <CrestApi/CrestAuth.h>
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+namespace Crest {
+
+CrestAuth::CrestAuth()
+    : m_tokenFilePath(std::getenv("HOME") + std::string("/.client-exchange-token.json")),
+      m_tokenEndpoint("https://auth.cern.ch/auth/realms/cern/protocol/openid-connect/token"),
+      m_clientId("crest-client"),
+      m_audience("crest-server"),
+      m_exchangeTokenExpiresAt(0),
+      m_finalAccessExpiresAt(0) {}
+
+bool CrestAuth::loadTokens() {
+  std::ifstream file(m_tokenFilePath);
+  if (!file.is_open()) {
+    std::cerr << "[CrestAuth] Failed to open token file: " << m_tokenFilePath << "\n";
+    return false;
+  }
+
+  std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+  if (content.empty()) {
+    std::cerr << "[CrestAuth] Token file is empty\n";
+    return false;
+  }
+
+  file.clear();
+  file.seekg(0);
+
+  nlohmann::json js;
+  try {
+    file >> js;
+  } catch (const std::exception& e) {
+    std::cerr << "[CrestAuth] Failed to parse token JSON: " << e.what() << "\n";
+    return false;
+  }
+
+  m_exchangeToken = js.value("access_token", "");
+  m_refreshToken = js.value("refresh_token", "");
+  int expires_in = js.value("expires_in", 300);
+  m_exchangeTokenExpiresAt = std::time(nullptr) + expires_in;
+
+  std::cerr << "[CrestAuth] Loaded exchange and refresh tokens from file.\n";
+  return true;
+}
+
+bool CrestAuth::saveTokens() const {
+  nlohmann::json js;
+  js["access_token"] = m_exchangeToken;
+  js["refresh_token"] = m_refreshToken;
+  js["expires_in"] = static_cast<int>(m_exchangeTokenExpiresAt - std::time(nullptr));
+
+  std::ofstream file(m_tokenFilePath);
+  if (!file.is_open()) {
+    std::cerr << "[CrestAuth] Failed to open token file for writing: " << m_tokenFilePath << "\n";
+    return false;
+  }
+
+  file << js.dump(2);
+  std::cerr << "[CrestAuth] Saved tokens to file.\n";
+  return true;
+}
+
+bool CrestAuth::isExchangeTokenExpired() const {
+  return std::time(nullptr) >= m_exchangeTokenExpiresAt;
+}
+
+bool CrestAuth::hasValidAccessToken() const {
+  return !m_finalAccessToken.empty() && std::time(nullptr) < m_finalAccessExpiresAt;
+}
+
+std::string CrestAuth::getAccessToken() {
+  if (hasValidAccessToken()) {
+    std::cerr << "[CrestAuth] Returning in-memory access token.\n";
+    return m_finalAccessToken;
+  }
+
+  std::cerr << "[CrestAuth] In-memory access token is missing or expired. Attempting exchange.\n";
+  if (exchangeTokenForAccess()) {
+    return m_finalAccessToken;
+  }
+
+  std::cerr << "[CrestAuth] Failed to obtain access token.\n";
+  return "";
+}
+
+bool CrestAuth::refreshExchangeToken() {
+  std::ostringstream oss;
+  oss << "grant_type=refresh_token"
+      << "&refresh_token=" << m_refreshToken
+      << "&client_id=" << m_clientId;
+
+  std::string response = postRequest(m_tokenEndpoint, oss.str());
+  if (response.empty()) return false;
+
+  auto js = nlohmann::json::parse(response, nullptr, false);
+  if (js.is_discarded() || js.contains("error")) {
+    std::cerr << "[CrestAuth] Failed to refresh exchange token: " << js.dump(2) << "\n";
+    return false;
+  }
+
+  m_exchangeToken = js["access_token"];
+  m_refreshToken = js.value("refresh_token", m_refreshToken);
+  m_exchangeTokenExpiresAt = std::time(nullptr) + js.value("expires_in", 300);
+
+  std::cerr << "[CrestAuth] Successfully refreshed exchange token.\n";
+  return saveTokens();
+}
+
+bool CrestAuth::exchangeTokenForAccess() {
+  CURL* curl = curl_easy_init();
+  if (!curl) {
+    std::cerr << "[CrestAuth] Failed to initialize CURL.\n";
+    return false;
+  }
+
+  if (m_exchangeToken.empty()) {
+    std::cerr << "[CrestAuth] Exchange token is empty.\n";
+    curl_easy_cleanup(curl);
+    return false;
+  }
+
+  std::ostringstream oss;
+  oss << "grant_type=urn:ietf:params:oauth:grant-type:token-exchange"
+      << "&subject_token=" << m_exchangeToken
+      << "&subject_token_type=urn:ietf:params:oauth:token-type:access_token"
+      << "&client_id=" << m_clientId
+      << "&audience=" << m_audience;
+
+  std::string response = postRequest(m_tokenEndpoint, oss.str());
+  curl_easy_cleanup(curl);
+
+  if (response.empty()) return false;
+
+  auto js = nlohmann::json::parse(response, nullptr, false);
+  if (js.is_discarded() || !js.contains("access_token")) {
+    std::cerr << "[CrestAuth] Token exchange failed: " << js.dump(2) << "\n";
+    return false;
+  }
+
+  m_finalAccessToken = js["access_token"];
+  m_finalAccessExpiresAt = std::time(nullptr) + js.value("expires_in", 300);
+
+  std::cerr << "[CrestAuth] Exchanged token successfully. Access token expires in "
+            << (m_finalAccessExpiresAt - std::time(nullptr)) << " seconds.\n";
+  return true;
+}
+
+std::string CrestAuth::postRequest(const std::string &url, const std::string &postFields) const {
+  CURL* curl = curl_easy_init();
+  if (!curl) return "";
+
+  std::string response;
+  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postFields.c_str());
+  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
+
+  struct curl_slist* headers = nullptr;
+  headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
+  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+  curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
+  CURLcode res = curl_easy_perform(curl);
+  if (res != CURLE_OK) {
+    std::cerr << "[CrestAuth] curl_easy_perform failed: " << curl_easy_strerror(res) << "\n";
+  }
+
+  curl_slist_free_all(headers);
+  curl_easy_cleanup(curl);
+  return response;
+}
+
+size_t CrestAuth::WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
+  size_t totalSize = size * nmemb;
+  std::string* out = static_cast<std::string*>(userp);
+  out->append(static_cast<char*>(contents), totalSize);
+  return totalSize;
+}
+
+}  // namespace Crest
+
diff --git a/src/CrestRequest.cxx b/src/CrestRequest.cxx
index 9d8e9ce..e3981c8 100644
--- a/src/CrestRequest.cxx
+++ b/src/CrestRequest.cxx
@@ -42,6 +42,11 @@ std::ostream &operator<<(std::ostream &os, Action action) {
 CrestRequest::CrestRequest() {
   curl_global_init(CURL_GLOBAL_ALL);
   m_CREST_PROXY = std::getenv(m_CREST_PROXY_VAR);
+  if (!m_auth.loadTokens() || m_auth.isExchangeTokenExpired()) {
+    if (!m_auth.refreshExchangeToken()) {
+      throw std::runtime_error("[CrestRequest] Failed to refresh tokens on init.");
+    }
+  }
 }
 
 CrestRequest::~CrestRequest() {
@@ -125,6 +130,14 @@ std::string CrestRequest::performRequest(const std::string &current_path,
   curl = curl_easy_init();
   std::string stt;
   struct curl_slist *headers = NULL;
+  std::string token = m_auth.getAccessToken();
+  if (token.empty()) {
+    throw std::runtime_error("[CrestRequest] Access token is empty.");
+  }
+
+  char authHeader[512];
+  snprintf(authHeader, sizeof(authHeader), "Authorization: Bearer %s", token.c_str());
+  headers = curl_slist_append(headers, authHeader);
   if (curl) {
     std::string url = Crest::StringUtils::appendEndpoint(m_url, current_path);
     std::string s;
-- 
GitLab


From 15c716fc097345cbce68e223f39390aa8df3e759 Mon Sep 17 00:00:00 2001
From: mavogel <mavogel@cern.ch>
Date: Tue, 13 May 2025 22:59:09 +0200
Subject: [PATCH 25/33] Added authorization layer

---
 CMakeLists.txt       |  2 ++
 CrestApi/CrestAuth.h |  6 ++--
 CrestApi/JwtUtils.h  | 19 ++++++++++++
 src/CrestAuth.cxx    | 61 +++++++++++++++------------------------
 src/CrestRequest.cxx | 33 ++++++++++++++++-----
 src/JwtUtils.cxx     | 69 ++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 142 insertions(+), 48 deletions(-)
 create mode 100644 CrestApi/JwtUtils.h
 create mode 100644 src/JwtUtils.cxx

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8473ff0..cde5800 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -45,6 +45,7 @@ set(HEADERS
     "CrestApi/CrestLogger.h"
     "CrestApi/CrestRequest.h"
     "CrestApi/CrestAuth.h"
+    "CrestApi/JwtUtils.h"
     "CrestApi/CrestApiExt.h"
     "CrestApi/CrestException.h"
     "CrestApi/CrestCondException.h"
@@ -80,6 +81,7 @@ set(SOURCES
     "src/CrestApi.cxx"
     "src/CrestRequest.cxx"
     "src/CrestAuth.cxx"
+    "src/JwtUtils.cxx"
     "src/CrestLogger.cxx"
     "src/StringUtils.cxx"
     "src/CrestApiFs.cxx"
diff --git a/CrestApi/CrestAuth.h b/CrestApi/CrestAuth.h
index ef40c13..0b52006 100644
--- a/CrestApi/CrestAuth.h
+++ b/CrestApi/CrestAuth.h
@@ -21,9 +21,8 @@ class CrestAuth {
   std::string m_finalAccessToken;
 
   std::time_t m_exchangeTokenExpiresAt;
-  std::time_t m_finalAccessExpiresAt;
 
-  bool saveTokens() const;
+  bool saveTokens(const nlohmann::json& tokenJson) const;
   std::string postRequest(const std::string &url, const std::string &postFields) const;
 
   static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp);
@@ -36,8 +35,7 @@ class CrestAuth {
   bool exchangeTokenForAccess();
 
   bool isExchangeTokenExpired() const;
-  bool hasValidAccessToken() const;
-  std::string getAccessToken();
+  std::string getAccessToken() const;
 };
 
 }  // namespace Crest
diff --git a/CrestApi/JwtUtils.h b/CrestApi/JwtUtils.h
new file mode 100644
index 0000000..a49ce9e
--- /dev/null
+++ b/CrestApi/JwtUtils.h
@@ -0,0 +1,19 @@
+#ifndef JWT_UTILS_H
+#define JWT_UTILS_H
+
+#include <string>
+#include <ctime>
+
+namespace Crest {
+class JwtUtils {
+public:
+    // Extracts the `exp` (expiration timestamp) from a JWT token
+    static std::time_t extractExp(const std::string& jwt, int graceSeconds = 0);
+
+private:
+    static std::string base64urlDecode(const std::string& input);
+};
+}  // namespace Crest
+
+#endif  // JWT_UTILS_H
+
diff --git a/src/CrestAuth.cxx b/src/CrestAuth.cxx
index abbb90c..804865c 100644
--- a/src/CrestAuth.cxx
+++ b/src/CrestAuth.cxx
@@ -1,6 +1,6 @@
 // CrestAuth.cxx
 #include <CrestApi/CrestAuth.h>
-
+#include <CrestApi/JwtUtils.h>
 #include <fstream>
 #include <iostream>
 #include <sstream>
@@ -12,8 +12,7 @@ CrestAuth::CrestAuth()
       m_tokenEndpoint("https://auth.cern.ch/auth/realms/cern/protocol/openid-connect/token"),
       m_clientId("crest-client"),
       m_audience("crest-server"),
-      m_exchangeTokenExpiresAt(0),
-      m_finalAccessExpiresAt(0) {}
+      m_exchangeTokenExpiresAt(0) {}
 
 bool CrestAuth::loadTokens() {
   std::ifstream file(m_tokenFilePath);
@@ -41,27 +40,26 @@ bool CrestAuth::loadTokens() {
 
   m_exchangeToken = js.value("access_token", "");
   m_refreshToken = js.value("refresh_token", "");
-  int expires_in = js.value("expires_in", 300);
-  m_exchangeTokenExpiresAt = std::time(nullptr) + expires_in;
+  try {
+      m_exchangeTokenExpiresAt = JwtUtils::extractExp(m_exchangeToken, 30);
+  } catch (const std::exception& e) {
+      std::cerr << "[CrestAuth] Failed to extract 'exp' from JWT: " << e.what() << "\n";
+      m_exchangeTokenExpiresAt = std::time(nullptr) + 300;  // fallback
+  }
 
   std::cerr << "[CrestAuth] Loaded exchange and refresh tokens from file.\n";
   return true;
 }
 
-bool CrestAuth::saveTokens() const {
-  nlohmann::json js;
-  js["access_token"] = m_exchangeToken;
-  js["refresh_token"] = m_refreshToken;
-  js["expires_in"] = static_cast<int>(m_exchangeTokenExpiresAt - std::time(nullptr));
-
+bool CrestAuth::saveTokens(const nlohmann::json& tokenJson) const {
   std::ofstream file(m_tokenFilePath);
   if (!file.is_open()) {
     std::cerr << "[CrestAuth] Failed to open token file for writing: " << m_tokenFilePath << "\n";
     return false;
   }
 
-  file << js.dump(2);
-  std::cerr << "[CrestAuth] Saved tokens to file.\n";
+  file << tokenJson.dump();
+  std::cerr << "[CrestAuth] Saved exchanged token to file.\n";
   return true;
 }
 
@@ -69,23 +67,8 @@ bool CrestAuth::isExchangeTokenExpired() const {
   return std::time(nullptr) >= m_exchangeTokenExpiresAt;
 }
 
-bool CrestAuth::hasValidAccessToken() const {
-  return !m_finalAccessToken.empty() && std::time(nullptr) < m_finalAccessExpiresAt;
-}
-
-std::string CrestAuth::getAccessToken() {
-  if (hasValidAccessToken()) {
-    std::cerr << "[CrestAuth] Returning in-memory access token.\n";
-    return m_finalAccessToken;
-  }
-
-  std::cerr << "[CrestAuth] In-memory access token is missing or expired. Attempting exchange.\n";
-  if (exchangeTokenForAccess()) {
-    return m_finalAccessToken;
-  }
-
-  std::cerr << "[CrestAuth] Failed to obtain access token.\n";
-  return "";
+std::string CrestAuth::getAccessToken() const {
+  return m_finalAccessToken;
 }
 
 bool CrestAuth::refreshExchangeToken() {
@@ -98,6 +81,7 @@ bool CrestAuth::refreshExchangeToken() {
   if (response.empty()) return false;
 
   auto js = nlohmann::json::parse(response, nullptr, false);
+  //std::cerr << "[CrestAuth] Response to refresh exchange token: " << js.dump() << "\n";
   if (js.is_discarded() || js.contains("error")) {
     std::cerr << "[CrestAuth] Failed to refresh exchange token: " << js.dump(2) << "\n";
     return false;
@@ -105,10 +89,14 @@ bool CrestAuth::refreshExchangeToken() {
 
   m_exchangeToken = js["access_token"];
   m_refreshToken = js.value("refresh_token", m_refreshToken);
-  m_exchangeTokenExpiresAt = std::time(nullptr) + js.value("expires_in", 300);
-
+  try {
+      m_exchangeTokenExpiresAt = JwtUtils::extractExp(m_exchangeToken, 30);
+  } catch (const std::exception& e) {
+      std::cerr << "[CrestAuth] Failed to extract 'exp' from JWT: " << e.what() << "\n";
+      m_exchangeTokenExpiresAt = std::time(nullptr) + 300;  // fallback
+  }
   std::cerr << "[CrestAuth] Successfully refreshed exchange token.\n";
-  return saveTokens();
+  return saveTokens(js);
 }
 
 bool CrestAuth::exchangeTokenForAccess() {
@@ -143,10 +131,9 @@ bool CrestAuth::exchangeTokenForAccess() {
   }
 
   m_finalAccessToken = js["access_token"];
-  m_finalAccessExpiresAt = std::time(nullptr) + js.value("expires_in", 300);
 
-  std::cerr << "[CrestAuth] Exchanged token successfully. Access token expires in "
-            << (m_finalAccessExpiresAt - std::time(nullptr)) << " seconds.\n";
+  std::cerr << "[CrestAuth] Token exchanged successfully.\n";
+
   return true;
 }
 
@@ -163,7 +150,7 @@ std::string CrestAuth::postRequest(const std::string &url, const std::string &po
   struct curl_slist* headers = nullptr;
   headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
   curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
-  curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+  //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
 
   CURLcode res = curl_easy_perform(curl);
   if (res != CURLE_OK) {
diff --git a/src/CrestRequest.cxx b/src/CrestRequest.cxx
index e3981c8..84d2d6a 100644
--- a/src/CrestRequest.cxx
+++ b/src/CrestRequest.cxx
@@ -42,11 +42,31 @@ std::ostream &operator<<(std::ostream &os, Action action) {
 CrestRequest::CrestRequest() {
   curl_global_init(CURL_GLOBAL_ALL);
   m_CREST_PROXY = std::getenv(m_CREST_PROXY_VAR);
-  if (!m_auth.loadTokens() || m_auth.isExchangeTokenExpired()) {
+
+  std::cerr << "[DEBUG] Setting up authorization...\n";
+  //std::cerr << "[DEBUG] Calling loadTokens()...\n";
+  bool tokensLoaded = m_auth.loadTokens();
+  //std::cerr << "[DEBUG] loadTokens() returned: " << tokensLoaded << "\n";
+
+  if (!tokensLoaded) {
+    throw std::runtime_error(
+        "[CrestRequest] No valid token file found.\n"
+        "Please generate a new exchange token using the authentication utility "
+        "(auth-get-user-token -c crest-client -o  ~/.client-exchange-token.json) and try again.");
+  }
+
+  std::cerr << "[DEBUG] Checking if exchange token is expired...\n";
+  if (m_auth.isExchangeTokenExpired()) {
+    std::cerr << "[DEBUG] Token is expired. Attempting to refresh it...\n";
     if (!m_auth.refreshExchangeToken()) {
-      throw std::runtime_error("[CrestRequest] Failed to refresh tokens on init.");
+      throw std::runtime_error(
+          "[CrestRequest] Exchange token is expired and refresh failed.\n"
+          "Please generate a new token manually.");
     }
   }
+  if (!m_auth.exchangeTokenForAccess()) {
+      throw std::runtime_error("[CrestRequest] Failed to obtain access token from exchange.");
+  }
 }
 
 CrestRequest::~CrestRequest() {
@@ -131,13 +151,12 @@ std::string CrestRequest::performRequest(const std::string &current_path,
   std::string stt;
   struct curl_slist *headers = NULL;
   std::string token = m_auth.getAccessToken();
+
   if (token.empty()) {
     throw std::runtime_error("[CrestRequest] Access token is empty.");
   }
-
-  char authHeader[512];
-  snprintf(authHeader, sizeof(authHeader), "Authorization: Bearer %s", token.c_str());
-  headers = curl_slist_append(headers, authHeader);
+  std::string authHeader = "Authorization: Bearer " + token;
+  headers = curl_slist_append(headers, authHeader.c_str());
   if (curl) {
     std::string url = Crest::StringUtils::appendEndpoint(m_url, current_path);
     std::string s;
@@ -160,7 +179,7 @@ std::string CrestRequest::performRequest(const std::string &current_path,
     // -k analogue:
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
-
+    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
     if (js.is_null()) {
       if (action == Action::DELETE)
         curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
diff --git a/src/JwtUtils.cxx b/src/JwtUtils.cxx
new file mode 100644
index 0000000..3f6b27d
--- /dev/null
+++ b/src/JwtUtils.cxx
@@ -0,0 +1,69 @@
+#include <CrestApi/JwtUtils.h>
+#include <nlohmann/json.hpp>
+#include <stdexcept>
+#include <sstream>
+#include <algorithm>
+
+namespace Crest {
+
+std::string JwtUtils::base64urlDecode(const std::string& input) {
+    std::string base64 = input;
+    std::replace(base64.begin(), base64.end(), '-', '+');
+    std::replace(base64.begin(), base64.end(), '_', '/');
+    while (base64.length() % 4 != 0) base64 += '=';
+
+    static constexpr unsigned char kDecodeTable[256] = {
+        64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+        64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,52,53,54,55,56,57,58,59,60,61,64,64,64, 0,64,64,
+        64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,
+        64,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,64,64,64,64,64,
+        64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+        64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+        64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+        64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64
+    };
+
+    std::string output;
+    size_t i = 0;
+    while (i < base64.length()) {
+        uint32_t val = 0;
+        int valb = -8;
+        for (int j = 0; j < 4 && i < base64.length(); ++j) {
+            unsigned char c = base64[i++];
+            if (kDecodeTable[c] == 64) throw std::runtime_error("Invalid base64url character");
+            val = (val << 6) + kDecodeTable[c];
+            valb += 6;
+        }
+        while (valb >= 0) {
+            output.push_back(static_cast<char>((val >> valb) & 0xFF));
+            valb -= 8;
+        }
+    }
+    return output;
+}
+
+std::time_t JwtUtils::extractExp(const std::string& jwt, int graceSeconds) {
+    size_t dot1 = jwt.find('.');
+    size_t dot2 = jwt.find('.', dot1 + 1);
+    if (dot1 == std::string::npos || dot2 == std::string::npos) {
+        throw std::runtime_error("Invalid JWT format");
+    }
+
+    std::string payloadEncoded = jwt.substr(dot1 + 1, dot2 - dot1 - 1);
+    std::string payloadJson = base64urlDecode(payloadEncoded);
+
+    auto js = nlohmann::json::parse(payloadJson, nullptr, false);
+    if (js.is_discarded()) {
+        throw std::runtime_error("Failed to parse JWT payload");
+    }
+
+    if (!js.contains("exp") || !js["exp"].is_number()) {
+        throw std::runtime_error("JWT payload does not contain valid 'exp'");
+    }
+
+    std::time_t rawExp = js["exp"];
+    return rawExp - graceSeconds;
+}
+
+}  // namespace Crest
+
-- 
GitLab


From a4ade5a9e6df3f80493f0dd3668813a4e8a7b660 Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Wed, 14 May 2025 11:23:33 +0200
Subject: [PATCH 26/33] clean up

---
 CrestApi/CrestApi.h     |   2 +-
 CrestApi/CrestApiFs.h   |  11 +++--
 CrestApi/CrestAuth.h    |  12 +++--
 CrestApi/CrestRequest.h |   2 +-
 CrestApi/JwtUtils.h     |  13 +++--
 src/CrestApiFs.cxx      |  12 +++--
 src/CrestAuth.cxx       |  67 +++++++++++++++----------
 src/CrestRequest.cxx    |  12 +++--
 src/JwtUtils.cxx        | 106 +++++++++++++++++++++-------------------
 9 files changed, 133 insertions(+), 104 deletions(-)

diff --git a/CrestApi/CrestApi.h b/CrestApi/CrestApi.h
index fd3eb9b..4ec654d 100644
--- a/CrestApi/CrestApi.h
+++ b/CrestApi/CrestApi.h
@@ -537,7 +537,7 @@ class CrestApi : public CrestApiBase {
    * @param hash - hash.
    * @return streamer info.
    */
-  std::string getStreamerInfo(const std::string &hash);
+  std::string getStreamerInfo(const std::string &hash) override;
 
   /**
    * This method returns the full CREST Server version.
diff --git a/CrestApi/CrestApiFs.h b/CrestApi/CrestApiFs.h
index 50c65a7..62c7e9c 100644
--- a/CrestApi/CrestApiFs.h
+++ b/CrestApi/CrestApiFs.h
@@ -97,14 +97,15 @@ class CrestApiFs : public CrestApiBase {
    */
   void flush();
 
-   /**
+  /**
    * Auxiliary method to store the streamer info on the file storage.
    * @param hash - the payload hash.
    * @param streamerInfo - the payload streamer info.
    *
-   */ 
-  void createStreamerInfo(const std::string &hash, const std::string streamerInfo);
-  
+   */
+  void createStreamerInfo(const std::string &hash,
+                          const std::string streamerInfo);
+
  public:
   /**
    * CrestApiFs constructor.
@@ -515,7 +516,7 @@ class CrestApiFs : public CrestApiBase {
    * @param hash - hash.
    * @return streamer info.
    */
-  std::string getStreamerInfo(const std::string &hash);
+  std::string getStreamerInfo(const std::string &hash) override;
 
   /**
    * This method returns the full CREST Server version.
diff --git a/CrestApi/CrestAuth.h b/CrestApi/CrestAuth.h
index 0b52006..b03c3c6 100644
--- a/CrestApi/CrestAuth.h
+++ b/CrestApi/CrestAuth.h
@@ -2,10 +2,11 @@
 #ifndef CRESTAPI_AUTH_H
 #define CRESTAPI_AUTH_H
 
-#include <string>
+#include <curl/curl.h>
+
 #include <ctime>
 #include <nlohmann/json.hpp>
-#include <curl/curl.h>
+#include <string>
 
 namespace Crest {
 
@@ -23,9 +24,11 @@ class CrestAuth {
   std::time_t m_exchangeTokenExpiresAt;
 
   bool saveTokens(const nlohmann::json& tokenJson) const;
-  std::string postRequest(const std::string &url, const std::string &postFields) const;
+  std::string postRequest(const std::string& url,
+                          const std::string& postFields) const;
 
-  static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp);
+  static size_t WriteCallback(void* contents, size_t size, size_t nmemb,
+                              void* userp);
 
  public:
   CrestAuth();
@@ -41,4 +44,3 @@ class CrestAuth {
 }  // namespace Crest
 
 #endif  // CRESTAPI_AUTH_H
-
diff --git a/CrestApi/CrestRequest.h b/CrestApi/CrestRequest.h
index 2e77d19..e2c32c3 100644
--- a/CrestApi/CrestRequest.h
+++ b/CrestApi/CrestRequest.h
@@ -5,6 +5,7 @@
 #ifndef CRESTAPI_REQUEST_H
 #define CRESTAPI_REQUEST_H
 
+#include <CrestApi/CrestAuth.h>
 #include <curl/curl.h>
 
 #include <cstdint>
@@ -13,7 +14,6 @@
 #include <nlohmann/json.hpp>
 #include <source_location>
 #include <string>
-#include <CrestApi/CrestAuth.h>
 
 namespace Crest {
 
diff --git a/CrestApi/JwtUtils.h b/CrestApi/JwtUtils.h
index a49ce9e..ccd2d5d 100644
--- a/CrestApi/JwtUtils.h
+++ b/CrestApi/JwtUtils.h
@@ -1,19 +1,18 @@
 #ifndef JWT_UTILS_H
 #define JWT_UTILS_H
 
-#include <string>
 #include <ctime>
+#include <string>
 
 namespace Crest {
 class JwtUtils {
-public:
-    // Extracts the `exp` (expiration timestamp) from a JWT token
-    static std::time_t extractExp(const std::string& jwt, int graceSeconds = 0);
+ public:
+  // Extracts the `exp` (expiration timestamp) from a JWT token
+  static std::time_t extractExp(const std::string& jwt, int graceSeconds = 0);
 
-private:
-    static std::string base64urlDecode(const std::string& input);
+ private:
+  static std::string base64urlDecode(const std::string& input);
 };
 }  // namespace Crest
 
 #endif  // JWT_UTILS_H
-
diff --git a/src/CrestApiFs.cxx b/src/CrestApiFs.cxx
index 85c8640..ba0922c 100644
--- a/src/CrestApiFs.cxx
+++ b/src/CrestApiFs.cxx
@@ -748,7 +748,7 @@ void CrestApiFs::storePayloadDump(const std::string &tag, uint64_t since,
   outFile.close();
 
   // streamer info:
-  createStreamerInfo(hashCode,streamerInfo);
+  createStreamerInfo(hashCode, streamerInfo);
 
   // check if data exists
 
@@ -859,7 +859,8 @@ std::string CrestApiFs::getStreamerInfo(const std::string &hash) {
     if (std::filesystem::exists(filePath)) {
       res = getFileString(filePath);
     } else {
-      throw CrestException("streamer info with hash " + hash + " does not exist.");
+      throw CrestException("streamer info with hash " + hash +
+                           " does not exist.");
     }
   } catch (const std::exception &e) {
     std::string message = e.what();
@@ -871,8 +872,9 @@ std::string CrestApiFs::getStreamerInfo(const std::string &hash) {
 
   return res;
 }
- 
-void CrestApiFs::createStreamerInfo(const std::string &hash, const std::string streamerInfo) {
+
+void CrestApiFs::createStreamerInfo(const std::string &hash,
+                                    const std::string streamerInfo) {
   std::string workDir = m_data_folder;
   workDir += '/';
   workDir += getFirstLetters(hash);
@@ -892,7 +894,7 @@ void CrestApiFs::createStreamerInfo(const std::string &hash, const std::string s
     outFile.close();
   }
 }
-  //
+//
 
 nlohmann::json CrestApiFs::getPage(nlohmann::json data, int size, int page) {
   nlohmann::json js = nlohmann::json::array();
diff --git a/src/CrestAuth.cxx b/src/CrestAuth.cxx
index 804865c..4cbb60d 100644
--- a/src/CrestAuth.cxx
+++ b/src/CrestAuth.cxx
@@ -1,6 +1,7 @@
 // CrestAuth.cxx
 #include <CrestApi/CrestAuth.h>
 #include <CrestApi/JwtUtils.h>
+
 #include <fstream>
 #include <iostream>
 #include <sstream>
@@ -8,8 +9,11 @@
 namespace Crest {
 
 CrestAuth::CrestAuth()
-    : m_tokenFilePath(std::getenv("HOME") + std::string("/.client-exchange-token.json")),
-      m_tokenEndpoint("https://auth.cern.ch/auth/realms/cern/protocol/openid-connect/token"),
+    : m_tokenFilePath(std::getenv("HOME") +
+                      std::string("/.client-exchange-token.json")),
+      m_tokenEndpoint(
+          "https://auth.cern.ch/auth/realms/cern/protocol/openid-connect/"
+          "token"),
       m_clientId("crest-client"),
       m_audience("crest-server"),
       m_exchangeTokenExpiresAt(0) {}
@@ -17,11 +21,13 @@ CrestAuth::CrestAuth()
 bool CrestAuth::loadTokens() {
   std::ifstream file(m_tokenFilePath);
   if (!file.is_open()) {
-    std::cerr << "[CrestAuth] Failed to open token file: " << m_tokenFilePath << "\n";
+    std::cerr << "[CrestAuth] Failed to open token file: " << m_tokenFilePath
+              << "\n";
     return false;
   }
 
-  std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+  std::string content((std::istreambuf_iterator<char>(file)),
+                      std::istreambuf_iterator<char>());
   if (content.empty()) {
     std::cerr << "[CrestAuth] Token file is empty\n";
     return false;
@@ -41,10 +47,11 @@ bool CrestAuth::loadTokens() {
   m_exchangeToken = js.value("access_token", "");
   m_refreshToken = js.value("refresh_token", "");
   try {
-      m_exchangeTokenExpiresAt = JwtUtils::extractExp(m_exchangeToken, 30);
+    m_exchangeTokenExpiresAt = JwtUtils::extractExp(m_exchangeToken, 30);
   } catch (const std::exception& e) {
-      std::cerr << "[CrestAuth] Failed to extract 'exp' from JWT: " << e.what() << "\n";
-      m_exchangeTokenExpiresAt = std::time(nullptr) + 300;  // fallback
+    std::cerr << "[CrestAuth] Failed to extract 'exp' from JWT: " << e.what()
+              << "\n";
+    m_exchangeTokenExpiresAt = std::time(nullptr) + 300;  // fallback
   }
 
   std::cerr << "[CrestAuth] Loaded exchange and refresh tokens from file.\n";
@@ -54,7 +61,8 @@ bool CrestAuth::loadTokens() {
 bool CrestAuth::saveTokens(const nlohmann::json& tokenJson) const {
   std::ofstream file(m_tokenFilePath);
   if (!file.is_open()) {
-    std::cerr << "[CrestAuth] Failed to open token file for writing: " << m_tokenFilePath << "\n";
+    std::cerr << "[CrestAuth] Failed to open token file for writing: "
+              << m_tokenFilePath << "\n";
     return false;
   }
 
@@ -74,26 +82,29 @@ std::string CrestAuth::getAccessToken() const {
 bool CrestAuth::refreshExchangeToken() {
   std::ostringstream oss;
   oss << "grant_type=refresh_token"
-      << "&refresh_token=" << m_refreshToken
-      << "&client_id=" << m_clientId;
+      << "&refresh_token=" << m_refreshToken << "&client_id=" << m_clientId;
 
   std::string response = postRequest(m_tokenEndpoint, oss.str());
-  if (response.empty()) return false;
+  if (response.empty())
+    return false;
 
   auto js = nlohmann::json::parse(response, nullptr, false);
-  //std::cerr << "[CrestAuth] Response to refresh exchange token: " << js.dump() << "\n";
+  // std::cerr << "[CrestAuth] Response to refresh exchange token: " <<
+  // js.dump() << "\n";
   if (js.is_discarded() || js.contains("error")) {
-    std::cerr << "[CrestAuth] Failed to refresh exchange token: " << js.dump(2) << "\n";
+    std::cerr << "[CrestAuth] Failed to refresh exchange token: " << js.dump(2)
+              << "\n";
     return false;
   }
 
   m_exchangeToken = js["access_token"];
   m_refreshToken = js.value("refresh_token", m_refreshToken);
   try {
-      m_exchangeTokenExpiresAt = JwtUtils::extractExp(m_exchangeToken, 30);
+    m_exchangeTokenExpiresAt = JwtUtils::extractExp(m_exchangeToken, 30);
   } catch (const std::exception& e) {
-      std::cerr << "[CrestAuth] Failed to extract 'exp' from JWT: " << e.what() << "\n";
-      m_exchangeTokenExpiresAt = std::time(nullptr) + 300;  // fallback
+    std::cerr << "[CrestAuth] Failed to extract 'exp' from JWT: " << e.what()
+              << "\n";
+    m_exchangeTokenExpiresAt = std::time(nullptr) + 300;  // fallback
   }
   std::cerr << "[CrestAuth] Successfully refreshed exchange token.\n";
   return saveTokens(js);
@@ -116,13 +127,13 @@ bool CrestAuth::exchangeTokenForAccess() {
   oss << "grant_type=urn:ietf:params:oauth:grant-type:token-exchange"
       << "&subject_token=" << m_exchangeToken
       << "&subject_token_type=urn:ietf:params:oauth:token-type:access_token"
-      << "&client_id=" << m_clientId
-      << "&audience=" << m_audience;
+      << "&client_id=" << m_clientId << "&audience=" << m_audience;
 
   std::string response = postRequest(m_tokenEndpoint, oss.str());
   curl_easy_cleanup(curl);
 
-  if (response.empty()) return false;
+  if (response.empty())
+    return false;
 
   auto js = nlohmann::json::parse(response, nullptr, false);
   if (js.is_discarded() || !js.contains("access_token")) {
@@ -137,9 +148,11 @@ bool CrestAuth::exchangeTokenForAccess() {
   return true;
 }
 
-std::string CrestAuth::postRequest(const std::string &url, const std::string &postFields) const {
+std::string CrestAuth::postRequest(const std::string& url,
+                                   const std::string& postFields) const {
   CURL* curl = curl_easy_init();
-  if (!curl) return "";
+  if (!curl)
+    return "";
 
   std::string response;
   curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
@@ -148,13 +161,15 @@ std::string CrestAuth::postRequest(const std::string &url, const std::string &po
   curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
 
   struct curl_slist* headers = nullptr;
-  headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
+  headers = curl_slist_append(
+      headers, "Content-Type: application/x-www-form-urlencoded");
   curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
-  //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+  // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
 
   CURLcode res = curl_easy_perform(curl);
   if (res != CURLE_OK) {
-    std::cerr << "[CrestAuth] curl_easy_perform failed: " << curl_easy_strerror(res) << "\n";
+    std::cerr << "[CrestAuth] curl_easy_perform failed: "
+              << curl_easy_strerror(res) << "\n";
   }
 
   curl_slist_free_all(headers);
@@ -162,7 +177,8 @@ std::string CrestAuth::postRequest(const std::string &url, const std::string &po
   return response;
 }
 
-size_t CrestAuth::WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
+size_t CrestAuth::WriteCallback(void* contents, size_t size, size_t nmemb,
+                                void* userp) {
   size_t totalSize = size * nmemb;
   std::string* out = static_cast<std::string*>(userp);
   out->append(static_cast<char*>(contents), totalSize);
@@ -170,4 +186,3 @@ size_t CrestAuth::WriteCallback(void* contents, size_t size, size_t nmemb, void*
 }
 
 }  // namespace Crest
-
diff --git a/src/CrestRequest.cxx b/src/CrestRequest.cxx
index 84d2d6a..369e0a9 100644
--- a/src/CrestRequest.cxx
+++ b/src/CrestRequest.cxx
@@ -44,15 +44,16 @@ CrestRequest::CrestRequest() {
   m_CREST_PROXY = std::getenv(m_CREST_PROXY_VAR);
 
   std::cerr << "[DEBUG] Setting up authorization...\n";
-  //std::cerr << "[DEBUG] Calling loadTokens()...\n";
+  // std::cerr << "[DEBUG] Calling loadTokens()...\n";
   bool tokensLoaded = m_auth.loadTokens();
-  //std::cerr << "[DEBUG] loadTokens() returned: " << tokensLoaded << "\n";
+  // std::cerr << "[DEBUG] loadTokens() returned: " << tokensLoaded << "\n";
 
   if (!tokensLoaded) {
     throw std::runtime_error(
         "[CrestRequest] No valid token file found.\n"
         "Please generate a new exchange token using the authentication utility "
-        "(auth-get-user-token -c crest-client -o  ~/.client-exchange-token.json) and try again.");
+        "(auth-get-user-token -c crest-client -o  "
+        "~/.client-exchange-token.json) and try again.");
   }
 
   std::cerr << "[DEBUG] Checking if exchange token is expired...\n";
@@ -65,7 +66,8 @@ CrestRequest::CrestRequest() {
     }
   }
   if (!m_auth.exchangeTokenForAccess()) {
-      throw std::runtime_error("[CrestRequest] Failed to obtain access token from exchange.");
+    throw std::runtime_error(
+        "[CrestRequest] Failed to obtain access token from exchange.");
   }
 }
 
@@ -179,7 +181,7 @@ std::string CrestRequest::performRequest(const std::string &current_path,
     // -k analogue:
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
-    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
     if (js.is_null()) {
       if (action == Action::DELETE)
         curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
diff --git a/src/JwtUtils.cxx b/src/JwtUtils.cxx
index 3f6b27d..f8e2f05 100644
--- a/src/JwtUtils.cxx
+++ b/src/JwtUtils.cxx
@@ -1,69 +1,77 @@
 #include <CrestApi/JwtUtils.h>
+
+#include <algorithm>
 #include <nlohmann/json.hpp>
-#include <stdexcept>
 #include <sstream>
-#include <algorithm>
+#include <stdexcept>
 
 namespace Crest {
 
 std::string JwtUtils::base64urlDecode(const std::string& input) {
-    std::string base64 = input;
-    std::replace(base64.begin(), base64.end(), '-', '+');
-    std::replace(base64.begin(), base64.end(), '_', '/');
-    while (base64.length() % 4 != 0) base64 += '=';
+  std::string base64 = input;
+  std::replace(base64.begin(), base64.end(), '-', '+');
+  std::replace(base64.begin(), base64.end(), '_', '/');
+  while (base64.length() % 4 != 0)
+    base64 += '=';
 
-    static constexpr unsigned char kDecodeTable[256] = {
-        64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
-        64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,52,53,54,55,56,57,58,59,60,61,64,64,64, 0,64,64,
-        64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,
-        64,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,64,64,64,64,64,
-        64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
-        64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
-        64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
-        64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64
-    };
+  static constexpr unsigned char kDecodeTable[256] = {
+      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+      64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57,
+      58, 59, 60, 61, 64, 64, 64, 0,  64, 64, 64, 0,  1,  2,  3,  4,  5,  6,
+      7,  8,  9,  10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+      25, 64, 64, 64, 64, 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+      37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64,
+      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+      64, 64, 64, 64};
 
-    std::string output;
-    size_t i = 0;
-    while (i < base64.length()) {
-        uint32_t val = 0;
-        int valb = -8;
-        for (int j = 0; j < 4 && i < base64.length(); ++j) {
-            unsigned char c = base64[i++];
-            if (kDecodeTable[c] == 64) throw std::runtime_error("Invalid base64url character");
-            val = (val << 6) + kDecodeTable[c];
-            valb += 6;
-        }
-        while (valb >= 0) {
-            output.push_back(static_cast<char>((val >> valb) & 0xFF));
-            valb -= 8;
-        }
+  std::string output;
+  size_t i = 0;
+  while (i < base64.length()) {
+    uint32_t val = 0;
+    int valb = -8;
+    for (int j = 0; j < 4 && i < base64.length(); ++j) {
+      unsigned char c = base64[i++];
+      if (kDecodeTable[c] == 64)
+        throw std::runtime_error("Invalid base64url character");
+      val = (val << 6) + kDecodeTable[c];
+      valb += 6;
     }
-    return output;
+    while (valb >= 0) {
+      output.push_back(static_cast<char>((val >> valb) & 0xFF));
+      valb -= 8;
+    }
+  }
+  return output;
 }
 
 std::time_t JwtUtils::extractExp(const std::string& jwt, int graceSeconds) {
-    size_t dot1 = jwt.find('.');
-    size_t dot2 = jwt.find('.', dot1 + 1);
-    if (dot1 == std::string::npos || dot2 == std::string::npos) {
-        throw std::runtime_error("Invalid JWT format");
-    }
+  size_t dot1 = jwt.find('.');
+  size_t dot2 = jwt.find('.', dot1 + 1);
+  if (dot1 == std::string::npos || dot2 == std::string::npos) {
+    throw std::runtime_error("Invalid JWT format");
+  }
 
-    std::string payloadEncoded = jwt.substr(dot1 + 1, dot2 - dot1 - 1);
-    std::string payloadJson = base64urlDecode(payloadEncoded);
+  std::string payloadEncoded = jwt.substr(dot1 + 1, dot2 - dot1 - 1);
+  std::string payloadJson = base64urlDecode(payloadEncoded);
 
-    auto js = nlohmann::json::parse(payloadJson, nullptr, false);
-    if (js.is_discarded()) {
-        throw std::runtime_error("Failed to parse JWT payload");
-    }
+  auto js = nlohmann::json::parse(payloadJson, nullptr, false);
+  if (js.is_discarded()) {
+    throw std::runtime_error("Failed to parse JWT payload");
+  }
 
-    if (!js.contains("exp") || !js["exp"].is_number()) {
-        throw std::runtime_error("JWT payload does not contain valid 'exp'");
-    }
+  if (!js.contains("exp") || !js["exp"].is_number()) {
+    throw std::runtime_error("JWT payload does not contain valid 'exp'");
+  }
 
-    std::time_t rawExp = js["exp"];
-    return rawExp - graceSeconds;
+  std::time_t rawExp = js["exp"];
+  return rawExp - graceSeconds;
 }
 
 }  // namespace Crest
-
-- 
GitLab


From 2eeac3424e2890a777001bd2024c830f3ab88792 Mon Sep 17 00:00:00 2001
From: mavogel <mavogel@cern.ch>
Date: Wed, 14 May 2025 19:36:23 +0200
Subject: [PATCH 27/33] Client authorization via env: CREST_AUTH_MODE

---
 CrestApi/CrestAuth.h    |  2 ++
 CrestApi/CrestRequest.h |  3 +++
 src/CrestApi.cxx        |  2 ++
 src/CrestAuth.cxx       | 28 ++++++++++++++++++++++
 src/CrestRequest.cxx    | 53 ++++++++++++++++++-----------------------
 5 files changed, 58 insertions(+), 30 deletions(-)

diff --git a/CrestApi/CrestAuth.h b/CrestApi/CrestAuth.h
index 0b52006..dac693c 100644
--- a/CrestApi/CrestAuth.h
+++ b/CrestApi/CrestAuth.h
@@ -36,6 +36,8 @@ class CrestAuth {
 
   bool isExchangeTokenExpired() const;
   std::string getAccessToken() const;
+
+  bool setupExchangeCredentials();
 };
 
 }  // namespace Crest
diff --git a/CrestApi/CrestRequest.h b/CrestApi/CrestRequest.h
index 2e77d19..d38d7f6 100644
--- a/CrestApi/CrestRequest.h
+++ b/CrestApi/CrestRequest.h
@@ -82,6 +82,9 @@ class CrestRequest {
   // Getters
   std::string getUrl();
 
+  // Setup authentication
+  void initAuth(const char* mode);
+
   /**
    * General auxillary method to make request to the CREST Server. This method
    * is used by other methods realizing the requests with the concrete kinds of
diff --git a/src/CrestApi.cxx b/src/CrestApi.cxx
index 2768c8e..b403d6e 100644
--- a/src/CrestApi.cxx
+++ b/src/CrestApi.cxx
@@ -36,6 +36,7 @@ CrestApi::CrestApi(const std::string &host, const std::string &port,
   m_request.setHost(host);
   m_request.setPort(port);
   m_request.setPrefix(protocol);
+  m_request.initAuth(std::getenv("CREST_AUTH_MODE"));
 }
 
 /**
@@ -60,6 +61,7 @@ CrestApi::CrestApi(std::string_view url, std::string_view apipath,
   m_request.setHost(parsedUrl.host.data());
   m_request.setPort(std::to_string(*parsedUrl.port));
   m_request.setPrefix(parsedUrl.protocol.data());
+  m_request.initAuth(std::getenv("CREST_AUTH_MODE"));
 
   if (parsedUrl.apipath == "") {
     m_PATH = urlPaths[UrlPathIndex::CREST_ROOT];
diff --git a/src/CrestAuth.cxx b/src/CrestAuth.cxx
index 804865c..ee54875 100644
--- a/src/CrestAuth.cxx
+++ b/src/CrestAuth.cxx
@@ -169,5 +169,33 @@ size_t CrestAuth::WriteCallback(void* contents, size_t size, size_t nmemb, void*
   return totalSize;
 }
 
+bool CrestAuth::setupExchangeCredentials() {
+  std::cerr << "[CrestAuth] Setting up authorization - requesting access token...\n";
+
+  bool tokensLoaded = loadTokens();
+  if (!tokensLoaded) {
+    throw std::runtime_error(
+      "[CrestAuth] No valid token file found.\n"
+      "Please generate a new exchange token using the authentication utility:\n"
+      "auth-get-user-token -c crest-client -o ~/.client-exchange-token.json");
+  }
+
+  std::cerr << "[CrestAuth] Checking if exchange token is expired...\n";
+  if (isExchangeTokenExpired()) {
+    std::cerr << "[CrestAuth] Exchange token is expired. Attempting refresh...\n";
+    if (!refreshExchangeToken()) {
+      throw std::runtime_error(
+        "[CrestAuth] Exchange token is expired and refresh failed.\n"
+        "Please generate a new token manually.");
+    }
+  }
+
+  if (!exchangeTokenForAccess()) {
+    throw std::runtime_error("[CrestAuth] Failed to obtain access token from exchange.");
+  }
+
+  std::cerr << "[CrestAuth] Setup complete — access token obtained.\n";
+  return true;
+}
 }  // namespace Crest
 
diff --git a/src/CrestRequest.cxx b/src/CrestRequest.cxx
index 84d2d6a..aa37b06 100644
--- a/src/CrestRequest.cxx
+++ b/src/CrestRequest.cxx
@@ -42,31 +42,6 @@ std::ostream &operator<<(std::ostream &os, Action action) {
 CrestRequest::CrestRequest() {
   curl_global_init(CURL_GLOBAL_ALL);
   m_CREST_PROXY = std::getenv(m_CREST_PROXY_VAR);
-
-  std::cerr << "[DEBUG] Setting up authorization...\n";
-  //std::cerr << "[DEBUG] Calling loadTokens()...\n";
-  bool tokensLoaded = m_auth.loadTokens();
-  //std::cerr << "[DEBUG] loadTokens() returned: " << tokensLoaded << "\n";
-
-  if (!tokensLoaded) {
-    throw std::runtime_error(
-        "[CrestRequest] No valid token file found.\n"
-        "Please generate a new exchange token using the authentication utility "
-        "(auth-get-user-token -c crest-client -o  ~/.client-exchange-token.json) and try again.");
-  }
-
-  std::cerr << "[DEBUG] Checking if exchange token is expired...\n";
-  if (m_auth.isExchangeTokenExpired()) {
-    std::cerr << "[DEBUG] Token is expired. Attempting to refresh it...\n";
-    if (!m_auth.refreshExchangeToken()) {
-      throw std::runtime_error(
-          "[CrestRequest] Exchange token is expired and refresh failed.\n"
-          "Please generate a new token manually.");
-    }
-  }
-  if (!m_auth.exchangeTokenForAccess()) {
-      throw std::runtime_error("[CrestRequest] Failed to obtain access token from exchange.");
-  }
 }
 
 CrestRequest::~CrestRequest() {
@@ -102,6 +77,26 @@ std::string CrestRequest::getUrl() {
   return m_url;
 }
 
+void CrestRequest::initAuth(const char* mode) {
+  if (!mode) {
+      std::cerr << "[CrestRequest] CREST_AUTH_MODE is unset — skipping client authorization.\n";
+      return;
+  }
+
+  std::string modeStr = mode;
+  std::cerr << "[CrestRequest] CREST_AUTH_MODE is set to: " << modeStr << "\n";
+
+  if (modeStr == "JWT") {
+      std::cerr << "[CrestRequest] Authorizing via JWT.\n";
+      m_auth.setupExchangeCredentials();
+  } else if (modeStr == "CLIENT_CREDENTIALS") {
+      std::cerr << "[CrestRequest] Authorizing via client id and secret.\n";
+      // m_auth.setupClientCredentials();
+  } else {
+      std::cerr << "[CrestRequest] Valid values of CREST_AUTH_MODE: JWT, CLIENT_CREDENTIALS — skipping client authorization.\n";
+  }
+}
+
 size_t WriteCallback(void *contents, size_t size, size_t nmemb,
                      std::vector<char> *output) {
   size_t total_size = size * nmemb;
@@ -151,12 +146,10 @@ std::string CrestRequest::performRequest(const std::string &current_path,
   std::string stt;
   struct curl_slist *headers = NULL;
   std::string token = m_auth.getAccessToken();
-
-  if (token.empty()) {
-    throw std::runtime_error("[CrestRequest] Access token is empty.");
+  if (!token.empty()) {
+    std::string authHeader = "Authorization: Bearer " + token;
+    headers = curl_slist_append(headers, authHeader.c_str());
   }
-  std::string authHeader = "Authorization: Bearer " + token;
-  headers = curl_slist_append(headers, authHeader.c_str());
   if (curl) {
     std::string url = Crest::StringUtils::appendEndpoint(m_url, current_path);
     std::string s;
-- 
GitLab


From e7d26a964ccec016d8bf4b9a8e6d917e30280dc2 Mon Sep 17 00:00:00 2001
From: mavogel <mavogel@cern.ch>
Date: Wed, 14 May 2025 23:25:41 +0200
Subject: [PATCH 28/33] Added documentation and stardard logging

---
 CrestApi/CrestAuth.h    | 63 +++++++++++++++++++++++++++++++++++++++++
 CrestApi/CrestRequest.h | 17 ++++++++++-
 src/CrestAuth.cxx       | 55 ++++++++++++++++++-----------------
 src/CrestRequest.cxx    | 10 +++----
 4 files changed, 111 insertions(+), 34 deletions(-)

diff --git a/CrestApi/CrestAuth.h b/CrestApi/CrestAuth.h
index dac693c..b1140c6 100644
--- a/CrestApi/CrestAuth.h
+++ b/CrestApi/CrestAuth.h
@@ -9,6 +9,10 @@
 
 namespace Crest {
 
+/**
+ * Class handling authentication for CREST requests.
+ * It loads, refreshes, and exchanges tokens used to authorize access.
+ */
 class CrestAuth {
  private:
   std::string m_tokenFilePath;
@@ -22,21 +26,80 @@ class CrestAuth {
 
   std::time_t m_exchangeTokenExpiresAt;
 
+  /**
+   * Save the full token JSON response to the configured token file.
+   * @param tokenJson Full JSON response from the authentication service.
+   * @return true if saving was successful, false otherwise.
+   */
   bool saveTokens(const nlohmann::json& tokenJson) const;
+
+
+  /**
+   * Sends a form-encoded POST request using libcurl to the given URL.
+   * @param url Target endpoint URL.
+   * @param postFields POST body formatted as application/x-www-form-urlencoded.
+   * @return The response body as a string, or empty string on failure.
+   */
   std::string postRequest(const std::string &url, const std::string &postFields) const;
 
+  /**
+   * libcurl write callback to accumulate received data into a string.
+   * @param contents Raw data pointer.
+   * @param size Size of each data chunk.
+   * @param nmemb Number of data chunks.
+   * @param userp Pointer to std::string used to collect output.
+   * @return Total bytes handled (size * nmemb).
+   */
   static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp);
 
  public:
+  /**
+   * Default constructor. Initializes internal variables from default
+   * environment-based settings or hardcoded defaults.
+   */
   CrestAuth();
 
+  /**
+   * Loads the exchange and refresh tokens from a file on disk.
+   * Sets internal state based on the saved token data.
+   * @return true if loading and parsing succeeded; false if file missing or malformed.
+   */
   bool loadTokens();
+
+  /**
+   * Uses the current refresh token to request a new exchange token from the authentication service.
+   * Stores the entire response to the token file and updates internal state.
+   * @return true if the refresh was successful, false otherwise.
+   */
   bool refreshExchangeToken();
+
+  /**
+   * Exchanges the current exchange token for a final access token used for authorization.
+   * This does not persist the access token to file.
+   * @return true if access token was successfully acquired, false otherwise.
+   */
   bool exchangeTokenForAccess();
 
+  /**
+   * Checks whether the current exchange token is expired based on the 'exp' claim.
+   * @return true if the exchange token is expired or invalid.
+   */
   bool isExchangeTokenExpired() const;
+
+  /**
+   * Returns the currently cached access token. This method does not initiate a new exchange.
+   * @return Access token string if previously acquired; otherwise an empty string.
+   */
   std::string getAccessToken() const;
 
+
+  /**
+   * Initializes exchange credentials:
+   * - Loads exchange and refresh tokens from file
+   * - Refreshes the exchange token if expired
+   * - Performs token exchange to obtain an access token
+   * @return true if all steps succeed; throws std::runtime_error on fatal errors.
+   */
   bool setupExchangeCredentials();
 };
 
diff --git a/CrestApi/CrestRequest.h b/CrestApi/CrestRequest.h
index d38d7f6..481e3a0 100644
--- a/CrestApi/CrestRequest.h
+++ b/CrestApi/CrestRequest.h
@@ -82,7 +82,22 @@ class CrestRequest {
   // Getters
   std::string getUrl();
 
-  // Setup authentication
+/**
+ * Initializes authentication and authorization for CREST requests.
+ * 
+ * This method reads the specified execution mode (e.g., "JWT", "CLIENT_CREDENTIALS")
+ * and sets up access credentials accordingly. It:
+ * - Requests an access token via an exchange token if mode="JWT"
+ * - Requests an access token via client id and secret if mode="CLIENT_CREDENTIALS"
+ *
+ * If the mode is nullptr (i.e. environment variable not set), no special behavior is applied.
+ *
+ * @param mode - A C-style string indicating the execution mode (e.g., from the CREST_AUTH_MODE
+ * environment variable). If nullptr, the default behavior is applied.
+ *
+ * @throws std::runtime_error if authentication fails critically (e.g., missing token file or
+ * failed token exchange).
+ */
   void initAuth(const char* mode);
 
   /**
diff --git a/src/CrestAuth.cxx b/src/CrestAuth.cxx
index ee54875..3354597 100644
--- a/src/CrestAuth.cxx
+++ b/src/CrestAuth.cxx
@@ -1,6 +1,7 @@
 // CrestAuth.cxx
 #include <CrestApi/CrestAuth.h>
 #include <CrestApi/JwtUtils.h>
+#include <CrestApi/CrestLogger.h>
 #include <fstream>
 #include <iostream>
 #include <sstream>
@@ -17,13 +18,13 @@ CrestAuth::CrestAuth()
 bool CrestAuth::loadTokens() {
   std::ifstream file(m_tokenFilePath);
   if (!file.is_open()) {
-    std::cerr << "[CrestAuth] Failed to open token file: " << m_tokenFilePath << "\n";
+    Logger::log(LogLevel::ERROR, "Failed to open token file: " + m_tokenFilePath);
     return false;
   }
 
   std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
   if (content.empty()) {
-    std::cerr << "[CrestAuth] Token file is empty\n";
+    Logger::log(LogLevel::ERROR, "Token file is empty");
     return false;
   }
 
@@ -34,7 +35,7 @@ bool CrestAuth::loadTokens() {
   try {
     file >> js;
   } catch (const std::exception& e) {
-    std::cerr << "[CrestAuth] Failed to parse token JSON: " << e.what() << "\n";
+    Logger::log(LogLevel::ERROR, "Failed to parse token JSON: " + (std::string)e.what());
     return false;
   }
 
@@ -43,23 +44,23 @@ bool CrestAuth::loadTokens() {
   try {
       m_exchangeTokenExpiresAt = JwtUtils::extractExp(m_exchangeToken, 30);
   } catch (const std::exception& e) {
-      std::cerr << "[CrestAuth] Failed to extract 'exp' from JWT: " << e.what() << "\n";
+      Logger::log(LogLevel::ERROR, "Failed to extract 'exp' from JWT: " + (std::string)e.what());
       m_exchangeTokenExpiresAt = std::time(nullptr) + 300;  // fallback
   }
 
-  std::cerr << "[CrestAuth] Loaded exchange and refresh tokens from file.\n";
+  Logger::log(LogLevel::INFO, "Loaded exchange and refresh tokens from file");
   return true;
 }
 
 bool CrestAuth::saveTokens(const nlohmann::json& tokenJson) const {
   std::ofstream file(m_tokenFilePath);
   if (!file.is_open()) {
-    std::cerr << "[CrestAuth] Failed to open token file for writing: " << m_tokenFilePath << "\n";
+    Logger::log(LogLevel::ERROR, "Failed to open token file for writing: " + m_tokenFilePath);
     return false;
   }
 
   file << tokenJson.dump();
-  std::cerr << "[CrestAuth] Saved exchanged token to file.\n";
+  Logger::log(LogLevel::INFO, "Saved exchanged token to file");
   return true;
 }
 
@@ -81,9 +82,8 @@ bool CrestAuth::refreshExchangeToken() {
   if (response.empty()) return false;
 
   auto js = nlohmann::json::parse(response, nullptr, false);
-  //std::cerr << "[CrestAuth] Response to refresh exchange token: " << js.dump() << "\n";
   if (js.is_discarded() || js.contains("error")) {
-    std::cerr << "[CrestAuth] Failed to refresh exchange token: " << js.dump(2) << "\n";
+    Logger::log(LogLevel::ERROR, "Failed to refresh exchange token: " + js.dump(2));
     return false;
   }
 
@@ -92,22 +92,22 @@ bool CrestAuth::refreshExchangeToken() {
   try {
       m_exchangeTokenExpiresAt = JwtUtils::extractExp(m_exchangeToken, 30);
   } catch (const std::exception& e) {
-      std::cerr << "[CrestAuth] Failed to extract 'exp' from JWT: " << e.what() << "\n";
+      Logger::log(LogLevel::ERROR, "Failed to extract 'exp' from JWT: " + (std::string)e.what());
       m_exchangeTokenExpiresAt = std::time(nullptr) + 300;  // fallback
   }
-  std::cerr << "[CrestAuth] Successfully refreshed exchange token.\n";
+  Logger::log(LogLevel::INFO, "Successfully refreshed exchange token");
   return saveTokens(js);
 }
 
 bool CrestAuth::exchangeTokenForAccess() {
   CURL* curl = curl_easy_init();
   if (!curl) {
-    std::cerr << "[CrestAuth] Failed to initialize CURL.\n";
+    Logger::log(LogLevel::ERROR, "Failed to initialize CURL");
     return false;
   }
 
   if (m_exchangeToken.empty()) {
-    std::cerr << "[CrestAuth] Exchange token is empty.\n";
+    Logger::log(LogLevel::ERROR, "Exchange token is empty");
     curl_easy_cleanup(curl);
     return false;
   }
@@ -126,13 +126,13 @@ bool CrestAuth::exchangeTokenForAccess() {
 
   auto js = nlohmann::json::parse(response, nullptr, false);
   if (js.is_discarded() || !js.contains("access_token")) {
-    std::cerr << "[CrestAuth] Token exchange failed: " << js.dump(2) << "\n";
+    Logger::log(LogLevel::ERROR, "Token exchange failed: " + js.dump(2));
     return false;
   }
 
   m_finalAccessToken = js["access_token"];
 
-  std::cerr << "[CrestAuth] Token exchanged successfully.\n";
+  Logger::log(LogLevel::INFO, "Token exchanged successfully");
 
   return true;
 }
@@ -154,7 +154,8 @@ std::string CrestAuth::postRequest(const std::string &url, const std::string &po
 
   CURLcode res = curl_easy_perform(curl);
   if (res != CURLE_OK) {
-    std::cerr << "[CrestAuth] curl_easy_perform failed: " << curl_easy_strerror(res) << "\n";
+    std::string errMsg = curl_easy_strerror(res);
+    Logger::log(LogLevel::ERROR, "curl_easy_perform failed: " + errMsg);
   }
 
   curl_slist_free_all(headers);
@@ -170,31 +171,29 @@ size_t CrestAuth::WriteCallback(void* contents, size_t size, size_t nmemb, void*
 }
 
 bool CrestAuth::setupExchangeCredentials() {
-  std::cerr << "[CrestAuth] Setting up authorization - requesting access token...\n";
+  Logger::log(LogLevel::INFO, "Setting up authorization - requesting access token...");
 
   bool tokensLoaded = loadTokens();
   if (!tokensLoaded) {
-    throw std::runtime_error(
-      "[CrestAuth] No valid token file found.\n"
-      "Please generate a new exchange token using the authentication utility:\n"
-      "auth-get-user-token -c crest-client -o ~/.client-exchange-token.json");
+    Logger::log(LogLevel::ERROR, "No valid token file found.\nExecute: auth-get-user-token -c crest-client -o ~/.client-exchange-token.json");
+    return false;
   }
 
-  std::cerr << "[CrestAuth] Checking if exchange token is expired...\n";
+  Logger::log(LogLevel::INFO, "Checking if exchange token is expired...");
   if (isExchangeTokenExpired()) {
-    std::cerr << "[CrestAuth] Exchange token is expired. Attempting refresh...\n";
+    Logger::log(LogLevel::INFO, "Exchange token is expired. Attempting refresh...");
     if (!refreshExchangeToken()) {
-      throw std::runtime_error(
-        "[CrestAuth] Exchange token is expired and refresh failed.\n"
-        "Please generate a new token manually.");
+      Logger::log(LogLevel::ERROR, "Exchange token is expired and refresh failed.\nExecute: auth-get-user-token -c crest-client -o ~/.client-exchange-token.json");
+      return false;
     }
   }
 
   if (!exchangeTokenForAccess()) {
-    throw std::runtime_error("[CrestAuth] Failed to obtain access token from exchange.");
+    Logger::log(LogLevel::ERROR, "Failed to obtain access token from exchange");
+    return false;
   }
 
-  std::cerr << "[CrestAuth] Setup complete — access token obtained.\n";
+  Logger::log(LogLevel::INFO, "Setup complete — access token obtained");
   return true;
 }
 }  // namespace Crest
diff --git a/src/CrestRequest.cxx b/src/CrestRequest.cxx
index aa37b06..b919c53 100644
--- a/src/CrestRequest.cxx
+++ b/src/CrestRequest.cxx
@@ -79,21 +79,21 @@ std::string CrestRequest::getUrl() {
 
 void CrestRequest::initAuth(const char* mode) {
   if (!mode) {
-      std::cerr << "[CrestRequest] CREST_AUTH_MODE is unset — skipping client authorization.\n";
+      Logger::log(LogLevel::ERROR, "CREST_AUTH_MODE is unset — skipping client authorization");
       return;
   }
 
   std::string modeStr = mode;
-  std::cerr << "[CrestRequest] CREST_AUTH_MODE is set to: " << modeStr << "\n";
+  Logger::log(LogLevel::DEBUG, "CREST_AUTH_MODE is set to: " + modeStr);
 
   if (modeStr == "JWT") {
-      std::cerr << "[CrestRequest] Authorizing via JWT.\n";
+      Logger::log(LogLevel::INFO, "Authorizing via JWT");
       m_auth.setupExchangeCredentials();
   } else if (modeStr == "CLIENT_CREDENTIALS") {
-      std::cerr << "[CrestRequest] Authorizing via client id and secret.\n";
+      Logger::log(LogLevel::INFO, "Authorizing via client id and secret");
       // m_auth.setupClientCredentials();
   } else {
-      std::cerr << "[CrestRequest] Valid values of CREST_AUTH_MODE: JWT, CLIENT_CREDENTIALS — skipping client authorization.\n";
+      Logger::log(LogLevel::INFO, "Valid values of CREST_AUTH_MODE: JWT, CLIENT_CREDENTIALS — skipping client authorization");
   }
 }
 
-- 
GitLab


From ddb0a736a84afd400ee910430e7dfe58190aeaec Mon Sep 17 00:00:00 2001
From: mavogel <mavogel@cern.ch>
Date: Thu, 15 May 2025 23:43:40 +0200
Subject: [PATCH 29/33] Added client id and secret authorization

---
 CrestApi/CrestAuth.h | 14 ++++++++++++++
 CrestApi/JwtUtils.h  | 20 +++++++++++++++++++-
 src/CrestAuth.cxx    | 41 ++++++++++++++++++++++++++++++++++++++++-
 src/CrestRequest.cxx | 12 ++++++++----
 4 files changed, 81 insertions(+), 6 deletions(-)

diff --git a/CrestApi/CrestAuth.h b/CrestApi/CrestAuth.h
index 1336f81..e9b1ff8 100644
--- a/CrestApi/CrestAuth.h
+++ b/CrestApi/CrestAuth.h
@@ -18,6 +18,7 @@ class CrestAuth {
  private:
   std::string m_tokenFilePath;
   std::string m_tokenEndpoint;
+  std::string m_apiEndpoint;
   std::string m_clientId;
   std::string m_audience;
 
@@ -108,6 +109,19 @@ class CrestAuth {
    * errors.
    */
   bool setupExchangeCredentials();
+
+  /**
+   * Requests an access token using the OAuth2 client credentials flow.
+   * Reads client ID and client secret from environment variables.
+   * Stores the resulting access token in memory.
+   *
+   * Environment variables:
+   * - CREST_CLIENT_ID
+   * - CREST_CLIENT_SECRET
+   *
+   * @return true if the token was successfully obtained and stored, false otherwise.
+   */
+  bool setupClientCredentials();
 };
 
 }  // namespace Crest
diff --git a/CrestApi/JwtUtils.h b/CrestApi/JwtUtils.h
index ccd2d5d..89a1f34 100644
--- a/CrestApi/JwtUtils.h
+++ b/CrestApi/JwtUtils.h
@@ -5,12 +5,30 @@
 #include <string>
 
 namespace Crest {
+
+/**
+ * Utility class for decoding and inspecting JWT (JSON Web Token) claims.
+ */
 class JwtUtils {
  public:
-  // Extracts the `exp` (expiration timestamp) from a JWT token
+  /**
+   * Extracts the `exp` (expiration) claim from a JWT access token.
+   * Optionally subtracts a grace period to account for clock skew.
+   *
+   * @param jwt A JWT string (in the format header.payload.signature).
+   * @param graceSeconds Optional number of seconds to subtract from the extracted expiration time.
+   * @return The expiration timestamp as time_t (Unix time).
+   * @throws std::runtime_error if the token is malformed or the exp claim is missing.
+   */
   static std::time_t extractExp(const std::string& jwt, int graceSeconds = 0);
 
  private:
+  /**
+   * Decodes a Base64URL-encoded string (used in JWT header and payload).
+   *
+   * @param input The Base64URL-encoded string.
+   * @return The decoded string.
+   */
   static std::string base64urlDecode(const std::string& input);
 };
 }  // namespace Crest
diff --git a/src/CrestAuth.cxx b/src/CrestAuth.cxx
index 1994bc2..dc339b9 100644
--- a/src/CrestAuth.cxx
+++ b/src/CrestAuth.cxx
@@ -15,6 +15,8 @@ CrestAuth::CrestAuth()
       m_tokenEndpoint(
           "https://auth.cern.ch/auth/realms/cern/protocol/openid-connect/"
           "token"),
+      m_apiEndpoint(
+          "https://auth.cern.ch/auth/realms/cern/api-access/token"),
       m_clientId("crest-client"),
       m_audience("crest-server"),
       m_exchangeTokenExpiresAt(0) {}
@@ -164,7 +166,7 @@ std::string CrestAuth::postRequest(const std::string& url,
   headers = curl_slist_append(
       headers, "Content-Type: application/x-www-form-urlencoded");
   curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
-  // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+  //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
 
   CURLcode res = curl_easy_perform(curl);
   if (res != CURLE_OK) {
@@ -218,4 +220,41 @@ bool CrestAuth::setupExchangeCredentials() {
   Logger::log(LogLevel::INFO, "Setup complete — access token obtained");
   return true;
 }
+
+bool CrestAuth::setupClientCredentials() {
+  const char* clientId = std::getenv("CREST_CLIENT_ID");
+  const char* clientSecret = std::getenv("CREST_CLIENT_SECRET");
+
+  if (!clientId || !clientSecret) {
+    Logger::log(LogLevel::ERROR, "Missing CREST_CLIENT_ID or CREST_CLIENT_SECRET environment variable.");
+    return false;
+  }
+
+  std::ostringstream oss;
+  oss << "grant_type=client_credentials"
+      << "&client_id=" << clientId
+      << "&client_secret=" << clientSecret
+      << "&subject_token_type=urn:ietf:params:oauth:token-type:access_token"
+      << "scope=openid"
+      << "&audience=" << m_audience;
+
+  std::string response = postRequest(m_apiEndpoint, oss.str());
+  if (response.empty()) {
+    Logger::log(LogLevel::ERROR, "No response received from client credentials request.");
+    return false;
+  }
+
+  auto js = nlohmann::json::parse(response, nullptr, false);
+  //std::cerr << " client id and secret token: " << js.dump() << "\n";
+  if (js.is_discarded() || !js.contains("access_token")) {
+    Logger::log(LogLevel::ERROR, "Failed to obtain access token using client credentials: " + js.dump(2));
+    return false;
+  }
+
+  m_finalAccessToken = js["access_token"];
+
+  Logger::log(LogLevel::INFO, "Access token acquired via client credentials flow");
+
+  return true;
+}
 }  // namespace Crest
diff --git a/src/CrestRequest.cxx b/src/CrestRequest.cxx
index 40f3a99..e64882b 100644
--- a/src/CrestRequest.cxx
+++ b/src/CrestRequest.cxx
@@ -79,7 +79,7 @@ std::string CrestRequest::getUrl() {
 
 void CrestRequest::initAuth(const char *mode) {
   if (!mode) {
-    Logger::log(LogLevel::ERROR,
+    Logger::log(LogLevel::WARNING,
                 "CREST_AUTH_MODE is unset — skipping client authorization");
     return;
   }
@@ -92,7 +92,7 @@ void CrestRequest::initAuth(const char *mode) {
     m_auth.setupExchangeCredentials();
   } else if (modeStr == "CLIENT_CREDENTIALS") {
     Logger::log(LogLevel::INFO, "Authorizing via client id and secret");
-    // m_auth.setupClientCredentials();
+    m_auth.setupClientCredentials();
   } else {
     Logger::log(LogLevel::INFO,
                 "Valid values of CREST_AUTH_MODE: JWT, CLIENT_CREDENTIALS — "
@@ -175,7 +175,7 @@ std::string CrestRequest::performRequest(const std::string &current_path,
     // -k analogue:
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
-    // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
     if (js.is_null()) {
       if (action == Action::DELETE)
         curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
@@ -304,7 +304,11 @@ std::string CrestRequest::uploadPayload(
   curl = curl_easy_init();
 
   struct curl_slist *headers = NULL;
-
+  std::string token = m_auth.getAccessToken();
+  if (!token.empty()) {
+    std::string authHeader = "Authorization: Bearer " + token;
+    headers = curl_slist_append(headers, authHeader.c_str());
+  }
   if (curl) {
     std::string url = Crest::StringUtils::appendEndpoint(m_url, current_path);
     std::string response;
-- 
GitLab


From 520c6b440ba30f079d978b436e3e28260fcccb58 Mon Sep 17 00:00:00 2001
From: mavogel <mavogel@cern.ch>
Date: Thu, 15 May 2025 23:58:23 +0200
Subject: [PATCH 30/33] Cleaning up

---
 src/CrestAuth.cxx | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/CrestAuth.cxx b/src/CrestAuth.cxx
index dc339b9..ea0866c 100644
--- a/src/CrestAuth.cxx
+++ b/src/CrestAuth.cxx
@@ -245,7 +245,6 @@ bool CrestAuth::setupClientCredentials() {
   }
 
   auto js = nlohmann::json::parse(response, nullptr, false);
-  //std::cerr << " client id and secret token: " << js.dump() << "\n";
   if (js.is_discarded() || !js.contains("access_token")) {
     Logger::log(LogLevel::ERROR, "Failed to obtain access token using client credentials: " + js.dump(2));
     return false;
-- 
GitLab


From 522eb575a64762c0490e58984b88966a91014b27 Mon Sep 17 00:00:00 2001
From: mavogel <mavogel@cern.ch>
Date: Fri, 16 May 2025 16:56:15 +0200
Subject: [PATCH 31/33] Added authorization section to README, and a helper
 function to append authentication header

---
 CrestApi/CrestRequest.h |  6 ++++++
 README.md               | 18 +++++++++++++++++-
 src/CrestAuth.cxx       |  2 +-
 src/CrestRequest.cxx    | 32 +++++++++++++++++++++-----------
 4 files changed, 45 insertions(+), 13 deletions(-)

diff --git a/CrestApi/CrestRequest.h b/CrestApi/CrestRequest.h
index 9825e7b..3e64d05 100644
--- a/CrestApi/CrestRequest.h
+++ b/CrestApi/CrestRequest.h
@@ -103,6 +103,12 @@ class CrestRequest {
    */
   void initAuth(const char *mode);
 
+  /**
+   * Creates an Authorization header using the current access token from CrestAuth.
+   * @return A curl_slist pointer containing the Authorization header, or nullptr if no token is available.
+   */
+  struct curl_slist* createAuthHeader() const;
+
   /**
    * General auxillary method to make request to the CREST Server. This method
    * is used by other methods realizing the requests with the concrete kinds of
diff --git a/README.md b/README.md
index c7eb90c..e846005 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,22 @@ cmake -DCMAKE_INSTALL_PREFIX=$HOME/local_lib ..
 make
 make install
 ```
+
+## Authorization
+Currently, the API supports two types of authorization workflows:
+* `Client credentials`: the client is authenticated using a client id and secret.
+```shell
+export CREST_CLIENT_ID=<client id>
+export CREST_CLIENT_SECRET=<secret>
+export CREST_AUTH_MODE=CLIENT_CREDENTIALS
+```
+* `Token exchange`: the client is authenticated using an exchange token. To obtain the token excute `auth-get-user-token`.
+```shell
+auth-get-user-token -c crest-client -o ~/.client-exchange-token.json
+export CREST_AUTH_MODE=JWT
+```
+Make sure to use `https` in `export CREST_API_URL=https://...`
+
 ## Testing
 The simple examples are in the `doc` and `test` directory. The executables will be created in the `build` directory.
 
@@ -64,4 +80,4 @@ The simple examples are in the `doc` and `test` directory. The executables will
 You can validate your installation for example using:
 ```shell
 ./crest_example_server http://crest-j23.cern.ch:8080/api-v5.0
-```
\ No newline at end of file
+```
diff --git a/src/CrestAuth.cxx b/src/CrestAuth.cxx
index ea0866c..efd2d86 100644
--- a/src/CrestAuth.cxx
+++ b/src/CrestAuth.cxx
@@ -95,7 +95,7 @@ bool CrestAuth::refreshExchangeToken() {
   auto js = nlohmann::json::parse(response, nullptr, false);
   if (js.is_discarded() || js.contains("error")) {
     Logger::log(LogLevel::ERROR,
-                "Failed to refresh exchange token: " + js.dump(2));
+                "Failed to refresh exchange token: " + js.dump());
     return false;
   }
 
diff --git a/src/CrestRequest.cxx b/src/CrestRequest.cxx
index e64882b..e09304f 100644
--- a/src/CrestRequest.cxx
+++ b/src/CrestRequest.cxx
@@ -100,6 +100,16 @@ void CrestRequest::initAuth(const char *mode) {
   }
 }
 
+struct curl_slist* CrestRequest::createAuthHeader() const {
+  struct curl_slist* headers = nullptr;
+  std::string token = m_auth.getAccessToken();
+  if (!token.empty()) {
+    std::string authHeader = "Authorization: Bearer " + token;
+    headers = curl_slist_append(headers, authHeader.c_str());
+  }
+  return headers;
+}
+
 size_t WriteCallback(void *contents, size_t size, size_t nmemb,
                      std::vector<char> *output) {
   size_t total_size = size * nmemb;
@@ -147,11 +157,9 @@ std::string CrestRequest::performRequest(const std::string &current_path,
   /* get a curl handle */
   curl = curl_easy_init();
   std::string stt;
-  struct curl_slist *headers = NULL;
-  std::string token = m_auth.getAccessToken();
-  if (!token.empty()) {
-    std::string authHeader = "Authorization: Bearer " + token;
-    headers = curl_slist_append(headers, authHeader.c_str());
+  struct curl_slist* headers = createAuthHeader();
+  if (headers) {
+  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
   }
   if (curl) {
     std::string url = Crest::StringUtils::appendEndpoint(m_url, current_path);
@@ -238,7 +246,10 @@ std::vector<char> CrestRequest::getPayloadRequest(
 
   curl_global_init(CURL_GLOBAL_DEFAULT);
   curl = curl_easy_init();
-
+  struct curl_slist* headers = createAuthHeader();
+  if (headers) {
+  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+  }
   if (curl) {
     std::string url = Crest::StringUtils::appendEndpoint(m_url, current_path);
     std::string response;
@@ -280,6 +291,7 @@ std::vector<char> CrestRequest::getPayloadRequest(
     long response_code;
     curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
 
+    curl_slist_free_all(headers);
     curl_easy_cleanup(curl);
 
     // error checking in the server response:
@@ -303,11 +315,9 @@ std::string CrestRequest::uploadPayload(
   curl_global_init(CURL_GLOBAL_DEFAULT);
   curl = curl_easy_init();
 
-  struct curl_slist *headers = NULL;
-  std::string token = m_auth.getAccessToken();
-  if (!token.empty()) {
-    std::string authHeader = "Authorization: Bearer " + token;
-    headers = curl_slist_append(headers, authHeader.c_str());
+  struct curl_slist* headers = createAuthHeader();
+  if (headers) {
+  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
   }
   if (curl) {
     std::string url = Crest::StringUtils::appendEndpoint(m_url, current_path);
-- 
GitLab


From 4eb83113d2b3f95940661f52172d3a0b1bea7313 Mon Sep 17 00:00:00 2001
From: mavogel <mavogel@cern.ch>
Date: Wed, 28 May 2025 00:48:36 +0200
Subject: [PATCH 32/33] Decreased verbosity in authentication logic

---
 src/CrestAuth.cxx    | 18 +++++++++---------
 src/CrestRequest.cxx |  6 +++---
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/CrestAuth.cxx b/src/CrestAuth.cxx
index efd2d86..2a48a3a 100644
--- a/src/CrestAuth.cxx
+++ b/src/CrestAuth.cxx
@@ -58,7 +58,7 @@ bool CrestAuth::loadTokens() {
     m_exchangeTokenExpiresAt = std::time(nullptr) + 300;  // fallback
   }
 
-  Logger::log(LogLevel::INFO, "Loaded exchange and refresh tokens from file");
+  Logger::log(LogLevel::DEBUG, "Loaded exchange and refresh tokens from file");
   return true;
 }
 
@@ -71,7 +71,7 @@ bool CrestAuth::saveTokens(const nlohmann::json& tokenJson) const {
   }
 
   file << tokenJson.dump();
-  Logger::log(LogLevel::INFO, "Saved exchanged token to file");
+  Logger::log(LogLevel::DEBUG, "Saved exchanged token to file");
   return true;
 }
 
@@ -108,7 +108,7 @@ bool CrestAuth::refreshExchangeToken() {
                 "Failed to extract 'exp' from JWT: " + (std::string)e.what());
     m_exchangeTokenExpiresAt = std::time(nullptr) + 300;  // fallback
   }
-  Logger::log(LogLevel::INFO, "Successfully refreshed exchange token");
+  Logger::log(LogLevel::DEBUG, "Successfully refreshed exchange token");
   return saveTokens(js);
 }
 
@@ -145,7 +145,7 @@ bool CrestAuth::exchangeTokenForAccess() {
 
   m_finalAccessToken = js["access_token"];
 
-  Logger::log(LogLevel::INFO, "Token exchanged successfully");
+  Logger::log(LogLevel::DEBUG, "Token exchanged successfully");
 
   return true;
 }
@@ -188,7 +188,7 @@ size_t CrestAuth::WriteCallback(void* contents, size_t size, size_t nmemb,
 }
 
 bool CrestAuth::setupExchangeCredentials() {
-  Logger::log(LogLevel::INFO,
+  Logger::log(LogLevel::DEBUG,
               "Setting up authorization - requesting access token...");
 
   bool tokensLoaded = loadTokens();
@@ -199,9 +199,9 @@ bool CrestAuth::setupExchangeCredentials() {
     return false;
   }
 
-  Logger::log(LogLevel::INFO, "Checking if exchange token is expired...");
+  Logger::log(LogLevel::DEBUG, "Checking if exchange token is expired...");
   if (isExchangeTokenExpired()) {
-    Logger::log(LogLevel::INFO,
+    Logger::log(LogLevel::DEBUG,
                 "Exchange token is expired. Attempting refresh...");
     if (!refreshExchangeToken()) {
       Logger::log(LogLevel::ERROR,
@@ -217,7 +217,7 @@ bool CrestAuth::setupExchangeCredentials() {
     return false;
   }
 
-  Logger::log(LogLevel::INFO, "Setup complete — access token obtained");
+  Logger::log(LogLevel::DEBUG, "Setup complete — access token obtained");
   return true;
 }
 
@@ -252,7 +252,7 @@ bool CrestAuth::setupClientCredentials() {
 
   m_finalAccessToken = js["access_token"];
 
-  Logger::log(LogLevel::INFO, "Access token acquired via client credentials flow");
+  Logger::log(LogLevel::DEBUG, "Access token acquired via client credentials flow");
 
   return true;
 }
diff --git a/src/CrestRequest.cxx b/src/CrestRequest.cxx
index e09304f..24b48ea 100644
--- a/src/CrestRequest.cxx
+++ b/src/CrestRequest.cxx
@@ -88,13 +88,13 @@ void CrestRequest::initAuth(const char *mode) {
   Logger::log(LogLevel::DEBUG, "CREST_AUTH_MODE is set to: " + modeStr);
 
   if (modeStr == "JWT") {
-    Logger::log(LogLevel::INFO, "Authorizing via JWT");
+    Logger::log(LogLevel::DEBUG, "Authorizing via JWT");
     m_auth.setupExchangeCredentials();
   } else if (modeStr == "CLIENT_CREDENTIALS") {
-    Logger::log(LogLevel::INFO, "Authorizing via client id and secret");
+    Logger::log(LogLevel::DEBUG, "Authorizing via client id and secret");
     m_auth.setupClientCredentials();
   } else {
-    Logger::log(LogLevel::INFO,
+    Logger::log(LogLevel::DEBUG,
                 "Valid values of CREST_AUTH_MODE: JWT, CLIENT_CREDENTIALS — "
                 "skipping client authorization");
   }
-- 
GitLab


From cfd3013a75d6ffbe0ef7e40b5256dc3dc846f61b Mon Sep 17 00:00:00 2001
From: andrea formica <andrea.formica@cern.ch>
Date: Sat, 7 Jun 2025 07:58:43 +0200
Subject: [PATCH 33/33] manual run of pre-commit for formatting

---
 CrestApi/CrestAuth.h    |  3 ++-
 CrestApi/CrestRequest.h |  8 +++++---
 CrestApi/JwtUtils.h     |  6 ++++--
 src/CrestAuth.cxx       | 22 +++++++++++++---------
 src/CrestRequest.cxx    | 18 +++++++++---------
 5 files changed, 33 insertions(+), 24 deletions(-)

diff --git a/CrestApi/CrestAuth.h b/CrestApi/CrestAuth.h
index e9b1ff8..83bf766 100644
--- a/CrestApi/CrestAuth.h
+++ b/CrestApi/CrestAuth.h
@@ -119,7 +119,8 @@ class CrestAuth {
    * - CREST_CLIENT_ID
    * - CREST_CLIENT_SECRET
    *
-   * @return true if the token was successfully obtained and stored, false otherwise.
+   * @return true if the token was successfully obtained and stored, false
+   * otherwise.
    */
   bool setupClientCredentials();
 };
diff --git a/CrestApi/CrestRequest.h b/CrestApi/CrestRequest.h
index 3e64d05..401b605 100644
--- a/CrestApi/CrestRequest.h
+++ b/CrestApi/CrestRequest.h
@@ -104,10 +104,12 @@ class CrestRequest {
   void initAuth(const char *mode);
 
   /**
-   * Creates an Authorization header using the current access token from CrestAuth.
-   * @return A curl_slist pointer containing the Authorization header, or nullptr if no token is available.
+   * Creates an Authorization header using the current access token from
+   * CrestAuth.
+   * @return A curl_slist pointer containing the Authorization header, or
+   * nullptr if no token is available.
    */
-  struct curl_slist* createAuthHeader() const;
+  struct curl_slist *createAuthHeader() const;
 
   /**
    * General auxillary method to make request to the CREST Server. This method
diff --git a/CrestApi/JwtUtils.h b/CrestApi/JwtUtils.h
index 89a1f34..2a15a64 100644
--- a/CrestApi/JwtUtils.h
+++ b/CrestApi/JwtUtils.h
@@ -16,9 +16,11 @@ class JwtUtils {
    * Optionally subtracts a grace period to account for clock skew.
    *
    * @param jwt A JWT string (in the format header.payload.signature).
-   * @param graceSeconds Optional number of seconds to subtract from the extracted expiration time.
+   * @param graceSeconds Optional number of seconds to subtract from the
+   * extracted expiration time.
    * @return The expiration timestamp as time_t (Unix time).
-   * @throws std::runtime_error if the token is malformed or the exp claim is missing.
+   * @throws std::runtime_error if the token is malformed or the exp claim is
+   * missing.
    */
   static std::time_t extractExp(const std::string& jwt, int graceSeconds = 0);
 
diff --git a/src/CrestAuth.cxx b/src/CrestAuth.cxx
index 2a48a3a..98900ec 100644
--- a/src/CrestAuth.cxx
+++ b/src/CrestAuth.cxx
@@ -15,8 +15,7 @@ CrestAuth::CrestAuth()
       m_tokenEndpoint(
           "https://auth.cern.ch/auth/realms/cern/protocol/openid-connect/"
           "token"),
-      m_apiEndpoint(
-          "https://auth.cern.ch/auth/realms/cern/api-access/token"),
+      m_apiEndpoint("https://auth.cern.ch/auth/realms/cern/api-access/token"),
       m_clientId("crest-client"),
       m_audience("crest-server"),
       m_exchangeTokenExpiresAt(0) {}
@@ -166,7 +165,7 @@ std::string CrestAuth::postRequest(const std::string& url,
   headers = curl_slist_append(
       headers, "Content-Type: application/x-www-form-urlencoded");
   curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
-  //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+  // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
 
   CURLcode res = curl_easy_perform(curl);
   if (res != CURLE_OK) {
@@ -226,33 +225,38 @@ bool CrestAuth::setupClientCredentials() {
   const char* clientSecret = std::getenv("CREST_CLIENT_SECRET");
 
   if (!clientId || !clientSecret) {
-    Logger::log(LogLevel::ERROR, "Missing CREST_CLIENT_ID or CREST_CLIENT_SECRET environment variable.");
+    Logger::log(
+        LogLevel::ERROR,
+        "Missing CREST_CLIENT_ID or CREST_CLIENT_SECRET environment variable.");
     return false;
   }
 
   std::ostringstream oss;
   oss << "grant_type=client_credentials"
-      << "&client_id=" << clientId
-      << "&client_secret=" << clientSecret
+      << "&client_id=" << clientId << "&client_secret=" << clientSecret
       << "&subject_token_type=urn:ietf:params:oauth:token-type:access_token"
       << "scope=openid"
       << "&audience=" << m_audience;
 
   std::string response = postRequest(m_apiEndpoint, oss.str());
   if (response.empty()) {
-    Logger::log(LogLevel::ERROR, "No response received from client credentials request.");
+    Logger::log(LogLevel::ERROR,
+                "No response received from client credentials request.");
     return false;
   }
 
   auto js = nlohmann::json::parse(response, nullptr, false);
   if (js.is_discarded() || !js.contains("access_token")) {
-    Logger::log(LogLevel::ERROR, "Failed to obtain access token using client credentials: " + js.dump(2));
+    Logger::log(LogLevel::ERROR,
+                "Failed to obtain access token using client credentials: " +
+                    js.dump(2));
     return false;
   }
 
   m_finalAccessToken = js["access_token"];
 
-  Logger::log(LogLevel::DEBUG, "Access token acquired via client credentials flow");
+  Logger::log(LogLevel::DEBUG,
+              "Access token acquired via client credentials flow");
 
   return true;
 }
diff --git a/src/CrestRequest.cxx b/src/CrestRequest.cxx
index 24b48ea..cbae7d4 100644
--- a/src/CrestRequest.cxx
+++ b/src/CrestRequest.cxx
@@ -100,8 +100,8 @@ void CrestRequest::initAuth(const char *mode) {
   }
 }
 
-struct curl_slist* CrestRequest::createAuthHeader() const {
-  struct curl_slist* headers = nullptr;
+struct curl_slist *CrestRequest::createAuthHeader() const {
+  struct curl_slist *headers = nullptr;
   std::string token = m_auth.getAccessToken();
   if (!token.empty()) {
     std::string authHeader = "Authorization: Bearer " + token;
@@ -157,9 +157,9 @@ std::string CrestRequest::performRequest(const std::string &current_path,
   /* get a curl handle */
   curl = curl_easy_init();
   std::string stt;
-  struct curl_slist* headers = createAuthHeader();
+  struct curl_slist *headers = createAuthHeader();
   if (headers) {
-  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
   }
   if (curl) {
     std::string url = Crest::StringUtils::appendEndpoint(m_url, current_path);
@@ -183,7 +183,7 @@ std::string CrestRequest::performRequest(const std::string &current_path,
     // -k analogue:
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
-    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
     if (js.is_null()) {
       if (action == Action::DELETE)
         curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
@@ -246,9 +246,9 @@ std::vector<char> CrestRequest::getPayloadRequest(
 
   curl_global_init(CURL_GLOBAL_DEFAULT);
   curl = curl_easy_init();
-  struct curl_slist* headers = createAuthHeader();
+  struct curl_slist *headers = createAuthHeader();
   if (headers) {
-  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
   }
   if (curl) {
     std::string url = Crest::StringUtils::appendEndpoint(m_url, current_path);
@@ -315,9 +315,9 @@ std::string CrestRequest::uploadPayload(
   curl_global_init(CURL_GLOBAL_DEFAULT);
   curl = curl_easy_init();
 
-  struct curl_slist* headers = createAuthHeader();
+  struct curl_slist *headers = createAuthHeader();
   if (headers) {
-  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
   }
   if (curl) {
     std::string url = Crest::StringUtils::appendEndpoint(m_url, current_path);
-- 
GitLab