From 6ef2694fefc5068f7ce5f4af2e30a48163c0e9a6 Mon Sep 17 00:00:00 2001 From: Priec Date: Sat, 15 Aug 2026 09:17:32 +0200 Subject: [PATCH] aliasing can now move things around --- common/proto/table_definition.proto | 6 ++++++ common/src/proto/descriptor.bin | Bin 183231 -> 183712 bytes common/src/proto/komp_ac.table_definition.rs | 9 +++++++++ server | 2 +- .../pages/admin/table_definition/loader.rs | 1 + web/src/pages/admin/table_definition/logic.rs | 1 + web/src/pages/admin/table_definition/state.rs | 4 ++++ web/src/pages/admin/table_definition/ui.rs | 2 ++ .../admin/table_definition/columns_panel.html | 1 + 9 files changed, 25 insertions(+), 1 deletion(-) diff --git a/common/proto/table_definition.proto b/common/proto/table_definition.proto index 25b767cf..10dc0ea5 100644 --- a/common/proto/table_definition.proto +++ b/common/proto/table_definition.proto @@ -375,6 +375,8 @@ message TableDetail { map column_behaviors = 7; string table_kind = 8; bool global = 9; + // Revision of this table definition for optimistic concurrency control. + int64 row_version = 10; } // Server-owned behavior for one logical column returned in table details. @@ -417,12 +419,16 @@ message SetColumnPresentationRequest { string profile_name = 1; string table_name = 2; repeated ColumnPresentation columns = 3; + // Row version returned by GetProfileDetails when this presentation was read. + int64 expected_row_version = 4; } message SetColumnPresentationResponse { bool success = 1; string message = 2; repeated ColumnPresentation columns = 3; + // New row version after the update. + int64 row_version = 4; } // Request to delete one table definition entirely. diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index 8b5f22767c28ab5fcbf53ef81e76e20dfc6bd9b5..2180e340db3e1331f3fd9d9972811708c81ae915 100644 GIT binary patch delta 3110 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 diff --git a/common/src/proto/komp_ac.table_definition.rs b/common/src/proto/komp_ac.table_definition.rs index c264b1bd..b798fc69 100644 --- a/common/src/proto/komp_ac.table_definition.rs +++ b/common/src/proto/komp_ac.table_definition.rs @@ -385,6 +385,9 @@ pub struct TableDetail { pub table_kind: ::prost::alloc::string::String, #[prost(bool, tag = "9")] pub global: bool, + /// Revision of this table definition for optimistic concurrency control. + #[prost(int64, tag = "10")] + pub row_version: i64, } /// Server-owned behavior for one logical column returned in table details. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] @@ -440,6 +443,9 @@ pub struct SetColumnPresentationRequest { pub table_name: ::prost::alloc::string::String, #[prost(message, repeated, tag = "3")] pub columns: ::prost::alloc::vec::Vec, + /// Row version returned by GetProfileDetails when this presentation was read. + #[prost(int64, tag = "4")] + pub expected_row_version: i64, } #[derive(serde::Serialize, serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -450,6 +456,9 @@ pub struct SetColumnPresentationResponse { pub message: ::prost::alloc::string::String, #[prost(message, repeated, tag = "3")] pub columns: ::prost::alloc::vec::Vec, + /// New row version after the update. + #[prost(int64, tag = "4")] + pub row_version: i64, } /// Request to delete one table definition entirely. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] diff --git a/server b/server index 481719e3..08319bdc 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 481719e3cb0d6960528f47c93f03975bb4a15ad3 +Subproject commit 08319bdc33f6952ed360ef02b3cc7af105360456 diff --git a/web/src/pages/admin/table_definition/loader.rs b/web/src/pages/admin/table_definition/loader.rs index 769793b6..1797b88c 100644 --- a/web/src/pages/admin/table_definition/loader.rs +++ b/web/src/pages/admin/table_definition/loader.rs @@ -194,6 +194,7 @@ pub(crate) async fn load_page( .find(|table| table.name == inputs.selection.table) .map(|table| TableDetailView { id: table.id, + row_version: table.row_version, columns: table .columns .iter() diff --git a/web/src/pages/admin/table_definition/logic.rs b/web/src/pages/admin/table_definition/logic.rs index d1d6174a..0c740c43 100644 --- a/web/src/pages/admin/table_definition/logic.rs +++ b/web/src/pages/admin/table_definition/logic.rs @@ -363,6 +363,7 @@ pub(crate) async fn set_column_presentation( profile_name: form.profile.clone(), table_name: form.table.clone(), columns, + expected_row_version: form.expected_row_version, }; let Ok(request) = authenticated_request(&headers, request) else { return Redirect::to("/login").into_response(); diff --git a/web/src/pages/admin/table_definition/state.rs b/web/src/pages/admin/table_definition/state.rs index 3a13e7d5..05c52aea 100644 --- a/web/src/pages/admin/table_definition/state.rs +++ b/web/src/pages/admin/table_definition/state.rs @@ -80,6 +80,7 @@ impl TableSummary { #[derive(Clone, Debug)] pub(crate) struct TableDetailView { pub id: i64, + pub row_version: i64, pub row_display_columns: Vec, pub columns: Vec, pub scripts: Vec, @@ -181,6 +182,8 @@ pub(crate) struct PresentationForm { #[serde(default)] pub table: String, #[serde(default)] + pub expected_row_version: i64, + #[serde(default)] pub column_ids: Vec, #[serde(default)] pub aliases: Vec, @@ -387,6 +390,7 @@ mod tests { fn provenance_and_renameability_are_independent() { let detail = TableDetailView { id: 1, + row_version: 1, row_display_columns: Vec::new(), scripts: Vec::new(), columns: vec![ diff --git a/web/src/pages/admin/table_definition/ui.rs b/web/src/pages/admin/table_definition/ui.rs index b2543216..574d77f5 100644 --- a/web/src/pages/admin/table_definition/ui.rs +++ b/web/src/pages/admin/table_definition/ui.rs @@ -237,6 +237,7 @@ mod tests { link_targets: vec![table("invoice", "dynamic"), table("accounts", "system")], detail: Some(TableDetailView { id: 7, + row_version: 1, row_display_columns: vec!["number".to_string()], scripts: Vec::new(), columns: vec![DetailColumn { @@ -377,6 +378,7 @@ mod tests { assert!(html.contains(r#"hx-post="/admin/tables/presentation""#), "{html}"); assert!(html.contains(r#"name="column_ids" value="1""#), "{html}"); + assert!(html.contains(r#"name="expected_row_version" value="1""#), "{html}"); assert!(html.contains(r#"name="aliases" value="number""#), "{html}"); assert!(html.contains(r#"name="action" value="save""#), "{html}"); } diff --git a/web/templates/pages/admin/table_definition/columns_panel.html b/web/templates/pages/admin/table_definition/columns_panel.html index 8c751955..48e64839 100644 --- a/web/templates/pages/admin/table_definition/columns_panel.html +++ b/web/templates/pages/admin/table_definition/columns_panel.html @@ -32,6 +32,7 @@ hx-target="#table-panel" hx-swap="innerHTML"> +
{% for column in detail.columns %}