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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ jobs:
exclude_api_version_tag: ""
audit_enabled: "false"
- reductstore_version: "latest"
exclude_api_version_tag: "~[1_19]"
exclude_api_version_tag: "~[1_20]"
audit_enabled: "false"

steps:
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

- Add lifecycle policy API support, [PR-128](https://github.com/reductstore/reduct-cpp/pull/128)

## 1.19.1 - 2026-04-21

### Fixed
Expand Down
61 changes: 61 additions & 0 deletions src/reduct/client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,67 @@ class Client : public IClient {
return client_->Delete(fmt::format("/replications/{}", name));
}

Result<std::vector<LifecycleInfo>> GetLifecycleList() const noexcept override {
auto [body, err] = client_->Get("/lifecycles");
if (err) {
return {{}, std::move(err)};
}

try {
nlohmann::json data = nlohmann::json::parse(body);
return internal::ParseLifecycleList(data);
} catch (const std::exception& e) {
return {{}, Error{.code = -1, .message = e.what()}};
}
}

Result<FullLifecycleInfo> GetLifecycle(std::string_view name) const noexcept override {
auto [body, err] = client_->Get(fmt::format("/lifecycles/{}", name));
if (err) {
return {{}, std::move(err)};
}

try {
nlohmann::json data = nlohmann::json::parse(body);
return internal::ParseFullLifecycleInfo(data);
} catch (const std::exception& e) {
return {{}, Error{.code = -1, .message = e.what()}};
}
}

Error CreateLifecycle(std::string_view name, LifecycleSettings settings) const noexcept override {
auto [json_data, json_err] = internal::LifecycleSettingsToJsonString(std::move(settings));
if (json_err) {
return json_err;
}

return client_->Post(fmt::format("/lifecycles/{}", name), json_data.dump());
}

Error UpdateLifecycle(std::string_view name, LifecycleSettings settings) const noexcept override {
auto [json_data, json_err] = internal::LifecycleSettingsToJsonString(std::move(settings));
if (json_err) {
return json_err;
}

return client_->Put(fmt::format("/lifecycles/{}", name), json_data.dump());
}

Error SetLifecycleMode(std::string_view name, LifecycleMode mode) const noexcept override {
try {
nlohmann::json payload = {{"mode", internal::LifecycleModeToString(mode)}};
auto patch_result = client_->Patch(fmt::format("/lifecycles/{}/mode", name), payload.dump(),
{{"Content-Type", "application/json"}});
return patch_result.error;
} catch (const std::exception& ex) {
return Error{.code = -1, .message = ex.what()};
}
}

Error RemoveLifecycle(std::string_view name) const noexcept override {
return client_->Delete(fmt::format("/lifecycles/{}", name));
}

private:
HttpOptions options_;
std::unique_ptr<internal::IHttpClient> client_;
Expand Down
140 changes: 112 additions & 28 deletions src/reduct/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class IClient {
public:
using Time = std::chrono::time_point<std::chrono::system_clock>;


/**
* Reduct Storage Information
*/
Expand Down Expand Up @@ -105,14 +104,14 @@ class IClient {
* API Token for authentication
*/
struct Token {
std::string name; // name of token
Time created_at; // creation time
bool is_provisioned; // true if token is provisioned, you can't remove it or change its permissions
std::optional<Time> expires_at; // absolute expiry timestamp (UTC)
std::optional<uint64_t> ttl; // inactivity TTL in seconds
std::optional<Time> last_access; // last access timestamp
std::string name; // name of token
Time created_at; // creation time
bool is_provisioned; // true if token is provisioned, you can't remove it or change its permissions
std::optional<Time> expires_at; // absolute expiry timestamp (UTC)
std::optional<uint64_t> ttl; // inactivity TTL in seconds
std::optional<Time> last_access; // last access timestamp
std::vector<std::string> ip_allowlist; // allowed source IP addresses
bool is_expired = false; // token cannot be used anymore
bool is_expired = false; // token cannot be used anymore

auto operator<=>(const IClient::Token&) const = default;
};
Expand All @@ -132,14 +131,14 @@ class IClient {
* Token with permissions
*/
struct FullTokenInfo {
std::string name; // name of token
Time created_at; // creation time
bool is_provisioned; // true if token is provisioned, you can't remove it or change its permissions
std::optional<Time> expires_at; // absolute expiry timestamp (UTC)
std::optional<uint64_t> ttl; // inactivity TTL in seconds
std::optional<Time> last_access; // last access timestamp
std::string name; // name of token
Time created_at; // creation time
bool is_provisioned; // true if token is provisioned, you can't remove it or change its permissions
std::optional<Time> expires_at; // absolute expiry timestamp (UTC)
std::optional<uint64_t> ttl; // inactivity TTL in seconds
std::optional<Time> last_access; // last access timestamp
std::vector<std::string> ip_allowlist; // allowed source IP addresses
bool is_expired = false; // token cannot be used anymore
bool is_expired = false; // token cannot be used anymore

Permissions permissions;

Expand Down Expand Up @@ -178,8 +177,8 @@ class IClient {
TokenCreateRequest request) const noexcept = 0;

[[deprecated("Use CreateToken(name, TokenCreateRequest) to set ttl/expires_at/ip_allowlist")]]
[[nodiscard]] virtual Result<std::string> CreateToken(std::string_view name,
Permissions permissions) const noexcept = 0;
[[nodiscard]] virtual Result<std::string>
CreateToken(std::string_view name, Permissions permissions) const noexcept = 0;

/**
* @brief Update token permissions
Expand All @@ -206,11 +205,11 @@ class IClient {
enum class ReplicationMode { kEnabled, kPaused, kDisabled };

struct ReplicationInfo {
std::string name; // Replication name
ReplicationMode mode = ReplicationMode::kEnabled; // Replication mode
bool is_active; // Remote instance is available and replication is active
bool is_provisioned; // Replication settings
uint64_t pending_records; // Number of records pending replication
std::string name; // Replication name
ReplicationMode mode = ReplicationMode::kEnabled; // Replication mode
bool is_active; // Remote instance is available and replication is active
bool is_provisioned; // Replication settings
uint64_t pending_records; // Number of records pending replication

auto operator<=>(const ReplicationInfo&) const = default;
};
Expand All @@ -219,13 +218,13 @@ class IClient {
* Replication settings
*/
struct ReplicationSettings {
std::string src_bucket; // Source bucket
std::string dst_bucket; // Destination bucket
std::string dst_host; // Destination host URL (e.g. https://reductstore.com)
std::optional<std::string> dst_token; // Destination access token
std::string src_bucket; // Source bucket
std::string dst_bucket; // Destination bucket
std::string dst_host; // Destination host URL (e.g. https://reductstore.com)
std::optional<std::string> dst_token; // Destination access token
std::vector<std::string>
entries; // Entries to replicate. If empty, all entries are replicated. Wildcards are supported.
std::optional<std::string> when; // Replication condition
entries; // Entries to replicate. If empty, all entries are replicated. Wildcards are supported.
std::optional<std::string> when; // Replication condition
ReplicationMode mode = ReplicationMode::kEnabled; // Replication mode

auto operator<=>(const ReplicationSettings&) const = default;
Expand Down Expand Up @@ -286,6 +285,91 @@ class IClient {
*/
[[nodiscard]] virtual Error RemoveReplication(std::string_view name) const noexcept = 0;

/**
* Lifecycle information
*/
enum class LifecycleType { kDelete };

enum class LifecycleMode { kEnabled, kDisabled, kDryRun };

struct LifecycleInfo {
std::string name; // Lifecycle name
LifecycleMode mode = LifecycleMode::kEnabled; // Lifecycle mode
bool is_provisioned; // Lifecycle is provisioned
bool is_running; // Lifecycle worker is running

auto operator<=>(const LifecycleInfo&) const = default;
};

/**
* Lifecycle settings
*/
struct LifecycleSettings {
LifecycleType type = LifecycleType::kDelete; // Lifecycle action type
std::string bucket; // Bucket to apply lifecycle policy
std::vector<std::string> entries; // Entries to process. If empty, all matching entries are used.
std::string max_age; // Maximum record age
std::optional<std::string> interval; // Interval between lifecycle runs
std::optional<std::string> when; // Lifecycle condition
LifecycleMode mode = LifecycleMode::kEnabled; // Lifecycle mode

auto operator<=>(const LifecycleSettings&) const = default;
};

/**
* Lifecycle full info with settings
*/
struct FullLifecycleInfo {
LifecycleInfo info; // Lifecycle info
LifecycleSettings settings; // Lifecycle settings

bool operator==(const FullLifecycleInfo&) const = default;
};

/**
* @brief Get list of lifecycles
* @return the list or an error
*/
[[nodiscard]] virtual Result<std::vector<LifecycleInfo>> GetLifecycleList() const noexcept = 0;

/**
* @brief Get lifecycle info with settings
* @param name name of lifecycle
* @return the info or an error
*/
[[nodiscard]] virtual Result<FullLifecycleInfo> GetLifecycle(std::string_view name) const noexcept = 0;

/**
* @brief Create a new lifecycle
* @param name name of lifecycle
* @param settings lifecycle settings
* @return error
*/
[[nodiscard]] virtual Error CreateLifecycle(std::string_view name, LifecycleSettings settings) const noexcept = 0;

/**
* @brief Update lifecycle settings
* @param name name of lifecycle
* @param settings lifecycle settings
* @return error
*/
[[nodiscard]] virtual Error UpdateLifecycle(std::string_view name, LifecycleSettings settings) const noexcept = 0;

/**
* @brief Update lifecycle mode without changing settings
* @param name name of lifecycle
* @param mode lifecycle mode
* @return error
*/
[[nodiscard]] virtual Error SetLifecycleMode(std::string_view name, LifecycleMode mode) const noexcept = 0;

/**
* @brief Remove lifecycle
* @param name name of lifecycle
* @return error
*/
[[nodiscard]] virtual Error RemoveLifecycle(std::string_view name) const noexcept = 0;

/**
* @brief Build a client
* @param url URL of React Storage
Expand Down
Loading
Loading