diff --git a/crates/context_aware_config/src/api/context/handlers.rs b/crates/context_aware_config/src/api/context/handlers.rs index 0d0df3a6..8377aefe 100644 --- a/crates/context_aware_config/src/api/context/handlers.rs +++ b/crates/context_aware_config/src/api/context/handlers.rs @@ -221,6 +221,8 @@ fn create_ctx_from_put_req( override_: ctx_override.to_owned(), created_at: Utc::now(), created_by: user.get_email(), + last_modified_at: Utc::now().naive_utc(), + last_modified_by: user.get_email(), }) } @@ -232,6 +234,7 @@ pub fn hash(val: &Value) -> String { fn update_override_of_existing_ctx( conn: &mut PgConnection, ctx: Context, + user: &User, ) -> superposition::Result { use contexts::dsl; let mut new_override: Value = dsl::contexts @@ -245,16 +248,22 @@ fn update_override_of_existing_ctx( override_id: new_override_id, ..ctx }; - diesel::update(dsl::contexts) + let update_resp = diesel::update(dsl::contexts) .filter(dsl::id.eq(&new_ctx.id)) - .set(&new_ctx) - .execute(conn)?; - Ok(get_put_resp(new_ctx)) + .set(( + dsl::override_.eq(new_ctx.override_), + dsl::override_id.eq(new_ctx.override_id), + dsl::last_modified_at.eq(Utc::now().naive_utc()), + dsl::last_modified_by.eq(user.get_email()), + )) + .get_result::(conn)?; + Ok(get_put_resp(update_resp)) } fn replace_override_of_existing_ctx( conn: &mut PgConnection, ctx: Context, + user: &User, ) -> superposition::Result { use contexts::dsl; let new_override = ctx.override_; @@ -264,11 +273,16 @@ fn replace_override_of_existing_ctx( override_id: new_override_id, ..ctx }; - diesel::update(dsl::contexts) + let update_resp = diesel::update(dsl::contexts) .filter(dsl::id.eq(&new_ctx.id)) - .set(&new_ctx) - .execute(conn)?; - Ok(get_put_resp(new_ctx)) + .set(( + dsl::override_.eq(new_ctx.override_), + dsl::override_id.eq(new_ctx.override_id), + dsl::last_modified_at.eq(Utc::now().naive_utc()), + dsl::last_modified_by.eq(user.get_email()), + )) + .get_result::(conn)?; + Ok(get_put_resp(update_resp)) } fn get_put_resp(ctx: Context) -> PutResp { @@ -299,7 +313,7 @@ pub fn put( if already_under_txn { diesel::sql_query("ROLLBACK TO put_ctx_savepoint").execute(conn)?; } - update_override_of_existing_ctx(conn, new_ctx) + update_override_of_existing_ctx(conn, new_ctx, user) } Err(e) => { log::error!("failed to update context with db error: {:?}", e); @@ -354,7 +368,7 @@ fn override_helper( if already_under_txn { diesel::sql_query("ROLLBACK TO insert_ctx_savepoint").execute(conn)?; } - replace_override_of_existing_ctx(conn, new_ctx) // no need for .map(Json) + replace_override_of_existing_ctx(conn, new_ctx, user) // no need for .map(Json) } Err(e) => { log::error!("failed to update context with db error: {:?}", e); @@ -422,6 +436,8 @@ fn r#move( dsl::id.eq(&new_ctx_id), dsl::value.eq(&ctx_condition), dsl::priority.eq(priority), + dsl::last_modified_at.eq(Utc::now().naive_utc()), + dsl::last_modified_by.eq(user.get_email()), )) .get_result(conn); @@ -433,6 +449,8 @@ fn r#move( created_by: user.get_email(), override_id: ctx.override_id, override_: ctx.override_, + last_modified_at: Utc::now().naive_utc(), + last_modified_by: user.get_email(), }; let handle_unique_violation = @@ -443,14 +461,14 @@ fn r#move( .get_result(db_conn)?; let ctx = contruct_new_ctx_with_old_overrides(deleted_ctxt); - update_override_of_existing_ctx(db_conn, ctx) + update_override_of_existing_ctx(db_conn, ctx, user) } else { db_conn.build_transaction().read_write().run(|conn| { let deleted_ctxt = diesel::delete(dsl::contexts) .filter(dsl::id.eq(&old_ctx_id)) .get_result(conn)?; let ctx = contruct_new_ctx_with_old_overrides(deleted_ctxt); - update_override_of_existing_ctx(conn, ctx) + update_override_of_existing_ctx(conn, ctx, user) }) } }; @@ -569,6 +587,13 @@ pub fn delete_context_api( conn: &mut PooledConnection>, ) -> superposition::Result<()> { use contexts::dsl; + diesel::update(dsl::contexts) + .filter(dsl::id.eq(&ctx_id)) + .set(( + dsl::last_modified_at.eq(Utc::now().naive_utc()), + dsl::last_modified_by.eq(user.get_email()), + )) + .execute(conn)?; let deleted_row = delete(dsl::contexts.filter(dsl::id.eq(&ctx_id))).execute(conn); match deleted_row { Ok(0) => Err(not_found!("Context Id `{}` doesn't exists", ctx_id)), diff --git a/crates/context_aware_config/src/api/default_config/handlers.rs b/crates/context_aware_config/src/api/default_config/handlers.rs index 12629e17..df3754d7 100644 --- a/crates/context_aware_config/src/api/default_config/handlers.rs +++ b/crates/context_aware_config/src/api/default_config/handlers.rs @@ -10,13 +10,13 @@ use superposition_macros::{ }; use superposition_types::{result as superposition, SuperpositionUser, User}; -use crate::api::context::helpers::validate_value_with_function; +use crate::{api::context::helpers::validate_value_with_function, db::models}; use crate::{ api::functions::helpers::get_published_function_code, db::{ self, models::{Context, DefaultConfig}, - schema::{contexts::dsl::contexts, default_configs::dsl::default_configs}, + schema::{contexts::dsl::contexts, default_configs::dsl}, }, helpers::{add_config_version, validate_jsonschema}, }; @@ -26,11 +26,11 @@ use actix_web::{ HttpResponse, Scope, }; use chrono::Utc; -use diesel::Connection; use diesel::{ r2d2::{ConnectionManager, PooledConnection}, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl, }; +use diesel::{Connection, SelectableHelper}; use jsonschema::{Draft, JSONSchema, ValidationError}; use regex::Regex; use serde_json::{from_value, json, Map, Value}; @@ -86,20 +86,34 @@ async fn create( let result = fetch_default_key(&key, &mut conn); - let (value, schema, function_name) = match result { - Ok((val, schema, f_name)) => { - let val = req.value.unwrap_or(val); - let schema = req.schema.map_or_else(|| schema, Value::Object); + let (value, schema, function_name, created_at_val, created_by_val) = match result { + Ok(default_config_row) => { + let val = req.value.unwrap_or(default_config_row.value); + let schema = req + .schema + .map_or_else(|| default_config_row.schema, Value::Object); let f_name = if req.function_name == Some(Value::Null) { None } else { - func_name.or(f_name) + func_name.or(default_config_row.function_name) }; - (val, schema, f_name) + ( + val, + schema, + f_name, + default_config_row.created_at, + default_config_row.created_by, + ) } Err(superposition::AppError::DbError(diesel::NotFound)) => { match (req.value, req.schema) { - (Some(val), Some(schema)) => (val, Value::Object(schema), func_name), + (Some(val), Some(schema)) => ( + val, + Value::Object(schema), + func_name, + Utc::now(), + user.get_email(), + ), _ => { log::error!("No record found for {key}."); return Err(bad_argument!("No record found for {}", key)); @@ -117,8 +131,10 @@ async fn create( value, schema, function_name, - created_by: user.get_email(), - created_at: Utc::now(), + created_by: created_by_val, + created_at: created_at_val, + last_modified_at: Utc::now().naive_utc(), + last_modified_by: user.get_email(), }; validate_jsonschema( @@ -168,7 +184,7 @@ async fn create( } let version_id = conn.transaction::<_, superposition::AppError, _>(|transaction_conn| { - let upsert = diesel::insert_into(default_configs) + let upsert = diesel::insert_into(dsl::default_configs) .values(&default_config) .on_conflict(db::schema::default_configs::key) .do_update() @@ -200,15 +216,11 @@ async fn create( fn fetch_default_key( key: &String, conn: &mut PooledConnection>, -) -> superposition::Result<(Value, Value, Option)> { - let res: (Value, Value, Option) = default_configs +) -> superposition::Result { + let res = dsl::default_configs .filter(db::schema::default_configs::key.eq(key)) - .select(( - db::schema::default_configs::value, - db::schema::default_configs::schema, - db::schema::default_configs::function_name, - )) - .get_result::<(Value, Value, Option)>(conn)?; + .select(models::DefaultConfig::as_select()) + .get_result(conn)?; Ok(res) } @@ -216,7 +228,7 @@ fn fetch_default_key( async fn get(db_conn: DbConnection) -> superposition::Result>> { let DbConnection(mut conn) = db_conn; - let result: Vec = default_configs.get_results(&mut conn)?; + let result: Vec = dsl::default_configs.get_results(&mut conn)?; Ok(Json(result)) } @@ -259,10 +271,17 @@ async fn delete( .map_err(|_| unexpected_error!("Something went wrong"))?; if context_ids.is_empty() { conn.transaction::<_, superposition::AppError, _>(|transaction_conn| { - let deleted_row = diesel::delete( - default_configs.filter(db::schema::default_configs::key.eq(&key)), - ) - .execute(transaction_conn); + diesel::update(dsl::default_configs) + .filter(dsl::key.eq(&key)) + .set(( + dsl::last_modified_at.eq(Utc::now().naive_utc()), + dsl::last_modified_by.eq(user.get_email()), + )) + .execute(transaction_conn)?; + + let deleted_row = + diesel::delete(dsl::default_configs.filter(dsl::key.eq(&key))) + .execute(transaction_conn); match deleted_row { Ok(0) => Err(not_found!("default config key `{}` doesn't exists", key)), Ok(_) => { diff --git a/crates/context_aware_config/src/api/dimension/handlers.rs b/crates/context_aware_config/src/api/dimension/handlers.rs index 4fb51cad..e9653835 100644 --- a/crates/context_aware_config/src/api/dimension/handlers.rs +++ b/crates/context_aware_config/src/api/dimension/handlers.rs @@ -68,6 +68,8 @@ async fn create( created_by: user.get_email(), created_at: Utc::now(), function_name: fun_name.clone(), + last_modified_at: Utc::now().naive_utc(), + last_modified_by: user.get_email(), }; let upsert = diesel::insert_into(dimensions) diff --git a/crates/context_aware_config/src/api/functions/handlers.rs b/crates/context_aware_config/src/api/functions/handlers.rs index 4c1e0bda..7463154d 100644 --- a/crates/context_aware_config/src/api/functions/handlers.rs +++ b/crates/context_aware_config/src/api/functions/handlers.rs @@ -62,6 +62,8 @@ async fn create( published_by: None, published_runtime_version: None, function_description: req.description, + last_modified_at: Utc::now().naive_utc(), + last_modified_by: user.get_email(), }; let insert: Result = diesel::insert_into(functions) @@ -139,6 +141,8 @@ async fn update( published_at: result.published_at, published_by: result.published_by, published_runtime_version: result.published_runtime_version, + last_modified_at: Utc::now().naive_utc(), + last_modified_by: user.get_email(), }; let mut updated_function = diesel::update(functions) @@ -184,6 +188,13 @@ async fn delete_function( let DbConnection(mut conn) = db_conn; let f_name = params.into_inner(); + diesel::update(functions) + .filter(function_name.eq(&f_name)) + .set(( + dsl::last_modified_at.eq(Utc::now().naive_utc()), + dsl::last_modified_by.eq(user.get_email()), + )) + .execute(&mut conn)?; let deleted_row = delete(functions.filter(function_name.eq(&f_name))).execute(&mut conn); match deleted_row { diff --git a/crates/context_aware_config/src/api/type_templates/handlers.rs b/crates/context_aware_config/src/api/type_templates/handlers.rs index dd9284d2..ff14c280 100644 --- a/crates/context_aware_config/src/api/type_templates/handlers.rs +++ b/crates/context_aware_config/src/api/type_templates/handlers.rs @@ -59,6 +59,7 @@ async fn create_type( type_templates::type_schema.eq(request.type_schema.clone()), type_templates::type_name.eq(type_name), type_templates::created_by.eq(user.email.clone()), + type_templates::last_modified_by.eq(user.email.clone()), )) .get_result::(&mut conn) .map_err(|err| { @@ -103,8 +104,8 @@ async fn update_type( .filter(type_templates::type_name.eq(type_name)) .set(( type_templates::type_schema.eq(request.clone()), - type_templates::created_by.eq(user.email), - type_templates::last_modified.eq(timestamp), + type_templates::last_modified_at.eq(timestamp), + type_templates::last_modified_by.eq(user.email), )) .get_result::(&mut conn) .map_err(|err| { @@ -118,9 +119,17 @@ async fn update_type( async fn delete_type( path: Path, db_conn: DbConnection, + user: User, ) -> superposition::Result { let DbConnection(mut conn) = db_conn; let type_name = path.into_inner(); + diesel::update(dsl::type_templates) + .filter(dsl::type_name.eq(type_name.clone())) + .set(( + dsl::last_modified_at.eq(Utc::now().naive_utc()), + dsl::last_modified_by.eq(user.email), + )) + .execute(&mut conn)?; let deleted_type = diesel::delete(dsl::type_templates.filter(dsl::type_name.eq(type_name))) .get_result::(&mut conn)?; diff --git a/postman/cac.postman_collection.json b/postman/cac.postman_collection.json index afc09c5f..299452f8 100644 --- a/postman/cac.postman_collection.json +++ b/postman/cac.postman_collection.json @@ -892,6 +892,8 @@ " ", " delete response.created_at;", " delete response.created_by;", + " delete response.last_modified_at;", + " delete response.last_modified_by;", "", " pm.expect(JSON.stringify(response)).to.be.eq(JSON.stringify(expected_context));", "});", diff --git a/postman/cac/Context/Get Context/event.test.js b/postman/cac/Context/Get Context/event.test.js index 5ccf43d0..a8553581 100644 --- a/postman/cac/Context/Get Context/event.test.js +++ b/postman/cac/Context/Get Context/event.test.js @@ -24,6 +24,8 @@ pm.test("Context equality check", function() { delete response.created_at; delete response.created_by; + delete response.last_modified_at; + delete response.last_modified_by; pm.expect(JSON.stringify(response)).to.be.eq(JSON.stringify(expected_context)); }); diff --git a/postman/cac/custom types/Create Type/event.test.js b/postman/cac/custom types/Create Type/event.test.js index cde737d8..e34b4bb7 100644 --- a/postman/cac/custom types/Create Type/event.test.js +++ b/postman/cac/custom types/Create Type/event.test.js @@ -17,7 +17,7 @@ pm.test('expect response be 200', function () { throw error; } const resp = response.json(); - const modified_response = resp.map(({ created_at, last_modified, ...rest }) => rest).sort((a, b) => { + const modified_response = resp.map(({ created_at, last_modified_at, last_modified_by, ...rest }) => rest).sort((a, b) => { return a.type_name > b.type_name; }); pm.expect(JSON.stringify(modified_response)).to.be.eq(JSON.stringify([