Skip to content

Commit 3a2abf9

Browse files
committed
Add Config::login API.
1 parent 11e35dc commit 3a2abf9

File tree

3 files changed

+88
-35
lines changed

3 files changed

+88
-35
lines changed

tiledb/sm/config/config.cc

Lines changed: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* The MIT License
77
*
8-
* @copyright Copyright (c) 2017-2023 TileDB, Inc.
8+
* @copyright Copyright (c) 2017-2024 TileDB, Inc.
99
*
1010
* Permission is hereby granted, free of charge, to any person obtaining a copy
1111
* of this software and associated documentation files (the "Software"), to deal
@@ -30,16 +30,20 @@
3030
* This file implements class Config.
3131
*/
3232

33+
#include <filesystem>
3334
#include <fstream>
3435
#include <iostream>
3536
#include <sstream>
3637

3738
#include "config.h"
39+
#include "external/include/nlohmann/json.hpp"
3840
#include "tiledb/common/logger.h"
3941
#include "tiledb/sm/enums/serialization_type.h"
4042
#include "tiledb/sm/misc/constants.h"
4143
#include "tiledb/sm/misc/parse_argument.h"
4244

45+
using json = nlohmann::json;
46+
4347
using namespace tiledb::common;
4448

4549
namespace {
@@ -60,14 +64,12 @@ bool ignore_default_via_env(const std::string& param) {
6064

6165
namespace tiledb::sm {
6266

63-
/** Return a Config error class Status with a given message **/
64-
inline Status Status_ConfigError(const std::string& msg) {
65-
return {"[TileDB::Config] Error", msg};
66-
}
67-
68-
void throw_config_exception(const std::string& msg) {
69-
throw StatusException(Status_ConfigError(msg));
70-
}
67+
class ConfigException : public StatusException {
68+
public:
69+
explicit ConfigException(const std::string& msg)
70+
: StatusException("Config", msg) {
71+
}
72+
};
7173

7274
/* ****************************** */
7375
/* CONFIG DEFAULTS */
@@ -561,17 +563,56 @@ Config::~Config() = default;
561563
/* API */
562564
/* ****************************** */
563565

566+
void Config::login() {
567+
// Retrieve $HOME path
568+
std::string home_dir;
569+
#ifdef _WIN32
570+
home_dir =
571+
std::string(std::getenv("HOMEDRIVE")) + std::string(getenv("HOMEPATH"));
572+
#else
573+
home_dir = std::string(std::getenv("HOME"));
574+
#endif
575+
576+
// For library versions 22 and older, simply parse the local .json file
577+
if (constants::format_version <= 22) {
578+
// Find and parse the cloud.json file
579+
std::string file = home_dir + "/.tiledb/cloud.json";
580+
if (!std::filesystem::exists(file)) {
581+
throw ConfigException("Cannot login; cloud.json file does not exist.");
582+
}
583+
json data = json::parse(std::ifstream(file));
584+
585+
// Set the config values that have been saved to the file
586+
if (data.contains("api_key") &&
587+
data["api_key"].contains("X-TILEDB-REST-API-KEY")) {
588+
throw_if_not_ok(
589+
set("rest.token", data["api_key"]["X-TILEDB-REST-API-KEY"]));
590+
}
591+
if (data.contains("host")) {
592+
throw_if_not_ok(set("rest.server_address", data["host"]));
593+
}
594+
if (data.contains("password")) {
595+
throw_if_not_ok(set("rest.password", data["password"]));
596+
}
597+
if (data.contains("username")) {
598+
throw_if_not_ok(set("rest.username", data["username"]));
599+
}
600+
if (data.contains("verify_ssl")) {
601+
throw_if_not_ok(
602+
set("vfs.s3.verify_ssl", data["verify_ssl"] ? "true" : "false"));
603+
}
604+
}
605+
}
606+
564607
Status Config::load_from_file(const std::string& filename) {
565608
// Do nothing if filename is empty
566-
if (filename.empty())
567-
return LOG_STATUS(
568-
Status_ConfigError("Cannot load from file; Invalid filename"));
609+
if (filename.empty()) {
610+
throw ConfigException("Cannot load from file; Invalid filename");
611+
}
569612

570613
std::ifstream ifs(filename);
571614
if (!ifs.is_open()) {
572-
std::stringstream msg;
573-
msg << "Failed to open config file '" << filename << "'";
574-
return LOG_STATUS(Status_ConfigError(msg.str()));
615+
throw ConfigException("Failed to open config file '" + filename + "'");
575616
}
576617

577618
size_t linenum = 0;
@@ -592,7 +633,7 @@ Status Config::load_from_file(const std::string& filename) {
592633
std::stringstream msg;
593634
msg << "Failed to parse config file '" << filename << "'; ";
594635
msg << "Missing parameter value (line: " << linenum << ")";
595-
return LOG_STATUS(Status_ConfigError(msg.str()));
636+
throw ConfigException(msg.str());
596637
}
597638

598639
// Parse extra
@@ -601,7 +642,7 @@ Status Config::load_from_file(const std::string& filename) {
601642
std::stringstream msg;
602643
msg << "Failed to parse config file '" << filename << "'; ";
603644
msg << "Invalid line format (line: " << linenum << ")";
604-
return LOG_STATUS(Status_ConfigError(msg.str()));
645+
throw ConfigException(msg.str());
605646
}
606647

607648
// Set param-value pair
@@ -614,15 +655,14 @@ Status Config::load_from_file(const std::string& filename) {
614655

615656
Status Config::save_to_file(const std::string& filename) {
616657
// Do nothing if filename is empty
617-
if (filename.empty())
618-
return LOG_STATUS(
619-
Status_ConfigError("Cannot save to file; Invalid filename"));
658+
if (filename.empty()) {
659+
throw ConfigException("Cannot save to file; Invalid filename");
660+
}
620661

621662
std::ofstream ofs(filename);
622663
if (!ofs.is_open()) {
623-
std::stringstream msg;
624-
msg << "Failed to open config file '" << filename << "' for writing";
625-
return LOG_STATUS(Status_ConfigError(msg.str()));
664+
throw ConfigException(
665+
"Failed to open config file '" + filename + "' for writing");
626666
}
627667
for (auto& pv : param_values_) {
628668
if (unserialized_params_.count(pv.first) != 0)
@@ -664,7 +704,7 @@ Status Config::get(const std::string& param, T* value, bool* found) const {
664704
// Parameter found, retrieve value
665705
auto status = utils::parse::convert(val, value);
666706
if (!status.ok()) {
667-
return Status_ConfigError(
707+
throw ConfigException(
668708
std::string("Failed to parse config value '") + std::string(val) +
669709
std::string("' for key '") + param + "' due to: " + status.to_string());
670710
}
@@ -751,8 +791,7 @@ Status Config::sanity_check(
751791
RETURN_NOT_OK(utils::parse::convert(value, &v32));
752792
} else if (param == "config.logging_format") {
753793
if (value != "DEFAULT" && value != "JSON")
754-
return LOG_STATUS(
755-
Status_ConfigError("Invalid logging format parameter value"));
794+
throw ConfigException("Invalid logging format parameter value");
756795
} else if (param == "sm.allow_separate_attribute_writes") {
757796
RETURN_NOT_OK(utils::parse::convert(value, &v));
758797
} else if (param == "sm.allow_updates_experimental") {
@@ -799,8 +838,7 @@ Status Config::sanity_check(
799838
RETURN_NOT_OK(utils::parse::convert(value, &v));
800839
} else if (param == "sm.var_offsets.mode") {
801840
if (value != "bytes" && value != "elements")
802-
return LOG_STATUS(
803-
Status_ConfigError("Invalid offsets format parameter value"));
841+
throw ConfigException("Invalid offsets format parameter value");
804842
} else if (param == "sm.fragment_info.preload_mbrs") {
805843
RETURN_NOT_OK(utils::parse::convert(value, &v));
806844
} else if (param == "ssl.verify") {
@@ -825,8 +863,7 @@ Status Config::sanity_check(
825863
RETURN_NOT_OK(utils::parse::convert(value, &v32));
826864
} else if (param == "vfs.s3.scheme") {
827865
if (value != "http" && value != "https")
828-
return LOG_STATUS(
829-
Status_ConfigError("Invalid S3 scheme parameter value"));
866+
throw ConfigException("Invalid S3 scheme parameter value");
830867
} else if (param == "vfs.s3.use_virtual_addressing") {
831868
RETURN_NOT_OK(utils::parse::convert(value, &v));
832869
} else if (param == "vfs.s3.skit_init") {
@@ -866,7 +903,7 @@ Status Config::sanity_check(
866903
(value == "bucket_owner_full_control"))))) {
867904
std::stringstream msg;
868905
msg << "value " << param << " invalid canned acl for " << param;
869-
return Status_Error(msg.str());
906+
throw ConfigException(msg.str());
870907
}
871908
}
872909

@@ -970,7 +1007,7 @@ optional<T> Config::get_internal(const std::string& key) const {
9701007
if (status.ok()) {
9711008
return {converted_value};
9721009
}
973-
throw_config_exception(
1010+
throw ConfigException(
9741011
"Failed to parse config value '" + std::string(value.value()) +
9751012
"' for key '" + key + "'. Reason: " + status.to_string());
9761013
}
@@ -988,7 +1025,7 @@ optional<std::string> Config::get_internal_string(
9881025
}
9891026

9901027
if constexpr (must_find_) {
991-
throw_config_exception("Failed to get config value for key: " + key);
1028+
throw ConfigException("Failed to get config value for key: " + key);
9921029
}
9931030
return {nullopt};
9941031
}

tiledb/sm/config/config.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* The MIT License
77
*
8-
* @copyright Copyright (c) 2017-2023 TileDB, Inc.
8+
* @copyright Copyright (c) 2017-2024 TileDB, Inc.
99
*
1010
* Permission is hereby granted, free of charge, to any person obtaining a copy
1111
* of this software and associated documentation files (the "Software"), to deal
@@ -667,6 +667,9 @@ class Config {
667667
/* API */
668668
/* ********************************* */
669669

670+
/** Log in to TileDB Cloud, saving profile info to the config. */
671+
void login();
672+
670673
/** Loads the config parameters from a configuration (local) file. */
671674
Status load_from_file(const std::string& filename);
672675

tiledb/sm/config/test/unit_config.cc

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* The MIT License
77
*
8-
* @copyright Copyright (c) 2022 TileDB, Inc.
8+
* @copyright Copyright (c) 2022-2024 TileDB, Inc.
99
*
1010
* Permission is hereby granted, free of charge, to any person obtaining a copy
1111
* of this software and associated documentation files (the "Software"), to deal
@@ -111,3 +111,16 @@ TEST_CASE("Config::get<std::string> - found and matched", "[config]") {
111111
CHECK(c.set(key, expected_value).ok());
112112
TestConfig<std::string>::check_expected(expected_value, c, key);
113113
}
114+
115+
TEST_CASE("Config::login", "[config][login]") {
116+
Config c{};
117+
118+
// Upon initial Config construction, no rest token is set
119+
auto initial_token = c.get<std::string>("rest.token");
120+
CHECK(!initial_token.has_value());
121+
122+
// After login, the rest token is set from the cloud.json file
123+
c.login();
124+
auto login_token = c.get<std::string>("rest.token");
125+
CHECK(login_token.has_value());
126+
}

0 commit comments

Comments
 (0)