Skip to main content
Glama
change_set_apply.rs7.4 kB
use std::collections::{ HashMap, HashSet, }; use dal::{ DalContext, action::Action, approval_requirement::{ ApprovalRequirement, ApprovalRequirementApprover, }, change_set::approval::ChangeSetApproval, diagram::view::View, }; use dal_test::{ Result, eyre, helpers::create_component_for_default_schema_name, prelude::ChangeSetTestHelpers, sdf_test, }; use indoc::indoc; use pretty_assertions_sorted::assert_eq; use sdf_core::{ dal_wrapper, dal_wrapper::DalWrapperError, }; use sdf_test::helpers::SdfTestHelpers; use si_data_spicedb::SpiceDbClient; use si_db::HistoryActor; use si_events::{ ChangeSetApprovalStatus, workspace_snapshot::EntityKind, }; use si_id::ViewId; // FIXME(nick,jacob): this must happen in the "sdf_test"'s equivalent to global setup, but not in // dal tests. This also should _really_ reflect the "schema.zed" file that production uses. async fn write_schema(client: &mut SpiceDbClient) -> Result<()> { let schema = indoc! {" definition user {} definition workspace { relation approver: user relation owner: user permission approve = approver+owner permission manage = owner } "}; client.write_schema(schema).await?; Ok(()) } // NOTE(nick): this is an integration test and not a service test, but given that "sdf_test" is in // a weird, unused place at the time of writing, this test will live here. #[sdf_test] async fn protected_apply(ctx: &mut DalContext, spicedb_client: SpiceDbClient) -> Result<()> { let mut spicedb_client = spicedb_client; // FIXME(nick,jacob): see the comment attached to this function. write_schema(&mut spicedb_client).await?; // Cache the IDs we need. let user_id = match ctx.history_actor() { HistoryActor::SystemInit => return Err(eyre!("invalid user")), HistoryActor::User(user_id) => *user_id, }; // Create a view with a requirement and then commit. let todd_view = View::new(ctx, "toddhoward").await?; let todd_view_id = todd_view.id(); ApprovalRequirement::new_definition( ctx, todd_view_id, 1, HashSet::from([ApprovalRequirementApprover::User(user_id)]), ) .await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; // Scenario 1: apply to HEAD and create a new change set. { ChangeSetTestHelpers::apply_change_set_to_base(ctx).await?; ChangeSetTestHelpers::fork_from_head_change_set(ctx).await?; let (frontend_latest_approvals, frontend_requirements) = dal_wrapper::change_set::status(ctx, &mut spicedb_client).await?; assert!(frontend_latest_approvals.is_empty()); assert!(frontend_requirements.is_empty()); } // Scenario 2: create a component in our new view. { let new_component = create_component_for_default_schema_name( ctx, "starfield", "shattered space", todd_view_id, ) .await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let queued_actions = Action::find_for_component_id(ctx, new_component.id()).await?; assert_eq!(1, queued_actions.len()); let (frontend_latest_approvals, mut frontend_requirements) = dal_wrapper::change_set::status(ctx, &mut spicedb_client).await?; frontend_requirements.sort_by_key(|r| r.entity_id); assert!(frontend_latest_approvals.is_empty()); assert_eq!( vec![si_frontend_types::ChangeSetApprovalRequirement { entity_id: todd_view_id.into_inner().into(), entity_kind: EntityKind::View, required_count: 1, is_satisfied: false, applicable_approval_ids: Vec::new(), approver_groups: HashMap::new(), approver_individuals: vec![user_id], },], // expected frontend_requirements // actual ); } // Scenario 3: try to perform protected apply. This should fail in an expected manner. { match SdfTestHelpers::protected_apply_change_set_to_base(ctx, &mut spicedb_client).await { Err(report) => match report.downcast_ref::<DalWrapperError>() { Some(DalWrapperError::ApplyWithUnsatisfiedRequirements( unsatisfied_requirements, )) => assert_eq!( vec![(todd_view_id.into_inner().into(), EntityKind::View)], // expected unsatisfied_requirements.to_owned() // actual ), Some(err) => return Err(eyre!("unexpected error: {err}")), None => return Err(eyre!("unexpected report: {report:?}")), }, other => return Err(eyre!("unexpected result: {other:?}")), } } // Scenario 4: approve the changes. { let approving_ids_with_hashes = dal_wrapper::change_set::new_approval_approving_ids_with_hashes( ctx, &mut spicedb_client, ) .await?; let approval = ChangeSetApproval::new( ctx, ChangeSetApprovalStatus::Approved, approving_ids_with_hashes, ) .await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let (frontend_latest_approvals, frontend_requirements) = dal_wrapper::change_set::status(ctx, &mut spicedb_client).await?; assert_eq!( vec![si_frontend_types::ChangeSetApproval { id: approval.id(), user_id, status: ChangeSetApprovalStatus::Approved, is_valid: true, }], // expected frontend_latest_approvals // actual ); assert_eq!( vec![si_frontend_types::ChangeSetApprovalRequirement { entity_id: todd_view_id.into_inner().into(), entity_kind: EntityKind::View, required_count: 1, is_satisfied: true, applicable_approval_ids: vec![approval.id()], approver_groups: HashMap::new(), approver_individuals: vec![user_id] }], // expected frontend_requirements // actual ); } // Scenario 5: apply the changes used the protected flow and observe that it works. { SdfTestHelpers::protected_apply_change_set_to_base(ctx, &mut spicedb_client).await?; ChangeSetTestHelpers::wait_for_actions_to_run(ctx).await?; ChangeSetTestHelpers::fork_from_head_change_set(ctx).await?; let default_view_id = View::get_id_for_default(ctx).await?; let mut view_ids: Vec<ViewId> = View::list(ctx).await?.iter().map(|v| v.id()).collect(); view_ids.sort(); assert_eq!( vec![default_view_id, todd_view_id], // expected view_ids // actual ); let (frontend_latest_approvals, frontend_requirements) = dal_wrapper::change_set::status(ctx, &mut spicedb_client).await?; assert!(frontend_latest_approvals.is_empty()); assert!(frontend_requirements.is_empty()); } Ok(()) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server