fix(MyShares): correct order if items in MyShares view, when grouped by Files

This commit is contained in:
Edouard Vanbelle
2026-05-31 23:02:19 +02:00
parent c7e950a707
commit a0d9cd881b
3 changed files with 28 additions and 31 deletions
@@ -423,7 +423,11 @@ mod integration_tests {
} }
fn rand_name(test: &str) -> String { fn rand_name(test: &str) -> String {
format!("rust-test-svc-{}-{}", test, &Uuid::new_v4().to_string()[..8]) format!(
"rust-test-svc-{}-{}",
test,
&Uuid::new_v4().to_string()[..8]
)
} }
// ── 9. Virtual group cannot be deleted ───────────────────────────────── // ── 9. Virtual group cannot be deleted ─────────────────────────────────
@@ -786,9 +786,7 @@ mod integration_tests {
let mut ids = Vec::with_capacity(8); let mut ids = Vec::with_capacity(8);
for i in 0..8 { for i in 0..8 {
let g = repo let g = repo
.create( .create(&SubjectGroup::new(&rand_name(&format!("cyc8-{i}")), None).unwrap())
&SubjectGroup::new(&rand_name(&format!("cyc8-{i}")), None).unwrap(),
)
.await .await
.unwrap(); .unwrap();
ids.push(g.id); ids.push(g.id);
@@ -843,9 +841,7 @@ mod integration_tests {
let mut ids = Vec::with_capacity(len); let mut ids = Vec::with_capacity(len);
for i in 0..len { for i in 0..len {
let g = repo let g = repo
.create( .create(&SubjectGroup::new(&rand_name(&format!("depth-{i}")), None).unwrap())
&SubjectGroup::new(&rand_name(&format!("depth-{i}")), None).unwrap(),
)
.await .await
.unwrap(); .unwrap();
ids.push(g.id); ids.push(g.id);
@@ -855,9 +851,7 @@ mod integration_tests {
for i in 0..(len - 1) { for i in 0..(len - 1) {
repo.add_member(ids[i], GroupMember::Group(ids[i + 1]), admin) repo.add_member(ids[i], GroupMember::Group(ids[i + 1]), admin)
.await .await
.unwrap_or_else(|e| { .unwrap_or_else(|e| panic!("edge {i} should fit in the depth budget: {:?}", e));
panic!("edge {i} should fit in the depth budget: {:?}", e)
});
} }
// Lift the whole chain under a new outer group → subtree depth 9. // Lift the whole chain under a new outer group → subtree depth 9.
+20 -21
View File
@@ -1401,30 +1401,29 @@ impl AuthorizationEngine for PgAclEngine {
.filter_map(|rid| { .filter_map(|rid| {
let (resource_type, first_shared_at, subj_map) = resource_map.remove(&rid)?; let (resource_type, first_shared_at, subj_map) = resource_map.remove(&rid)?;
let mut grants: Vec<OutgoingGrantEntry> = subj_map.into_values().collect(); let mut grants: Vec<OutgoingGrantEntry> = subj_map.into_values().collect();
let role_rank = |perms: &[Permission]| -> u8 { // Per-resource subject ordering (matches the subject-sort
if perms.contains(&Permission::Delete) && perms.contains(&Permission::Share) { // branch's SQL CASE):
0 // admin → Can manage // 0 = group, 1 = user, 2 = token-with-password, 3 = token,
} else if perms.contains(&Permission::Create) // 4 = external.
|| perms.contains(&Permission::Update) // Alphabetical tiebreak by display name. This intentionally
{ // ignores role/permission tier — the share dialog renders
1 // editor → Can edit // role as a separate pill; ordering by subject type is the
} else { // UX contract.
2 // viewer → Can view let subject_rank = |e: &OutgoingGrantEntry| -> u8 {
match e.subject_type.as_str() {
"group" => 0,
"user" => 1,
"token" if e.has_password => 2,
"token" => 3,
_ => 4,
} }
}; };
grants.sort_by(|a, b| { grants.sort_by(|a, b| {
role_rank(&a.permissions) subject_rank(a).cmp(&subject_rank(b)).then_with(|| {
.cmp(&role_rank(&b.permissions)) a.subject_display
.then_with(|| { .to_lowercase()
// users before tokens .cmp(&b.subject_display.to_lowercase())
let type_rank = |st: &str| if st == "user" { 0u8 } else { 1 }; })
type_rank(&a.subject_type).cmp(&type_rank(&b.subject_type))
})
.then_with(|| {
a.subject_display
.to_lowercase()
.cmp(&b.subject_display.to_lowercase())
})
}); });
Some(OutgoingResourceSummary { Some(OutgoingResourceSummary {
resource_type, resource_type,