Skip to content

Commit

Permalink
chore(flags): reorganized the modules (#26297)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmarticus authored Nov 19, 2024
1 parent f348721 commit d4556c9
Show file tree
Hide file tree
Showing 36 changed files with 298 additions and 303 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::net::IpAddr;

use crate::{
api::{FlagError, FlagsResponse},
request_handler::{process_request, FlagsQueryParams, RequestContext},
api::errors::FlagError,
api::handler::{process_request, FlagsQueryParams, RequestContext},
api::types::FlagsResponse,
router,
};
// TODO: stream this instead
Expand Down
Original file line number Diff line number Diff line change
@@ -1,60 +1,9 @@
use std::collections::HashMap;

use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::database::CustomDatabaseError;
use crate::redis::CustomRedisError;

#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum FlagsResponseCode {
Ok = 1,
}

#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum FlagValue {
Boolean(bool),
String(String),
}

// TODO the following two types are kinda general, maybe we should move them to a shared module
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BooleanOrStringObject {
Boolean(bool),
Object(HashMap<String, String>),
}

#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BooleanOrBooleanObject {
Boolean(bool),
Object(HashMap<String, bool>),
}

#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FlagsResponse {
pub error_while_computing_flags: bool,
pub feature_flags: HashMap<String, FlagValue>,
// TODO support the other fields in the payload
// pub config: HashMap<String, bool>,
// pub toolbar_params: HashMap<String, String>,
// pub is_authenticated: bool,
// pub supported_compression: Vec<String>,
// pub session_recording: bool,
// pub feature_flag_payloads: HashMap<String, String>,
// pub capture_performance: BooleanOrBooleanObject,
// #[serde(rename = "autocapture_opt_out")]
// pub autocapture_opt_out: bool,
// pub autocapture_exceptions: BooleanOrStringObject,
// pub surveys: bool,
// pub heatmaps: bool,
// pub site_apps: Vec<String>,
}
use crate::client::database::CustomDatabaseError;
use crate::client::redis::CustomRedisError;

