This repository has been archived by the owner on Mar 25, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #26 from flipt-io/add-rollouts
feat(rollouts): Add CRUD rollouts and new evaluation operations
- Loading branch information
Showing
7 changed files
with
456 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
use crate::api::{ApiClient, Result, DEFAULT_NAMESPACE}; | ||
use chrono::{DateTime, Utc}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
pub struct RolloutClient<'client> { | ||
client: &'client ApiClient, | ||
} | ||
|
||
impl<'client> RolloutClient<'client> { | ||
pub fn new(client: &'client ApiClient) -> Self { | ||
Self { client } | ||
} | ||
|
||
pub async fn get(&self, get: &RolloutGetRequest) -> Result<Rollout> { | ||
let path = format!( | ||
"/api/v1/namespaces/{namespace_key}/flags/{flag_key}/rollouts/{id}", | ||
namespace_key = get | ||
.namespace_key | ||
.as_ref() | ||
.unwrap_or(&DEFAULT_NAMESPACE.to_string()), | ||
flag_key = get.flag_key, | ||
id = get.id | ||
); | ||
|
||
self.client.get(&path, None::<&()>).await | ||
} | ||
|
||
pub async fn create(&self, create: &RolloutCreateRequest) -> Result<Rollout> { | ||
let path = format!( | ||
"/api/v1/namespaces/{namespace_key}/flags/{flag_key}/rollouts", | ||
namespace_key = create | ||
.namespace_key | ||
.as_ref() | ||
.unwrap_or(&DEFAULT_NAMESPACE.to_string()), | ||
flag_key = create.flag_key | ||
); | ||
|
||
self.client.post(&path, Some(create)).await | ||
} | ||
|
||
pub async fn delete(&self, delete: &RolloutDeleteRequest) -> Result<Empty> { | ||
let path = format!( | ||
"/api/v1/namespaces/{namespace_key}/flags/{flag_key}/rollouts/{id}", | ||
namespace_key = delete | ||
.namespace_key | ||
.as_ref() | ||
.unwrap_or(&DEFAULT_NAMESPACE.to_string()), | ||
flag_key = delete.flag_key, | ||
id = delete.id | ||
); | ||
|
||
self.client.delete(&path, None::<&()>).await | ||
} | ||
|
||
pub async fn update(&self, update: &RolloutUpdateRequest) -> Result<Rollout> { | ||
let path = format!( | ||
"/api/v1/namespaces/{namespace_key}/flags/{flag_key}/rollouts/{id}", | ||
namespace_key = update | ||
.namespace_key | ||
.as_ref() | ||
.unwrap_or(&DEFAULT_NAMESPACE.to_string()), | ||
flag_key = update.flag_key, | ||
id = update.id | ||
); | ||
|
||
self.client.put(&path, Some(update)).await | ||
} | ||
|
||
pub async fn order(&self, order: &RolloutOrderRequest) -> Result<Empty> { | ||
let path = format!( | ||
"/api/v1/namespaces/{namespace_key}/flags/{flag_key}/rollouts/order", | ||
namespace_key = order | ||
.namespace_key | ||
.as_ref() | ||
.unwrap_or(&DEFAULT_NAMESPACE.to_string()), | ||
flag_key = order.flag_key, | ||
); | ||
|
||
self.client.put(&path, Some(order)).await | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Deserialize)] | ||
pub struct Empty {} | ||
|
||
#[derive(Debug, Clone, Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct RolloutOrderRequest { | ||
#[serde(skip_serializing)] | ||
pub flag_key: String, | ||
#[serde(skip_serializing)] | ||
pub namespace_key: Option<String>, | ||
pub rollout_ids: Vec<String>, | ||
} | ||
|
||
#[derive(Debug, Default, Serialize)] | ||
pub struct RolloutGetRequest { | ||
pub id: String, | ||
pub namespace_key: Option<String>, | ||
pub flag_key: String, | ||
} | ||
|
||
#[derive(Debug, Default, Serialize)] | ||
pub struct RolloutCreateRequest { | ||
#[serde(skip_serializing)] | ||
pub namespace_key: Option<String>, | ||
#[serde(skip_serializing)] | ||
pub flag_key: String, | ||
pub rank: usize, | ||
pub description: String, | ||
pub threshold: Option<RolloutThreshold>, | ||
pub segment: Option<RolloutSegment>, | ||
} | ||
|
||
#[derive(Debug, Default, Serialize)] | ||
pub struct RolloutUpdateRequest { | ||
#[serde(skip_serializing)] | ||
pub id: String, | ||
#[serde(skip_serializing)] | ||
pub namespace_key: Option<String>, | ||
#[serde(skip_serializing)] | ||
pub flag_key: String, | ||
pub rank: u32, | ||
pub description: String, | ||
} | ||
|
||
#[derive(Debug, Default, Serialize)] | ||
pub struct RolloutDeleteRequest { | ||
pub namespace_key: Option<String>, | ||
pub flag_key: String, | ||
pub id: String, | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Deserialize)] | ||
pub enum RolloutType { | ||
#[serde(rename = "UNKNOWN_ROLLOUT_TYPE")] | ||
Unknown, | ||
#[serde(rename = "SEGMENT_ROLLOUT_TYPE")] | ||
Segment, | ||
#[serde(rename = "THRESHOLD_ROLLOUT_TYPE")] | ||
Threshold, | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct Rollout { | ||
pub id: String, | ||
pub rank: u32, | ||
#[serde(rename = "type")] | ||
pub rollout_type: RolloutType, | ||
pub description: String, | ||
pub created_at: DateTime<Utc>, | ||
pub updated_at: DateTime<Utc>, | ||
pub threshold: Option<RolloutThreshold>, | ||
pub segment: Option<RolloutSegment>, | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] | ||
pub struct RolloutThreshold { | ||
pub percentage: f32, | ||
pub value: bool, | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct RolloutSegment { | ||
pub segment_key: String, | ||
pub value: bool, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
use crate::api::{ApiClient, Result}; | ||
use chrono::{DateTime, Utc}; | ||
use serde::{Deserialize, Serialize}; | ||
use std::collections::HashMap; | ||
|
||
pub struct EvaluationClient<'client> { | ||
client: &'client ApiClient, | ||
} | ||
|
||
impl<'client> EvaluationClient<'client> { | ||
pub fn new(client: &'client ApiClient) -> Self { | ||
Self { client } | ||
} | ||
|
||
pub async fn boolean(&self, eval: &EvaluateRequest) -> Result<BooleanEvaluation> { | ||
let path = "/evaluate/v1/boolean".to_string(); | ||
|
||
self.client.post(&path, Some(eval)).await | ||
} | ||
|
||
pub async fn variant(&self, eval: &EvaluateRequest) -> Result<VariantEvaluation> { | ||
let path = "/evaluate/v1/variant".to_string(); | ||
|
||
self.client.post(&path, Some(eval)).await | ||
} | ||
|
||
pub async fn batch(&self, batch: &BatchEvaluateRequest) -> Result<BatchEvaluation> { | ||
let path = "/evaluate/v1/batch".to_string(); | ||
|
||
self.client.post(&path, Some(batch)).await | ||
} | ||
} | ||
|
||
#[derive(Debug, Default, Serialize)] | ||
pub struct BatchEvaluateRequest { | ||
pub requests: Vec<EvaluateRequest>, | ||
} | ||
|
||
#[derive(Debug, Default, Clone, Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct EvaluateRequest { | ||
pub context: HashMap<String, String>, | ||
pub entity_id: String, | ||
pub namespace_key: String, | ||
pub flag_key: String, | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct BooleanEvaluation { | ||
pub enabled: bool, | ||
pub reason: Reason, | ||
pub request_id: String, | ||
pub request_duration_millis: f64, | ||
pub timestamp: DateTime<Utc>, | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct VariantEvaluation { | ||
#[serde(rename = "match")] | ||
pub is_match: bool, | ||
pub segment_keys: Vec<String>, | ||
pub reason: Reason, | ||
pub variant_key: String, | ||
pub variant_attachment: String, | ||
pub request_id: String, | ||
pub request_duration_millis: f64, | ||
pub timestamp: DateTime<Utc>, | ||
} | ||
|
||
#[derive(Debug, Clone, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct ErrorEvaluation { | ||
pub flag_key: String, | ||
pub namespace_key: String, | ||
pub reason: ErrorEvaluationReason, | ||
} | ||
|
||
#[derive(Debug, Clone, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct BatchEvaluation { | ||
pub request_id: String, | ||
pub responses: Vec<Response>, | ||
pub request_duration_millis: f64, | ||
} | ||
|
||
#[derive(Debug, Clone, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct Response { | ||
pub r#type: ResponseType, | ||
pub boolean_response: Option<BooleanEvaluation>, | ||
pub variant_response: Option<VariantEvaluation>, | ||
pub error_response: Option<ErrorEvaluation>, | ||
} | ||
|
||
#[derive(Debug, Clone, Deserialize)] | ||
pub enum ResponseType { | ||
#[serde(rename = "VARIANT_EVALUATION_RESPONSE_TYPE")] | ||
Variant, | ||
#[serde(rename = "BOOLEAN_EVALUATION_RESPONSE_TYPE")] | ||
Boolean, | ||
#[serde(rename = "ERROR_EVALUATION_RESPONSE_TYPE")] | ||
Error, | ||
} | ||
|
||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize)] | ||
pub enum ErrorEvaluationReason { | ||
#[serde(rename = "UNKNOWN_ERROR_EVALUATION_REASON")] | ||
Unknown, | ||
#[serde(rename = "NOT_FOUND_ERROR_EVALUATION_REASON")] | ||
NotFound, | ||
} | ||
|
||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize)] | ||
pub enum Reason { | ||
#[serde(rename = "UNKNOWN_EVALUATION_REASON")] | ||
Unknown, | ||
#[serde(rename = "FLAG_DISABLED_EVALUATION_REASON")] | ||
FlagDisabled, | ||
#[serde(rename = "MATCH_EVALUATION_REASON")] | ||
Match, | ||
#[serde(rename = "DEFAULT_EVALUATION_REASON")] | ||
Default, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
pub mod api; | ||
pub mod auth; | ||
pub mod error; | ||
pub mod evaluation; | ||
pub mod meta; | ||
|
||
use anyhow::Result; | ||
|
Oops, something went wrong.