From 3661f20f84c6a51c36fd4e56a57e2c322c8f88d5 Mon Sep 17 00:00:00 2001 From: Anshul Garg Date: Wed, 11 Mar 2026 22:32:52 +0530 Subject: [PATCH 1/3] feat(auth): sort OAuth scopes by service, then access level Previously scopes were sorted by risk classification first (restricted, sensitive, non-sensitive), making it hard to find all scopes for a specific service. Now scopes are grouped by service name (Drive, Gmail, etc.), then by access level (read-only first), then by classification (safe before restricted), then alphabetically. This makes the scope picker much easier to navigate when selecting scopes for specific services. Closes #319 --- .changeset/sort-scopes-by-service.md | 5 +++ src/setup.rs | 63 ++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 .changeset/sort-scopes-by-service.md diff --git a/.changeset/sort-scopes-by-service.md b/.changeset/sort-scopes-by-service.md new file mode 100644 index 00000000..cdf55e2b --- /dev/null +++ b/.changeset/sort-scopes-by-service.md @@ -0,0 +1,5 @@ +--- +"@googleworkspace/cli": patch +--- + +Sort OAuth scopes in the TUI picker by service name first, then access level (read-only before write), then sensitivity classification, for easier browsing diff --git a/src/setup.rs b/src/setup.rs index 10aebe1b..8e7610a3 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -359,10 +359,14 @@ pub async fn fetch_scopes_for_apis(enabled_api_ids: &[String]) -> Vec Date: Wed, 11 Mar 2026 22:47:38 +0530 Subject: [PATCH 2/3] refactor: extract compare_scopes helper, improve test coverage Address Gemini review feedback: - Extract inline sort closure into a reusable compare_scopes function to avoid duplicating sort logic between production code and tests - Add a Sensitive-classified scope to the test to verify all four sort criteria (service, access level, classification, name) --- src/setup.rs | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/setup.rs b/src/setup.rs index 8e7610a3..e19698fe 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -254,6 +254,16 @@ pub struct DiscoveredScope { pub classification: ScopeClassification, } +/// Compare two scopes for sorting: group by service, then read-only first, +/// then non-sensitive before restricted, then alphabetically. +fn compare_scopes(a: &DiscoveredScope, b: &DiscoveredScope) -> std::cmp::Ordering { + a.api_name + .cmp(&b.api_name) + .then_with(|| b.is_readonly.cmp(&a.is_readonly)) + .then_with(|| a.classification.cmp(&b.classification)) + .then_with(|| a.short.cmp(&b.short)) +} + /// Fetch scopes from discovery docs for the given enabled API IDs. pub async fn fetch_scopes_for_apis(enabled_api_ids: &[String]) -> Vec { let mut all_scopes: Vec = Vec::new(); @@ -362,13 +372,7 @@ pub async fn fetch_scopes_for_apis(enabled_api_ids: &[String]) -> Vec Date: Fri, 13 Mar 2026 02:02:05 +0530 Subject: [PATCH 3/3] refactor: use slice assertion for scope sort order Replace brittle index-based assertions with a collected Vec comparison so the test remains maintainable as scopes change over time. --- src/setup.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/setup.rs b/src/setup.rs index e19698fe..df219e46 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -2368,15 +2368,23 @@ mod tests { scopes.sort_by(compare_scopes); - // Drive scopes first (alphabetically before Gmail), read-only before write - assert_eq!(scopes[0].short, "drive.readonly"); - // Within Drive write scopes: Sensitive before Restricted - assert_eq!(scopes[1].short, "drive.metadata"); + // Assert the full sort order of scope short names. + // Order: by service (alpha), then read-only before write, + // then by classification (Sensitive before Restricted), then by short name. + let sorted_shorts: Vec<_> = scopes.iter().map(|s| s.short.as_str()).collect(); + assert_eq!( + sorted_shorts, + &[ + "drive.readonly", + "drive.metadata", + "drive", + "gmail.readonly", + "gmail", + ] + ); + + // Verify classification sorting for the Drive write scopes. assert_eq!(scopes[1].classification, ScopeClassification::Sensitive); - assert_eq!(scopes[2].short, "drive"); assert_eq!(scopes[2].classification, ScopeClassification::Restricted); - // Gmail scopes second - assert_eq!(scopes[3].short, "gmail.readonly"); - assert_eq!(scopes[4].short, "gmail"); } }