Skip to content

Commit

Permalink
feat: Tenant specific config support via .cac.toml
Browse files Browse the repository at this point in the history
  • Loading branch information
ayushjain17 committed Sep 23, 2024
1 parent e571480 commit 1268793
Show file tree
Hide file tree
Showing 14 changed files with 237 additions and 230 deletions.
3 changes: 0 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@ CAC_HOST="http://localhost:8080"
API_HOSTNAME="http://localhost:8080"
SUPERPOSITION_VERSION="v0.1.0"
HOSTNAME="<application_name>-<deployment_id>-<replicaset>-<pod>"
MJOS_ALLOWED_ORIGINS=https://potato.in,https://onion.in,http://localhost:8080
ACTIX_KEEP_ALIVE=120
MAX_DB_CONNECTION_POOL_SIZE=3
ENABLE_TENANT_AND_SCOPE=true
TENANTS=dev,test
TENANT_MIDDLEWARE_EXCLUSION_LIST="/health,/assets/favicon.ico,/pkg/frontend.js,/pkg,/pkg/frontend_bg.wasm,/pkg/tailwind.css,/pkg/style.css,/assets,/admin,/"
SERVICE_PREFIX=""
SERVICE_NAME="CAC"
MANDATORY_DIMENSIONS=""
# MANDATORY_DIMENSIONS="dev:clientId,fare;test:fare"
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ leptos-use = "0.10.3"
mime = "0.3.17"
aws-sdk-kms = {version = "1.38.0"}
aws-config = { version = "1.1.7", features = ["behavior-version-latest"] }
toml = { version = "0.8.8", features = ["preserve_order"] }

[workspace.lints.clippy]
mod_module_files = "warn"
2 changes: 1 addition & 1 deletion crates/cac_toml/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ path = "src/bin.rs"
clap = { version = "4.3.0", features = ["derive"] }
pest = "2.6"
pest_derive = "2.6"
toml = { version = "0.8.8", features = ["preserve_order"] }
toml = { workspace = true }
47 changes: 22 additions & 25 deletions crates/context_aware_config/src/api/config/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
use std::{collections::HashMap, str::FromStr};

use super::helpers::{
filter_config_by_dimensions, filter_config_by_prefix, get_query_params_map,
};
use super::types::{Config, Context};
use crate::api::context::{
delete_context_api, hash, put, validate_dimensions_and_calculate_priority, PutReq,
};
use crate::api::dimension::get_all_dimension_schema_map;
use crate::{
db::schema::{config_versions::dsl as config_versions, event_log::dsl as event_log},
helpers::generate_cac,
};
use actix_http::header::HeaderValue;
use actix_web::web::Data;
use actix_web::{get, put, web, HttpRequest, HttpResponse, HttpResponseBuilder, Scope};
use cac_client::{eval_cac, eval_cac_with_reasoning, MergeStrategy};
use chrono::{DateTime, NaiveDateTime, TimeZone, Timelike, Utc};
Expand All @@ -22,19 +9,32 @@ use diesel::{
r2d2::{ConnectionManager, PooledConnection},
ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl,
};
use serde_json::{json, Map, Value};
use service_utils::service::types::{AppState, Tenant};
use superposition_macros::{bad_argument, db_error, unexpected_error};
use superposition_types::{result as superposition, Cac, Condition, Overrides, User};

use itertools::Itertools;
use jsonschema::JSONSchema;
use serde_json::{json, Map, Value};
use service_utils::service::types::TenantConfig;
use service_utils::{
helpers::extract_dimensions,
service::types::{AppHeader, DbConnection},
};
use superposition_macros::{bad_argument, db_error, unexpected_error};
use superposition_types::{result as superposition, Cac, Condition, Overrides, User};
use uuid::Uuid;

use crate::api::context::{
delete_context_api, hash, put, validate_dimensions_and_calculate_priority, PutReq,
};
use crate::api::dimension::get_all_dimension_schema_map;
use crate::{
db::schema::{config_versions::dsl as config_versions, event_log::dsl as event_log},
helpers::generate_cac,
};

use super::helpers::{
filter_config_by_dimensions, filter_config_by_prefix, get_query_params_map,
};
use super::types::{Config, Context};