#[derive(Error, Debug)]
pub enum ClientFacingError {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::{
api::{FlagError, FlagsResponse},
cohort_cache::CohortCacheManager,
database::Client,
flag_definitions::FeatureFlagList,
flag_matching::{FeatureFlagMatcher, GroupTypeMappingCache},
flag_request::FlagRequest,
geoip::GeoIpClient,
api::errors::FlagError,
api::types::FlagsResponse,
client::database::Client,
client::geoip::GeoIpClient,
cohort::cohort_cache_manager::CohortCacheManager,
flags::flag_matching::{FeatureFlagMatcher, GroupTypeMappingCache},
flags::flag_models::FeatureFlagList,
flags::flag_request::FlagRequest,
router,
};
use axum::{extract::State, http::HeaderMap};
Expand Down Expand Up @@ -254,10 +255,13 @@ fn decompress_gzip(compressed: Bytes) -> Result<Bytes, FlagError> {
#[cfg(test)]
mod tests {
use crate::{
api::FlagValue,
api::types::FlagValue,
config::Config,
flag_definitions::{FeatureFlag, FlagFilters, FlagGroupType, OperatorType, PropertyFilter},
test_utils::{insert_new_team_in_pg, setup_pg_reader_client, setup_pg_writer_client},
flags::flag_models::{FeatureFlag, FlagFilters, FlagGroupType},
properties::property_models::{OperatorType, PropertyFilter},
utils::test_utils::{
insert_new_team_in_pg, setup_pg_reader_client, setup_pg_writer_client,
},
};

use super::*;
Expand Down
4 changes: 4 additions & 0 deletions rust/feature-flags/src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod endpoint;
pub mod errors;
pub mod handler;
pub mod types;
21 changes: 21 additions & 0 deletions rust/feature-flags/src/api/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum FlagsResponseCode {
Ok = 1,
}

#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum FlagValue {
Boolean(bool),
String(String),
}

#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FlagsResponse {
pub error_while_computing_flags: bool,
pub feature_flags: HashMap<String, FlagValue>,
}
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions rust/feature-flags/src/client/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod database;
pub mod geoip;
pub mod redis;
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::api::FlagError;
use crate::cohort_models::Cohort;
use crate::flag_matching::{PostgresReader, TeamId};
use crate::api::errors::FlagError;
use crate::cohort::cohort_models::Cohort;
use crate::flags::flag_matching::{PostgresReader, TeamId};
use moka::future::Cache;
use std::time::Duration;

Expand Down Expand Up @@ -74,8 +74,8 @@ impl CohortCacheManager {
#[cfg(test)]
mod tests {
use super::*;
use crate::cohort_models::Cohort;
use crate::test_utils::{
use crate::cohort::cohort_models::Cohort;
use crate::utils::test_utils::{
insert_cohort_for_team_in_pg, insert_new_team_in_pg, setup_pg_reader_client,
setup_pg_writer_client,
};
Expand All @@ -84,15 +84,15 @@ mod tests {

/// Helper function to setup a new team for testing.
async fn setup_test_team(
writer_client: Arc<dyn crate::database::Client + Send + Sync>,
writer_client: Arc<dyn crate::client::database::Client + Send + Sync>,
) -> Result<TeamId, anyhow::Error> {
let team = crate::test_utils::insert_new_team_in_pg(writer_client, None).await?;
let team = insert_new_team_in_pg(writer_client, None).await?;
Ok(team.id)
}

/// Helper function to insert a cohort for a team.
async fn setup_test_cohort(
writer_client: Arc<dyn crate::database::Client + Send + Sync>,
writer_client: Arc<dyn crate::client::database::Client + Send + Sync>,
team_id: TeamId,
name: Option<String>,
) -> Result<Cohort, anyhow::Error> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::flag_definitions::PropertyFilter;
use crate::properties::property_models::PropertyFilter;
use serde::{Deserialize, Serialize};
use sqlx::FromRow;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ use std::collections::HashSet;
use std::sync::Arc;
use tracing::instrument;

use crate::cohort_models::{Cohort, CohortId, CohortProperty, InnerCohortProperty};
use crate::{api::FlagError, database::Client as DatabaseClient, flag_definitions::PropertyFilter};
use crate::cohort::cohort_models::{Cohort, CohortId, CohortProperty, InnerCohortProperty};
use crate::{
api::errors::FlagError, client::database::Client as DatabaseClient,
properties::property_models::PropertyFilter,
};

impl Cohort {
/// Returns a cohort from postgres given a cohort_id and team_id
Expand Down Expand Up @@ -185,8 +188,8 @@ impl InnerCohortProperty {
mod tests {
use super::*;
use crate::{
cohort_models::{CohortPropertyType, CohortValues},
test_utils::{
cohort::cohort_models::{CohortPropertyType, CohortValues},
utils::test_utils::{
insert_cohort_for_team_in_pg, insert_new_team_in_pg, setup_pg_reader_client,
setup_pg_writer_client,
},
Expand Down
3 changes: 3 additions & 0 deletions rust/feature-flags/src/cohort/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod cohort_cache_manager;
pub mod cohort_models;
pub mod cohort_operations;
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use anyhow::Result;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};

use crate::flag_request::FlagRequestType;
use crate::redis::{Client as RedisClient, CustomRedisError};
use crate::client::redis::{Client as RedisClient, CustomRedisError};
use crate::flags::flag_request::FlagRequestType;

const CACHE_BUCKET_SIZE: u64 = 60 * 2; // duration in seconds

Expand Down Expand Up @@ -37,7 +37,7 @@ pub async fn increment_request_count(
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::setup_redis_client;
use crate::utils::test_utils::setup_redis_client;

#[tokio::test]
async fn test_get_team_request_key() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use crate::{
api::{FlagError, FlagValue, FlagsResponse},
cohort_cache::CohortCacheManager,
cohort_models::{Cohort, CohortId},
database::Client as DatabaseClient,
feature_flag_match_reason::FeatureFlagMatchReason,
flag_definitions::{FeatureFlag, FeatureFlagList, FlagGroupType, OperatorType, PropertyFilter},
metrics_consts::{FLAG_EVALUATION_ERROR_COUNTER, FLAG_HASH_KEY_WRITES_COUNTER},
metrics_utils::parse_exception_for_prometheus_label,
property_matching::match_property,
};
use crate::api::errors::FlagError;
use crate::api::types::{FlagValue, FlagsResponse};
use crate::client::database::Client as DatabaseClient;
use crate::cohort::cohort_cache_manager::CohortCacheManager;
use crate::cohort::cohort_models::{Cohort, CohortId};
use crate::flags::flag_match_reason::FeatureFlagMatchReason;
use crate::flags::flag_models::{FeatureFlag, FeatureFlagList, FlagGroupType};
use crate::metrics::metrics_consts::{FLAG_EVALUATION_ERROR_COUNTER, FLAG_HASH_KEY_WRITES_COUNTER};
use crate::metrics::metrics_utils::parse_exception_for_prometheus_label;
use crate::properties::property_matching::match_property;
use crate::properties::property_models::{OperatorType, PropertyFilter};
use anyhow::Result;
use common_metrics::inc;
use petgraph::algo::{is_cyclic_directed, toposort};
Expand Down Expand Up @@ -1796,11 +1796,11 @@ mod tests {

use super::*;
use crate::{
flag_definitions::{
flags::flag_models::{
FeatureFlagRow, FlagFilters, MultivariateFlagOptions, MultivariateFlagVariant,
OperatorType,
},
test_utils::{
properties::property_models::OperatorType,
utils::test_utils::{
add_person_to_cohort, get_person_id_by_distinct_id, insert_cohort_for_team_in_pg,
insert_flag_for_team_in_pg, insert_new_team_in_pg, insert_person_for_team_in_pg,
setup_pg_reader_client, setup_pg_writer_client,
Expand Down
70 changes: 70 additions & 0 deletions rust/feature-flags/src/flags/flag_models.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use serde::{Deserialize, Serialize};

use crate::properties::property_models::PropertyFilter;

// TRICKY: This cache data is coming from django-redis. If it ever goes out of sync, we'll bork.
// TODO: Add integration tests across repos to ensure this doesn't happen.
pub const TEAM_FLAGS_CACHE_PREFIX: &str = "posthog:1:team_feature_flags_";

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct FlagGroupType {
pub properties: Option<Vec<PropertyFilter>>,
pub rollout_percentage: Option<f64>,
pub variant: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MultivariateFlagVariant {
pub key: String,
pub name: Option<String>,
pub rollout_percentage: f64,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MultivariateFlagOptions {
pub variants: Vec<MultivariateFlagVariant>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct FlagFilters {
pub groups: Vec<FlagGroupType>,
pub multivariate: Option<MultivariateFlagOptions>,
pub aggregation_group_type_index: Option<i32>,
pub payloads: Option<serde_json::Value>,
pub super_groups: Option<Vec<FlagGroupType>>,
}

// TODO: see if you can combine these two structs, like we do with cohort models
// this will require not deserializing on read and instead doing it lazily, on-demand
// (which, tbh, is probably a better idea)
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct FeatureFlag {
pub id: i32,
pub team_id: i32,
pub name: Option<String>,
pub key: String,
pub filters: FlagFilters,
#[serde(default)]
pub deleted: bool,
#[serde(default)]
pub active: bool,
#[serde(default)]
pub ensure_experience_continuity: bool,
}

#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct FeatureFlagRow {
pub id: i32,
pub team_id: i32,
pub name: Option<String>,
pub key: String,
pub filters: serde_json::Value,
pub deleted: bool,
pub active: bool,
pub ensure_experience_continuity: bool,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct FeatureFlagList {
pub flags: Vec<FeatureFlag>,
}
Loading

0 comments on commit d4556c9

Please sign in to comment.