From 181f3597c4966571abb37ac706e72674306f866c Mon Sep 17 00:00:00 2001 From: "ayush.jain@juspay.in" Date: Mon, 16 Mar 2026 13:32:21 +0530 Subject: [PATCH 1/8] fix: Spec for list exp and get config endpoints --- Cargo.lock | 1 - crates/context_aware_config/Cargo.toml | 1 - .../src/api/config/handlers.rs | 13 +- .../src/api/config/helpers.rs | 39 +--- .../src/api/context/handlers.rs | 9 +- .../src/api/experiment_groups/handlers.rs | 169 +++++++++++++++--- .../src/api/experiments/handlers.rs | 43 +++-- crates/frontend/src/api.rs | 6 +- .../src/components/experiment_form.rs | 3 +- .../src/pages/experiment_group_listing.rs | 32 +++- .../pages/experiment_group_listing/filter.rs | 153 ++++++++++++++-- crates/service_utils/src/helpers.rs | 1 - crates/service_utils/src/redis.rs | 2 + crates/service_utils/src/service/types.rs | 43 ++++- crates/superposition/src/resolve/handlers.rs | 7 +- .../src/api/experiment_groups.rs | 18 +- .../src/api/experiments.rs | 5 + .../src/database/models/experimentation.rs | 6 + smithy/models/config.smithy | 33 +--- smithy/models/experiment_groups.smithy | 19 +- smithy/models/experiments.smithy | 17 +- 21 files changed, 449 insertions(+), 171 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 49eb9f3d6..8cec80425 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1683,7 +1683,6 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" name = "context_aware_config" version = "0.101.0" dependencies = [ - "actix-http", "actix-web", "anyhow", "bigdecimal", diff --git a/crates/context_aware_config/Cargo.toml b/crates/context_aware_config/Cargo.toml index a735e9563..067d0cd9d 100644 --- a/crates/context_aware_config/Cargo.toml +++ b/crates/context_aware_config/Cargo.toml @@ -9,7 +9,6 @@ rust-version.workspace = true [dependencies] -actix-http = { workspace = true } actix-web = { workspace = true } anyhow = { workspace = true } bigdecimal = { workspace = true } diff --git a/crates/context_aware_config/src/api/config/handlers.rs b/crates/context_aware_config/src/api/config/handlers.rs index 7debd6d39..9d96e4f1c 100644 --- a/crates/context_aware_config/src/api/config/handlers.rs +++ b/crates/context_aware_config/src/api/config/handlers.rs @@ -11,7 +11,7 @@ use serde_json::{Map, Value, json}; use service_utils::{ helpers::fetch_dimensions_info_map, redis::{CONFIG_KEY_SUFFIX, LAST_MODIFIED_KEY_SUFFIX, read_through_cache}, - service::types::{AppState, DbConnection, WorkspaceContext}, + service::types::{AppHeader, AppState, DbConnection, WorkspaceContext}, }; use superposition_core::{ ConfigFormat, JsonFormat, TomlFormat, @@ -43,7 +43,6 @@ use superposition_types::{ use crate::{ api::{ config::helpers::{ - add_config_version_to_header, add_last_modified_to_header, generate_config_from_version, get_config_version, get_max_created_at, is_not_modified, }, @@ -541,8 +540,8 @@ async fn get_handler( if !context.is_empty() { config = config.filter_by_dimensions(&context); } - add_last_modified_to_header(max_created_at, is_smithy, &mut response); - add_config_version_to_header(&Some(version), &mut response); + AppHeader::add_last_modified(max_created_at, is_smithy, &mut response); + AppHeader::add_config_version(&Some(version), &mut response); Ok(response.json(config)) } @@ -574,7 +573,7 @@ async fn get_toml_handler( .map_err(|e| unexpected_error!("Failed to serialize config to TOML: {}", e))?; let mut response = HttpResponse::Ok(); - add_last_modified_to_header(max_created_at, false, &mut response); + AppHeader::add_last_modified(max_created_at, false, &mut response); response.insert_header(("Content-Type", "application/toml")); Ok(response.body(toml_str)) @@ -692,8 +691,8 @@ async fn resolve_handler( }; let mut resp = HttpResponse::Ok(); - add_last_modified_to_header(max_created_at, is_smithy, &mut resp); - add_config_version_to_header(&Some(config_version), &mut resp); + AppHeader::add_last_modified(max_created_at, is_smithy, &mut resp); + AppHeader::add_config_version(&Some(config_version), &mut resp); Ok(resp.json(resolved_config)) } diff --git a/crates/context_aware_config/src/api/config/helpers.rs b/crates/context_aware_config/src/api/config/helpers.rs index 84af4b414..4a5ae2dde 100644 --- a/crates/context_aware_config/src/api/config/helpers.rs +++ b/crates/context_aware_config/src/api/config/helpers.rs @@ -1,6 +1,5 @@ -use actix_http::header::HeaderValue; use actix_web::{ - HttpRequest, HttpResponseBuilder, + HttpRequest, web::{Data, Header, Json}, }; use cac_client::{eval_cac, eval_cac_with_reasoning}; @@ -9,7 +8,7 @@ use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, dsl::max}; use serde_json::{Map, Value}; use service_utils::{ redis::{CONFIG_VERSION_KEY_SUFFIX, read_through_cache}, - service::types::{AppHeader, AppState, EncryptionKey, SchemaName, WorkspaceContext}, + service::types::{AppState, EncryptionKey, SchemaName, WorkspaceContext}, }; use superposition_macros::{bad_argument, db_error, unexpected_error}; use superposition_types::{ @@ -70,40 +69,6 @@ pub async fn get_config_version( } } -pub fn add_last_modified_to_header( - max_created_at: Option>, - is_smithy: bool, - resp_builder: &mut HttpResponseBuilder, -) { - if let Some(date) = max_created_at { - let value = if is_smithy { - // Smithy needs to be in this format otherwise they can't - // deserialize it. - HeaderValue::from_str(date.to_rfc3339().as_str()) - } else { - HeaderValue::from_str(date.to_rfc2822().as_str()) - }; - if let Ok(header_value) = value { - resp_builder - .insert_header((AppHeader::LastModified.to_string(), header_value)); - } else { - log::error!("failed parsing datetime_utc {:?}", value); - } - } -} - -pub fn add_config_version_to_header( - config_version: &Option, - resp_builder: &mut HttpResponseBuilder, -) { - if let Some(val) = config_version { - resp_builder.insert_header(( - AppHeader::XConfigVersion.to_string(), - val.clone().to_string(), - )); - } -} - pub fn get_max_created_at( conn: &mut DBConnection, schema_name: &SchemaName, diff --git a/crates/context_aware_config/src/api/context/handlers.rs b/crates/context_aware_config/src/api/context/handlers.rs index 9ecc25dc4..83dc7cf70 100644 --- a/crates/context_aware_config/src/api/context/handlers.rs +++ b/crates/context_aware_config/src/api/context/handlers.rs @@ -770,18 +770,13 @@ async fn bulk_operations_handler( use contexts::dsl::contexts; let DbConnection(mut conn) = db_conn; - let mut is_v2 = false; + let is_v2 = matches!(req, Either::Right(_)); let ops = match req { Either::Left(o) => o.into_inner(), - Either::Right(bo) => { - is_v2 = true; - bo.into_inner().operations - } + Either::Right(bo) => bo.into_inner().operations, }; bulk_authorized(&_auth_z, &ops, &workspace_context.schema_name, &mut conn).await?; - // Marking immutable. - let is_v2 = is_v2; let mut all_change_reasons = Vec::new(); let mut webhook_actions: Vec = Vec::new(); let mut webhook_contexts: Vec = Vec::new(); diff --git a/crates/experimentation_platform/src/api/experiment_groups/handlers.rs b/crates/experimentation_platform/src/api/experiment_groups/handlers.rs index 52e1514ef..30fe18871 100644 --- a/crates/experimentation_platform/src/api/experiment_groups/handlers.rs +++ b/crates/experimentation_platform/src/api/experiment_groups/handlers.rs @@ -1,8 +1,10 @@ +use std::cmp::min; + use actix_web::{ - Scope, delete, get, patch, post, + HttpRequest, HttpResponse, Scope, delete, get, patch, post, routes, web::{self, Data, Json}, }; -use chrono::Utc; +use chrono::{DateTime, Utc}; use diesel::{ Connection, ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper, TextExpressionMethods, @@ -10,19 +12,27 @@ use diesel::{ use serde_json::Value; use service_utils::{ db::run_query, - helpers::{generate_snowflake_id, get_from_env_or_default}, + helpers::{ + fetch_dimensions_info_map, generate_snowflake_id, get_from_env_or_default, + }, redis::{EXPERIMENT_GROUPS_LIST_KEY_SUFFIX, read_through_cache}, - service::types::{AppState, DbConnection, WorkspaceContext}, + service::types::{AppHeader, AppState, DbConnection, WorkspaceContext}, }; use superposition_derives::{authorized, declare_resource}; use superposition_macros::{bad_argument, unexpected_error}; use superposition_types::{ - DBConnection, IsEmpty, PaginatedResponse, SortBy, User, - api::experiment_groups::{ - ExpGroupCreateRequest, ExpGroupFilters, ExpGroupMemberRequest, - ExpGroupUpdateRequest, SortOn, + Contextual, DBConnection, IsEmpty, PaginatedResponse, SortBy, User, + api::{ + DimensionMatchStrategy, + experiment_groups::{ + ExpGroupCreateRequest, ExpGroupFilters, ExpGroupListRequest, + ExpGroupMemberRequest, ExpGroupUpdateRequest, SortOn, + }, + }, + custom_query::{ + self as superposition_query, CustomQuery, DimensionQuery, PaginationParams, + QueryMap, }, - custom_query::{self as superposition_query, CustomQuery, PaginationParams}, database::{ models::{ ChangeReason, @@ -32,9 +42,11 @@ use superposition_types::{ }, }, schema::{ - experiment_groups::dsl as experiment_groups, experiments::dsl as experiments, + event_log, experiment_groups::dsl as experiment_groups, + experiments::dsl as experiments, }, }, + logic::evaluate_local_cohorts_skip_unresolved, result as superposition, }; @@ -307,19 +319,70 @@ async fn remove_members_handler( Ok(experiment_group) } +#[allow(clippy::too_many_arguments)] #[authorized] +#[routes] #[get("")] +#[post("/list")] async fn list_handler( workspace_context: WorkspaceContext, pagination_params: superposition_query::Query, filters: superposition_query::Query, + dimension_params: DimensionQuery, + body: Option>, state: Data, -) -> superposition::Result>> { + req: HttpRequest, +) -> superposition::Result { + let max_event_timestamp = read_through_cache::>>( + format!( + "{}{EXPERIMENT_GROUPS_LIST_KEY_SUFFIX}", + *workspace_context.schema_name + ), + &workspace_context.schema_name, + &state.redis, + &state.db_pool, + |conn| { + event_log::table + .filter(event_log::table_name.eq("experiment_groups")) + .select(diesel::dsl::max(event_log::timestamp)) + .schema_name(&workspace_context.schema_name) + .first(conn) + }, + ) + .await?; + + let last_modified = req + .headers() + .get("If-Modified-Since") + .and_then(|header_val| header_val.to_str().ok()) + .and_then(|header_str| { + DateTime::parse_from_rfc2822(header_str) + .map(|datetime| datetime.with_timezone(&Utc)) + .ok() + }); + + if max_event_timestamp.is_some() && max_event_timestamp < last_modified { + return Ok(HttpResponse::NotModified().finish()); + }; + + let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); + let dimension_params = if req.method() == actix_web::http::Method::GET { + dimension_params.into_inner() + } else { + body.and_then(|b| b.into_inner().context) + .map(Into::into) + .unwrap_or_default() + }; + let key = format!( "{}{}", *workspace_context.schema_name, EXPERIMENT_GROUPS_LIST_KEY_SUFFIX ); let read_from_redis = pagination_params.all.is_some_and(|e| e) && filters.is_empty(); + + let mut response = HttpResponse::Ok(); + AppHeader::add_last_modified(max_event_timestamp, is_smithy, &mut response); + if read_from_redis { read_through_cache::>( key, @@ -330,30 +393,31 @@ async fn list_handler( list_experiment_groups_db( &pagination_params, filters, + dimension_params, conn, &workspace_context, ) }, ) .await - .map(Json) - .map_err(|e| unexpected_error!(e)) } else { run_query(&state.db_pool, |conn| { list_experiment_groups_db( &pagination_params, filters, + dimension_params, conn, &workspace_context, ) }) - .map(Json) } + .map(|r| response.json(r)) } fn list_experiment_groups_db( pagination_params: &superposition_query::Query, filters: superposition_query::Query, + dimension_params: QueryMap, conn: &mut DBConnection, workspace_context: &WorkspaceContext, ) -> superposition::DieselResult> { @@ -383,6 +447,10 @@ fn list_experiment_groups_db( let count_query = query_builder(&filters); let sort_by = filters.sort_by.unwrap_or(SortBy::Desc); let sort_on = filters.sort_on.unwrap_or_default(); + let show_all = pagination_params.all.unwrap_or_default(); + let limit = pagination_params.count.unwrap_or(10); + let offset = (pagination_params.page.unwrap_or(1) - 1) * limit; + #[rustfmt::skip] let base_query = match (sort_on, sort_by) { (SortOn::LastModifiedAt, SortBy::Desc) => base_query.order(experiment_groups::last_modified_at.desc()), @@ -392,22 +460,67 @@ fn list_experiment_groups_db( (SortOn::Name, SortBy::Desc) => base_query.order(experiment_groups::name.desc()), (SortOn::Name, SortBy::Asc) => base_query.order(experiment_groups::name.asc()), }; - if let Some(true) = pagination_params.all { + + let perform_in_memory_filter = !dimension_params.is_empty(); + + let paginated_response = if perform_in_memory_filter { + let all_experiments: Vec = base_query.load(conn)?; + + let dimensions_info = + fetch_dimensions_info_map(conn, &workspace_context.schema_name)?; + let dimension_params = + evaluate_local_cohorts_skip_unresolved(&dimensions_info, &dimension_params); + let dimension_keys = dimension_params.keys().cloned().collect::>(); + + let filter_fn = match filters.dimension_match_strategy.unwrap_or_default() { + DimensionMatchStrategy::Exact => ExperimentGroup::filter_exact_match, + DimensionMatchStrategy::Subset => ExperimentGroup::filter_by_eval, + }; + + let dimension_filtered_experiments = + filter_fn(all_experiments, &dimension_params); + + let filtered_experiments = ExperimentGroup::filter_by_dimension( + dimension_filtered_experiments, + &dimension_keys, + ); + + if show_all { + PaginatedResponse::all(filtered_experiments) + } else { + let total_items = filtered_experiments.len(); + let start = offset as usize; + let end = min((offset + limit) as usize, total_items); + let data = filtered_experiments + .get(start..end) + .map(|slice| slice.to_vec()) + .unwrap_or_default(); + + PaginatedResponse { + total_pages: (total_items as f64 / limit as f64).ceil() as i64, + total_items: total_items as i64, + data, + } + } + } else if show_all { let result: ExperimentGroups = base_query.get_results::(conn)?; - return Ok(PaginatedResponse::all(result)); - } - let total_items = count_query.count().get_result(conn)?; - let limit = pagination_params.count.unwrap_or(10); - let offset = (pagination_params.page.unwrap_or(1) - 1) * limit; + PaginatedResponse::all(result) + } else { + let total_items = count_query.count().get_result(conn)?; + let limit = pagination_params.count.unwrap_or(10); + let offset = (pagination_params.page.unwrap_or(1) - 1) * limit; + + let query = base_query.limit(limit).offset(offset); + let data = query.load::(conn)?; + let total_pages = (total_items as f64 / limit as f64).ceil() as i64; + PaginatedResponse { + total_pages, + total_items, + data, + } + }; - let query = base_query.limit(limit).offset(offset); - let data = query.load::(conn)?; - let total_pages = (total_items as f64 / limit as f64).ceil() as i64; - Ok(PaginatedResponse { - total_pages, - total_items, - data, - }) + Ok(paginated_response) } #[authorized] diff --git a/crates/experimentation_platform/src/api/experiments/handlers.rs b/crates/experimentation_platform/src/api/experiments/handlers.rs index 04dc08dc1..f19e3e383 100644 --- a/crates/experimentation_platform/src/api/experiments/handlers.rs +++ b/crates/experimentation_platform/src/api/experiments/handlers.rs @@ -53,8 +53,8 @@ use superposition_types::{ experiments::{ ApplicableVariantsQuery, ApplicableVariantsRequest, ConcludeExperimentRequest, ExperimentCreateRequest, ExperimentListFilters, - ExperimentResponse, ExperimentSortOn, ExperimentStateChangeRequest, - OverrideKeysUpdateRequest, RampRequest, + ExperimentListRequest, ExperimentResponse, ExperimentSortOn, + ExperimentStateChangeRequest, OverrideKeysUpdateRequest, RampRequest, }, webhook::Action, }, @@ -1019,11 +1019,15 @@ async fn get_applicable_variants_handler( } } +#[allow(clippy::too_many_arguments)] #[authorized] +#[routes] #[get("")] +#[post("/list")] async fn list_handler( workspace_context: WorkspaceContext, req: HttpRequest, + body: Option>, pagination_params: superposition_query::Query, filters: superposition_query::Query, dimension_params: DimensionQuery, @@ -1061,14 +1065,28 @@ async fn list_handler( return Ok(HttpResponse::NotModified().finish()); }; let show_all = pagination_params.all.unwrap_or_default(); + + let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); + let dimension_params = if req.method() == actix_web::http::Method::GET { + dimension_params.into_inner() + } else { + body.and_then(|b| b.into_inner().context) + .map(Into::into) + .unwrap_or_default() + }; + let read_from_redis = show_all && filters .status .clone() .is_some_and(|v| *v == ExperimentStatusType::active_list()) && dimension_params.is_empty(); + + let mut response = HttpResponse::Ok(); + AppHeader::add_last_modified(max_event_timestamp, is_smithy, &mut response); + if read_from_redis { - let response = read_through_cache::>( + read_through_cache::>( format!( "{}{EXPERIMENTS_LIST_KEY_SUFFIX}", *workspace_context.schema_name @@ -1078,18 +1096,17 @@ async fn list_handler( &state.db_pool, |conn| { list_experiments_db( - pagination_params.clone(), - filters.clone(), - dimension_params.clone(), + pagination_params, + filters, + dimension_params, conn, &workspace_context, ) }, ) - .await?; - Ok(HttpResponse::Ok().json(response)) + .await } else { - let paginated_response = run_query(&state.db_pool, |conn| { + run_query(&state.db_pool, |conn| { list_experiments_db( pagination_params, filters, @@ -1097,20 +1114,18 @@ async fn list_handler( conn, &workspace_context, ) - })?; - Ok(HttpResponse::Ok().json(paginated_response)) + }) } + .map(|r| response.json(r)) } fn list_experiments_db( pagination_params: superposition_query::Query, filters: superposition_query::Query, - dimension_params: DimensionQuery, + dimension_params: QueryMap, conn: &mut DBConnection, workspace_context: &WorkspaceContext, ) -> superposition::DieselResult> { - let dimension_params = dimension_params.into_inner(); - let query_builder = |filters: &ExperimentListFilters| { let mut builder = experiments::experiments .schema_name(&workspace_context.schema_name) diff --git a/crates/frontend/src/api.rs b/crates/frontend/src/api.rs index 05126219d..8257a7a21 100644 --- a/crates/frontend/src/api.rs +++ b/crates/frontend/src/api.rs @@ -1429,16 +1429,18 @@ pub mod experiment_groups { pub async fn list( filters: &ExpGroupFilters, pagination: &PaginationParams, + dimension_params: &DimensionQuery, workspace: &str, org_id: &str, ) -> Result, String> { let host = use_host_server(); let url = format!( - "{}/experiment-groups?{}&{}", + "{}/experiment-groups?{}&{}&{}", host, filters.to_query_param(), - pagination.to_query_param() + pagination.to_query_param(), + dimension_params.to_query_param() ); let response = request::<()>( url, diff --git a/crates/frontend/src/components/experiment_form.rs b/crates/frontend/src/components/experiment_form.rs index df5a5563c..419bc8afc 100644 --- a/crates/frontend/src/components/experiment_form.rs +++ b/crates/frontend/src/components/experiment_form.rs @@ -12,7 +12,7 @@ use superposition_types::{ experiments::{ExperimentResponse, OverrideKeysUpdateRequest}, functions::FunctionEnvironment, }, - custom_query::PaginationParams, + custom_query::{DimensionQuery, PaginationParams}, database::models::{ Metrics, cac::DefaultConfig, @@ -118,6 +118,7 @@ pub fn ExperimentForm( experiment_groups::list( &ExpGroupFilters::default(), &PaginationParams::all_entries(), + &DimensionQuery::default(), &workspace, &org, ) diff --git a/crates/frontend/src/pages/experiment_group_listing.rs b/crates/frontend/src/pages/experiment_group_listing.rs index e75fb6529..75c559b03 100644 --- a/crates/frontend/src/pages/experiment_group_listing.rs +++ b/crates/frontend/src/pages/experiment_group_listing.rs @@ -13,7 +13,7 @@ use superposition_types::{ dimension::DimensionResponse, experiment_groups::{ExpGroupFilters, SortOn}, }, - custom_query::{CustomQuery, PaginationParams, Query}, + custom_query::{CustomQuery, DimensionQuery, PaginationParams, Query, QueryMap}, database::models::experimentation::ExperimentGroup, }; use web_sys::MouseEvent; @@ -231,11 +231,12 @@ pub fn ExperimentGroupListing() -> impl IntoView { let org = use_context::>().unwrap(); let delete_inprogress_rws = RwSignal::new(false); - let (filters_rws, pagination_params_rws) = + let (filters_rws, pagination_params_rws, dimension_params_rws) = use_signal_from_query(move |query_string| { ( Query::::extract_non_empty(query_string).into_inner(), Query::::extract_non_empty(query_string).into_inner(), + DimensionQuery::::extract_non_empty(query_string), ) }); @@ -246,6 +247,7 @@ pub fn ExperimentGroupListing() -> impl IntoView { ( filters_rws.get(), pagination_params_rws.get(), + dimension_params_rws.get(), workspace.get().0, org.get().0, ) @@ -253,11 +255,16 @@ pub fn ExperimentGroupListing() -> impl IntoView { let experiment_groups_resource = create_blocking_resource( source, - |(filters, pagination, workspace, org_id)| async move { - let experiment_groups = - experiment_groups::list(&filters, &pagination, &workspace, &org_id) - .await - .unwrap_or_default(); + |(filters, pagination, dimension_params, workspace, org_id)| async move { + let experiment_groups = experiment_groups::list( + &filters, + &pagination, + &dimension_params, + &workspace, + &org_id, + ) + .await + .unwrap_or_default(); let dimensions = dimensions::list(&PaginationParams::all_entries(), &workspace, &org_id) .await @@ -323,7 +330,14 @@ pub fn ExperimentGroupListing() -> impl IntoView { number=total_items />
- + impl IntoView {
} - }} + }}
{move || { diff --git a/crates/frontend/src/pages/experiment_group_listing/filter.rs b/crates/frontend/src/pages/experiment_group_listing/filter.rs index a48b20436..04b029818 100644 --- a/crates/frontend/src/pages/experiment_group_listing/filter.rs +++ b/crates/frontend/src/pages/experiment_group_listing/filter.rs @@ -1,21 +1,38 @@ -use std::{fmt::Display, str::FromStr}; +use std::{fmt::Display, ops::Deref, str::FromStr}; use leptos::*; +use serde_json::Map; use superposition_types::{ - api::experiment_groups::ExpGroupFilters, - custom_query::{CommaSeparatedQParams, PaginationParams}, + api::{ + DimensionMatchStrategy, experiment_groups::ExpGroupFilters, + functions::FunctionEnvironment, + }, + custom_query::{ + CommaSeparatedQParams, CustomQuery, DimensionQuery, PaginationParams, QueryMap, + }, }; use web_sys::MouseEvent; -use crate::components::{ - badge::{GlassyPills, GrayPill, ListPills}, - button::{Button, ButtonStyle}, - drawer::{Drawer, DrawerBtn, close_drawer}, - form::label::Label, +use crate::{ + components::{ + badge::{GlassyPills, GrayPill, ListPills}, + button::{Button, ButtonStyle}, + condition_pills::Condition, + context_form::ContextForm, + drawer::{Drawer, DrawerBtn, close_drawer}, + form::label::Label, + input::Toggle, + }, + logic::Conditions, + pages::experiment_group_listing::CombinedResource, + providers::condition_collapse_provider::ConditionCollapseProvider, }; #[component] -pub(super) fn FilterSummary(filters_rws: RwSignal) -> impl IntoView { +pub(super) fn FilterSummary( + filters_rws: RwSignal, + dimension_params_rws: RwSignal>, +) -> impl IntoView { let force_open_rws = RwSignal::new(true); // let force_open_rws = RwSignal::new(scrolled_to_top.get_untracked()); @@ -36,7 +53,8 @@ pub(super) fn FilterSummary(filters_rws: RwSignal) -> impl Into f.created_by.is_none() && f.name.is_none() && f.last_modified_by.is_none() && f.group_type.is_none() }); - !filters_empty + let dimension_params_empty = dimension_params_rws.with(|f| f.is_empty()); + !filters_empty || !dimension_params_empty }>
) -> impl Into if force_open_rws.get() { "max-h-[1000px]" } else { "max-h-0" }, ) }> + {move || { + if !dimension_params_rws.with(|d| d.is_empty()) { + let dimension_params = dimension_params_rws.get(); + let conditions = Conditions::from_iter( + dimension_params.clone().into_inner(), + ); + let condition_id = serde_json::to_string( + dimension_params.clone().into_inner().deref(), + ) + .unwrap_or_else(|_| "[]".to_string()); + view! { +
+
"Context"
+ + + +
+ +
+ "Exact match context" + +
+
+ } + .into_view() + } else { + ().into_view() + } + }} {move || { filters_rws .with(|f| f.name.clone()) @@ -150,8 +214,19 @@ pub(super) fn FilterSummary(filters_rws: RwSignal) -> impl Into pub(super) fn ExperimentGroupFilterWidget( pagination_params_rws: RwSignal, filters_rws: RwSignal, + dimension_params_rws: RwSignal>, + combined_resource: CombinedResource, ) -> impl IntoView { let filters_buffer_rws = RwSignal::new(filters_rws.get_untracked()); + let dimension_buffer_rws = RwSignal::new(dimension_params_rws.get_untracked()); + let context_rws = RwSignal::new(Conditions::from_iter( + dimension_params_rws.get_untracked().into_inner(), + )); + + let fn_environment = Memo::new(move |_| FunctionEnvironment { + context: context_rws.get().into(), + overrides: Map::new(), + }); view! {
-
+ {move || { + view! { + + } + }} + {move || { + view! { +
+ +
+ } + }}
-
+
-
+