use anyhow::bail;
use hugsqlx::{params, HugSqlx};
use sqlx::{Connection, Row, SqliteConnection};
use sqlx::{Pool, Sqlite};
use crate::errors::FlagrantError;
use flagrant_types::{Environment, Feature, Variant};
#[derive(HugSqlx)]
#[queries = "resources/db/queries/variants.sql"]
struct Variants {}
pub async fn upsert_default(
conn: &mut SqliteConnection,
environment: &Environment,
feature: &Feature,
value: String,
) -> anyhow::Result<Variant> {
let mut tx = conn.begin().await?;
let variant_id = Variants::upsert_default_variant(
&mut *tx,
params![environment.id, feature.id, &value],
|v| v.get("variant_id"),
)
.await
.map_err(|e| FlagrantError::QueryFailed("Could not upsert default variant", e))?;
upsert_default_weight(&mut tx, environment, feature.id).await?;
tx.commit().await?;
Ok(Variant::build_default(environment, variant_id, value))
}
pub async fn create(
pool: &Pool<Sqlite>,
environment: &Environment,
feature: &Feature,
value: String,
weight: i16,
) -> anyhow::Result<Variant> {
let mut tx = pool.begin().await?;
let variant_id = Variants::create_variant(&mut *tx, params!(feature.id, &value), |v| {
v.get("variant_id")
})
.await
.map_err(|e| FlagrantError::QueryFailed("Could not create a variant", e))?;
let weight = Variants::upsert_variant_weight(
&mut *tx,
params![environment.id, variant_id, weight],
|v| v.get("weight"),
)
.await
.map_err(|e| FlagrantError::QueryFailed("Could not insert a variant weight", e))?;
upsert_default_weight(&mut tx, environment, feature.id).await?;
tx.commit().await?;
Ok(Variant::build(variant_id, value, weight))
}
pub async fn update(
pool: &Pool<Sqlite>,
environment: &Environment,
variant: &Variant,
new_value: String,
new_weight: i16,
) -> anyhow::Result<()> {
let mut tx = pool.begin().await?;
let feature_id: u16 =
Variants::update_variant_value(&mut *tx, params![variant.id, new_value], |v| {
v.get("feature_id")
})
.await
.map_err(|e| FlagrantError::QueryFailed("Could not update variant value", e))?;
Variants::upsert_variant_weight::<_, _, i16>(
&mut *tx,
params![environment.id, variant.id, new_weight],
|v| v.get("weight"),
)
.await
.map_err(|e| FlagrantError::QueryFailed("Could not set a variant's weight", e))?;
upsert_default_weight(&mut tx, environment, feature_id).await?;
tx.commit().await?;
Ok(())
}
pub async fn fetch(
pool: &Pool<Sqlite>,
environment: &Environment,
variant_id: u16,
) -> anyhow::Result<Variant> {
let variant = Variants::fetch_variant::<_, Variant>(pool, params!(environment.id, variant_id))
.await
.map_err(|e| FlagrantError::QueryFailed("Could fetch a variant", e))?;
Ok(variant)
}
pub async fn list(
pool: &Pool<Sqlite>,
environment: &Environment,
feature: &Feature,
) -> anyhow::Result<Vec<Variant>> {
let variants = Variants::fetch_variants_for_feature::<_, Variant>(
pool,
params![environment.id, feature.id],
)
.await
.map_err(|e| FlagrantError::QueryFailed("Could not fetch variants for feature", e))?;
if !variants.iter().any(|v| is_default(environment, v)) {
bail!(FlagrantError::BadRequest(
"No feature value set. Use \"FEATURE val ...\" to set default feature value."
));
}
Ok(variants)
}
pub async fn delete(
conn: &mut SqliteConnection,
environment: &Environment,
variant: &Variant,
) -> anyhow::Result<()> {
let mut tx = conn.begin().await?;
let variants_count: u16 = Variants::fetch_count_of_feature_variants(
&mut *tx,
params![environment.id, variant.id],
|r| r.get("count"),
)
.await?;
if variants_count > 1 && is_default(environment, variant) {
bail!(FlagrantError::BadRequest("Could not remove default variant as there are still other variants defined for this feature"));
}
Variants::delete_variant_weights(&mut *tx, params![variant.id])
.await
.map_err(|e| FlagrantError::QueryFailed("Could not remove variant weights", e))?;
let feature_id: u16 =
Variants::delete_variant(&mut *tx, params![variant.id], |v| v.get("feature_id"))
.await
.map_err(|e| FlagrantError::QueryFailed("Could not remove variant", e))?;
if !is_default(environment, variant) {
upsert_default_weight(&mut tx, environment, feature_id).await?;
}
tx.commit().await?;
Ok(())
}
pub async fn update_accumulator(
conn: &mut SqliteConnection,
environment: &Environment,
variant: &Variant,
accumulator: i16,
) -> anyhow::Result<()> {
Variants::update_variant_accumulator(conn, params![environment.id, variant.id, accumulator])
.await
.map_err(|e| FlagrantError::QueryFailed("Could not update variant accumulator", e))?;
Ok(())
}
async fn upsert_default_weight(
conn: &mut SqliteConnection,
environment: &Environment,
feature_id: u16,
) -> anyhow::Result<()> {
Variants::upsert_default_variant_weight(conn, params![environment.id, feature_id])
.await
.map_err(|e| FlagrantError::QueryFailed("Could not upsert default variant weight", e))?;
Ok(())
}
fn is_default(environment: &Environment, variant: &Variant) -> bool {
variant
.environment_id
.map(|id| id == environment.id)
.unwrap_or(false)
}