pub fn endpoints() -> Scope {
Scope::new("")
.service(get)
Expand Down Expand Up @@ -365,8 +365,7 @@ fn construct_new_payload(
async fn reduce_config_key(
user: User,
conn: &mut PooledConnection<ConnectionManager<PgConnection>>,
state: &Data<AppState>,
tenant: &Tenant,
tenant_config: &TenantConfig,
mut og_contexts: Vec<Context>,
mut og_overrides: HashMap<String, Overrides>,
check_key: &str,
Expand Down Expand Up @@ -454,7 +453,7 @@ async fn reduce_config_key(
if is_approve {
let _ = delete_context_api(cid.clone(), user.clone(), conn);
if let Ok(put_req) = construct_new_payload(request_payload) {
let _ = put(put_req, conn, false, &user, state, tenant);
let _ = put(put_req, conn, false, &user, &tenant_config);
}
}

Expand Down Expand Up @@ -497,8 +496,7 @@ async fn reduce_config(
req: HttpRequest,
user: User,
db_conn: DbConnection,
state: Data<AppState>,
tenant: Tenant,
tenant_config: TenantConfig,
) -> superposition::Result<HttpResponse> {
let DbConnection(mut conn) = db_conn;
let is_approve = req
Expand All @@ -517,8 +515,7 @@ async fn reduce_config(
config = reduce_config_key(
user.clone(),
&mut conn,
&state,
&tenant,
&tenant_config,
contexts.clone(),
overrides.clone(),
key.as_str(),
Expand Down
63 changes: 26 additions & 37 deletions crates/context_aware_config/src/api/context/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::{
},
};
use actix_web::web::Data;
use service_utils::service::types::{AppHeader, AppState, CustomHeaders, Tenant};
use service_utils::service::types::{AppHeader, AppState, CustomHeaders, TenantConfig};

use actix_web::{
delete, get, post, put,
Expand Down Expand Up @@ -193,17 +193,15 @@ fn create_ctx_from_put_req(
req: Json<PutReq>,
conn: &mut DBConnection,
user: &User,
state: &Data<AppState>,
tenant: &Tenant,
tenant_config: &TenantConfig,
) -> superposition::Result<Context> {
let ctx_condition = req.context.to_owned().into_inner();
let condition_val = json!(ctx_condition);
let r_override = req.r#override.clone().into_inner();
let ctx_override = json!(r_override.to_owned());
validate_condition_with_mandatory_dimensions(
&ctx_condition,
&state.mandatory_dimensions,
tenant,
&tenant_config.mandatory_dimensions,
)?;
validate_override_with_default_configs(conn, &r_override)?;
validate_condition_with_functions(conn, &ctx_condition)?;
Expand Down Expand Up @@ -307,11 +305,10 @@ pub fn put(
conn: &mut PooledConnection<ConnectionManager<PgConnection>>,
already_under_txn: bool,
user: &User,
state: &Data<AppState>,
tenant: &Tenant,
tenant_config: &TenantConfig,
) -> superposition::Result<PutResp> {
use contexts::dsl::contexts;
let new_ctx = create_ctx_from_put_req(req, conn, user, &state, tenant)?;
let new_ctx = create_ctx_from_put_req(req, conn, user, tenant_config)?;

if already_under_txn {
diesel::sql_query("SAVEPOINT put_ctx_savepoint").execute(conn)?;
Expand Down Expand Up @@ -340,11 +337,11 @@ async fn put_handler(
req: Json<PutReq>,
mut db_conn: DbConnection,
user: User,
tenant: Tenant,
tenant_config: TenantConfig,
) -> superposition::Result<HttpResponse> {
let tags = parse_config_tags(custom_headers.config_tags)?;
db_conn.transaction::<_, superposition::AppError, _>(|transaction_conn| {
let put_response = put(req, transaction_conn, true, &user, &state, &tenant)
let put_response = put(req, transaction_conn, true, &user, &tenant_config)
.map_err(|err: superposition::AppError| {
log::info!("context put failed with error: {:?}", err);
err
Expand All @@ -365,11 +362,10 @@ fn override_helper(
conn: &mut PooledConnection<ConnectionManager<PgConnection>>,
already_under_txn: bool,
user: &User,
state: &Data<AppState>,
tenant: Tenant,
tenant_config: &TenantConfig,
) -> superposition::Result<PutResp> {
use contexts::dsl::contexts;
let new_ctx = create_ctx_from_put_req(req, conn, user, state, &tenant)?;
let new_ctx = create_ctx_from_put_req(req, conn, user, &tenant_config)?;
if already_under_txn {
diesel::sql_query("SAVEPOINT insert_ctx_savepoint").execute(conn)?;
}
Expand Down Expand Up @@ -397,12 +393,12 @@ async fn update_override_handler(
req: Json<PutReq>,
mut db_conn: DbConnection,
user: User,
tenant: Tenant,
tenant_config: TenantConfig,
) -> superposition::Result<HttpResponse> {
let tags = parse_config_tags(custom_headers.config_tags)?;
db_conn.transaction::<_, superposition::AppError, _>(|transaction_conn| {
let override_resp =
override_helper(req, transaction_conn, true, &user, &state, tenant).map_err(
override_helper(req, transaction_conn, true, &user, &tenant_config).map_err(
|err: superposition::AppError| {
log::info!("context put failed with error: {:?}", err);
err
Expand All @@ -425,8 +421,7 @@ fn r#move(
conn: &mut PooledConnection<ConnectionManager<PgConnection>>,
already_under_txn: bool,
user: &User,
state: &Data<AppState>,
tenant: &Tenant,
tenant_config: &TenantConfig,
) -> superposition::Result<PutResp> {
use contexts::dsl;
let req = req.into_inner();
Expand All @@ -441,8 +436,7 @@ fn r#move(
)?;
validate_condition_with_mandatory_dimensions(
&req.context.into_inner(),
&state.mandatory_dimensions,
tenant,
&tenant_config.mandatory_dimensions,
)?;

if priority == 0 {
Expand Down Expand Up @@ -519,7 +513,7 @@ async fn move_handler(
req: Json<MoveReq>,
mut db_conn: DbConnection,
user: User,
tenant: Tenant,
tenant_config: TenantConfig,
) -> superposition::Result<HttpResponse> {
let tags = parse_config_tags(custom_headers.config_tags)?;
db_conn.transaction::<_, superposition::AppError, _>(|transaction_conn| {
Expand All @@ -529,8 +523,7 @@ async fn move_handler(
transaction_conn,
true,
&user,
&state,
&tenant,
&tenant_config,
)
.map_err(|err| {
log::info!("move api failed with error: {:?}", err);
Expand Down Expand Up @@ -669,7 +662,7 @@ async fn bulk_operations(
reqs: Json<Vec<ContextAction>>,
db_conn: DbConnection,
user: User,
tenant: Tenant,
tenant_config: TenantConfig,
) -> superposition::Result<HttpResponse> {
use contexts::dsl::contexts;
let DbConnection(mut conn) = db_conn;
Expand All @@ -680,18 +673,15 @@ async fn bulk_operations(
for action in reqs.into_inner().into_iter() {
match action {
ContextAction::Put(put_req) => {
let put_resp = put(
Json(put_req),
transaction_conn,
true,
&user,
&state,
&tenant,
)
.map_err(|err| {
log::error!("Failed at insert into contexts due to {:?}", err);
err
})?;
let put_resp =
put(Json(put_req), transaction_conn, true, &user, &tenant_config)
.map_err(|err| {
log::error!(
"Failed at insert into contexts due to {:?}",
err
);
err
})?;
response.push(ContextBulkResponse::Put(put_resp));
}
ContextAction::Delete(ctx_id) => {
Expand Down Expand Up @@ -725,8 +715,7 @@ async fn bulk_operations(
transaction_conn,
true,
&user,
&state,
&tenant,
&tenant_config,
)
.map_err(|err| {
log::error!("Failed at moving context reponse due to {:?}", err);
Expand Down
23 changes: 11 additions & 12 deletions crates/context_aware_config/src/api/context/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
extern crate base64;

use std::collections::HashMap;
use std::str;

use base64::prelude::*;
use diesel::{
r2d2::{ConnectionManager, PooledConnection},
ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl,
};
use serde_json::{Map, Value};
use service_utils::helpers::extract_dimensions;
use service_utils::service::types::Tenant;
use std::str;
use superposition_macros::{unexpected_error, validation_error};
use superposition_types::{result as superposition, Condition};

Expand All @@ -15,23 +22,15 @@ use crate::{
dimensions::{self},
},
};
use diesel::{
r2d2::{ConnectionManager, PooledConnection},
ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl,
};
use serde_json::{Map, Value};
use std::collections::HashMap;

type DBConnection = PooledConnection<ConnectionManager<PgConnection>>;

pub fn validate_condition_with_mandatory_dimensions(
context: &Condition,
mandatory_dimensions: &Map<String, Value>,
tenant: &Tenant,
mandatory_dimensions: &Vec<String>,
) -> superposition::Result<()> {
let context_map = extract_dimensions(context)?;
let dimensions_list: Vec<String> = context_map.keys().cloned().collect();
let mandatory_dimensions =
Tenant::get_mandatory_dimensions(&tenant, mandatory_dimensions);
let all_mandatory_present = mandatory_dimensions
.iter()
.all(|dimension| dimensions_list.contains(dimension));
Expand Down
Loading

0 comments on commit 1268793

Please sign in to comment.