public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: "Shannon Sterz" <s.sterz@proxmox.com>
To: "Shan Shaji" <s.shaji@proxmox.com>
Cc: pdm-devel@lists.proxmox.com
Subject: Re: [RFC PATCH datacenter-manager 2/3] ui: acl: list granular level permission paths for views
Date: Thu, 26 Mar 2026 12:16:04 +0100	[thread overview]
Message-ID: <DHCO6BBURS8V.388FJXGOBCAX5@proxmox.com> (raw)
In-Reply-To: <20260325153535.286380-3-s.shaji@proxmox.com>

some comments in-line.

On Wed Mar 25, 2026 at 4:35 PM CET, Shan Shaji wrote:
> Previously, users were unable to assign permissions to individual views.
> Resolved this by loading the views list and extending permission paths
> to include specific view routes.
>
> Signed-off-by: Shan Shaji <s.shaji@proxmox.com>
> ---
>  .../configuration/permission_path_selector.rs | 80 +++++++++++++++----
>  1 file changed, 64 insertions(+), 16 deletions(-)
>
> diff --git a/ui/src/configuration/permission_path_selector.rs b/ui/src/configuration/permission_path_selector.rs
> index 59bdabb..a0fec86 100644
> --- a/ui/src/configuration/permission_path_selector.rs
> +++ b/ui/src/configuration/permission_path_selector.rs
> @@ -1,12 +1,15 @@
>  use std::rc::Rc;
>
> +use anyhow::Error;
>  use yew::html::IntoPropValue;
>
> -use pwt::prelude::*;
>  use pwt::widget::form::Combobox;
> +use pwt::{prelude::*, AsyncPool};
>
>  use pwt_macros::{builder, widget};
>
> +use crate::pdm_client;
> +
>  static PREDEFINED_PATHS: &[&str] = &[
>      "/",
>      "/access",
> @@ -44,33 +47,78 @@ impl PermissionPathSelector {
>      }
>  }
>
> -enum Msg {}
> +enum Msg {
> +    Prefetched(Vec<String>),
> +    PrefetchFailed,
> +}
>
>  struct PdmPermissionPathSelector {
>      items: Rc<Vec<AttrValue>>,
> +    async_pool: AsyncPool,
>  }
>
> -impl PdmPermissionPathSelector {}
> +impl PdmPermissionPathSelector {
> +    async fn get_view_paths() -> Result<Vec<String>, Error> {
> +        let views = pdm_client().list_views().await?;
> +        let paths: Vec<String> = views
> +            .iter()
> +            .map(|cfg| format!("/view/{}", cfg.id))
> +            .collect();
> +        Ok(paths)
> +    }
> +
> +    async fn get_paths() -> Result<Vec<String>, Error> {
> +        let paths = Self::get_view_paths().await?;
> +        Ok(paths)
> +    }
> +}
>
>  impl Component for PdmPermissionPathSelector {
>      type Message = Msg;
>      type Properties = PermissionPathSelector;
>
> -    fn create(_ctx: &Context<Self>) -> Self {
> -        // TODO: fetch resources & remotes from the backend to improve the pre-defined selection of
> -        // acl paths
> -        Self {
> -            items: Rc::new(
> -                PREDEFINED_PATHS
> -                    .iter()
> -                    .map(|i| AttrValue::from(*i))
> -                    .collect(),
> -            ),
> -        }
> +    fn create(ctx: &Context<Self>) -> Self {
> +        let base_items: Vec<AttrValue> = PREDEFINED_PATHS
> +            .iter()
> +            .map(|i| AttrValue::from(*i))
> +            .collect();
> +
> +        let this = Self {
> +            items: Rc::new(base_items),
> +            async_pool: AsyncPool::new(),
> +        };
> +
> +        let link = ctx.link().clone();
> +        this.async_pool.spawn(async move {
> +            let paths = Self::get_paths().await;
> +            match paths {
> +                Ok(paths) => {
> +                    link.send_message(Msg::Prefetched(paths));
> +                }
> +                Err(_) => {
> +                    link.send_message(Msg::PrefetchFailed);
> +                }

this is minor, but at least for me cargo fmt will also accept the
following formatting:

            match paths {
                Ok(paths) => link.send_message(Msg::Prefetched(paths)),
                Err(_) => link.send_message(Msg::PrefetchFailed),
            }

imo that's a little neater.

> +            }
> +        });

im generally not too big of a fan of this "this" pattern in rust and
here there is no need for it, you can simply do:

    let async_pool = AsyncPool::new();
    async_pool.spawn(..);
    Self {
        items: Rc::new(..),
        async_pool,
    }

you may need to change `async_pool` in the struct to `_async_pool`
because then this member is never directly accessed.

> +
> +        this
>      }
>
> -    fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
> -        false
> +    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
> +        match msg {
> +            Msg::Prefetched(paths) => {
> +                let mut items: Vec<String> = Vec::with_capacity(self.items.len() + paths.len());
> +
> +                items.extend(self.items.iter().map(|item| item.to_string()));
> +                items.extend(paths.into_iter());
> +                items.sort_by(|a, b| a.to_lowercase().cmp(&b.to_lowercase()));
> +
> +                self.items = Rc::new(items.into_iter().map(AttrValue::from).collect());

this could be expressed more neatly as:

```
            Msg::Prefetched(paths) => {
                let items = Rc::make_mut(&mut self.items);
                items.extend(paths.into_iter().map(AttrValue::from));
                items.sort_by_key(|k| k.to_lowercase());
                true
            }
```

this has a couple of advantages:

* we don't move the original items from `AttrValue` to a `String` back
  to `AttrValue`, saving on allocations.
* `Rc::make_mut()` will only clone if there are other pointers to the
  same allocation. since the is an `Rc::clone` in the view method below,
  this probably won't actually impact much here from what i can tell,
  but it still seems sensible to me to only force a new `Rc<Vec>` if
  absolutely needed.
* uses `sort_by_key` instead of `sort_by` is more concise and also
  recommended by clippy.

> +
> +                true
> +            }
> +            Msg::PrefetchFailed => false,
> +        }
>      }
>
>      fn view(&self, ctx: &Context<Self>) -> Html {





  reply	other threads:[~2026-03-26 11:16 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-25 15:35 [RFC PATCH datacenter-manager 0/3] ui: acl: pre-populate permission path selector Shan Shaji
2026-03-25 15:35 ` [RFC PATCH datacenter-manager 1/3] pdm-client: add `list_views` function to fetch views list Shan Shaji
2026-03-25 15:35 ` [RFC PATCH datacenter-manager 2/3] ui: acl: list granular level permission paths for views Shan Shaji
2026-03-26 11:16   ` Shannon Sterz [this message]
2026-03-25 15:35 ` [RFC PATCH datacenter-manager 3/3] ui: acl: list granular level permission paths for resources Shan Shaji
2026-03-26 11:16   ` Shannon Sterz
2026-03-26 13:58     ` Shan Shaji
2026-03-27 10:21   ` Lukas Wagner
2026-03-30  9:07     ` Shan Shaji
2026-03-31  9:59       ` Shan Shaji

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DHCO6BBURS8V.388FJXGOBCAX5@proxmox.com \
    --to=s.sterz@proxmox.com \
    --cc=pdm-devel@lists.proxmox.com \
    --cc=s.shaji@proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal