Skip to main content

edc_web_components/pages/
policy_page.rs

1use crate::components::ListPolicies;
2use crate::contexts::{PolicyLibraryItem, use_edc_connector_context, use_policy_library_context};
3use crate::models::PolicyDefinitionItem;
4use edc_connector_client::EdcConnectorApiVersion;
5use edc_connector_client::types::policy::NewPolicyDefinition;
6use edc_connector_client::types::query::Query;
7use log::error;
8use patternfly_yew::prelude::*;
9use uuid::Uuid;
10use yew::platform::spawn_local;
11use yew::prelude::*;
12use yew::suspense::use_future_with;
13
14#[derive(Clone, Debug, PartialEq, Properties)]
15pub struct PolicyPageProps {
16  #[prop_or("List Policies".to_string())]
17  pub title: String,
18  #[prop_or(Some("A policy consists of one or more rules. The policy is then bound to an asset during the creation of the contract definition.".to_string()))]
19  pub tag_line: Option<String>,
20  #[prop_or("Create a Policy".to_string())]
21  pub create_title: String,
22  #[prop_or("Import from Library".to_string())]
23  pub open_library_title: String,
24  pub on_new_policy: Callback<()>,
25  pub onshow: Callback<String>,
26}
27
28#[component]
29pub fn PolicyPage(props: &PolicyPageProps) -> Html {
30  let refresh = use_state(|| 0usize);
31  let offset = use_state(|| 0usize);
32  let limit = use_state(|| 10usize);
33
34  let onoffset = use_callback(
35    (refresh.clone(), offset.setter()),
36    |offset, (refresh, offset_setter)| {
37      offset_setter.set(offset);
38      refresh.set(**refresh + 1);
39    },
40  );
41
42  let onlimit = use_callback(
43    (refresh.clone(), limit.setter()),
44    |limit, (refresh, limit_setter)| {
45      limit_setter.set(limit);
46      refresh.set(**refresh + 1);
47    },
48  );
49
50  let edc_connector_context = use_edc_connector_context();
51
52  let ondelete = use_callback(
53    (refresh.clone(), edc_connector_context.clone()),
54    |policy_id: String, (refresh, edc_connector_context)| {
55      let refresh = refresh.clone();
56      let edc_connector_context = edc_connector_context.clone();
57      let policy_id = policy_id.clone();
58
59      spawn_local(async move {
60        if let Some(client) = edc_connector_context.get_client() {
61          let _ = client
62            .policies(EdcConnectorApiVersion::V4)
63            .delete(&policy_id)
64            .await;
65          log::warn!("Deleted policy {} - {}", policy_id, *refresh + 1);
66          refresh.set(*refresh + 1);
67        }
68      });
69    },
70  );
71
72  let tag_line = props
73    .tag_line
74    .as_ref()
75    .map(|tag_line| html!(<p>{ tag_line }</p>))
76    .unwrap_or_default();
77
78  let policy_library_context = use_policy_library_context();
79
80  let expanded = use_state_eq(|| false);
81  let onclick = use_callback(expanded.clone(), |_, expanded| {
82    expanded.set(!**expanded);
83  });
84
85  let open_policy_library = policy_library_context.as_ref().map(|_| {
86    html_nested!(
87      <SplitItem>
88        <Button icon={Icon::Catalog} {onclick} variant={ButtonVariant::Secondary}>
89          { &props.open_library_title }
90        </Button>
91      </SplitItem>
92    )
93  });
94
95  let import_policy = use_callback(
96    (edc_connector_context.clone(), refresh.clone()),
97    |policy_library_item: PolicyLibraryItem, (edc_connector_context, refresh)| {
98      let edc_connector_context = edc_connector_context.clone();
99      let refresh = refresh.clone();
100
101      let new_policy_definition = NewPolicyDefinition::builder()
102        .id(Uuid::new_v4().to_string())
103        .policy(policy_library_item.policy.clone())
104        .private_property("name", policy_library_item.name)
105        .build();
106
107      spawn_local(async move {
108        if let Some(client) = edc_connector_context.get_client() {
109          match client
110            .policies(EdcConnectorApiVersion::V4)
111            .create(&new_policy_definition)
112            .await
113          {
114            Ok(_) => {
115              refresh.set(*refresh + 1);
116            }
117            Err(error) => {
118              error!("Failed to import Policy: {}", error);
119            }
120          }
121        }
122      })
123    },
124  );
125
126  let panel_content = policy_library_context
127    .clone()
128    .map(move |policy_library_context| {
129      let items = policy_library_context.policy_library().iter().map(|item| {
130        let name = item.name.to_string();
131        let item = item.clone();
132
133        html!(
134          <StackItem>
135            <Button
136              variant={ButtonVariant::Control}
137              icon={Icon::Import}
138              onclick={import_policy.reform(move |_| item.clone())}
139            >
140              { name }
141            </Button>
142          </StackItem>
143        )
144      });
145
146      html!(
147        <Panel>
148          <PanelHeader>{ "Policy Library" }</PanelHeader>
149          <PanelMain>
150            <PanelMainBody>
151              <Stack gutter=true>{ for items }</Stack>
152            </PanelMainBody>
153          </PanelMain>
154        </Panel>
155      )
156    });
157
158  html!(
159    <Stack gutter=true>
160      <StackItem>
161        <Split gutter=true>
162          <SplitItem fill=true>
163            <Title level={Level::H3} size={Size::XXLarge}>{ &props.title }</Title>
164            { tag_line }
165          </SplitItem>
166          { open_policy_library }
167          <SplitItem>
168            <Button
169              icon={Icon::Plus}
170              onclick={props.on_new_policy.reform(|_| ())}
171              variant={ButtonVariant::Primary}
172            >
173              { &props.create_title }
174            </Button>
175          </SplitItem>
176        </Split>
177      </StackItem>
178      <StackItem>
179        <Drawer expanded={*expanded} inline=true>
180          <DrawerContent {panel_content}>
181            <DrawerContentBody>
182              <Suspense>
183                <PolicyPageInner
184                  offset={*offset}
185                  limit={*limit}
186                  {onoffset}
187                  {onlimit}
188                  {ondelete}
189                  onshow={props.onshow.clone()}
190                  force_refresh={*refresh}
191                />
192              </Suspense>
193            </DrawerContentBody>
194          </DrawerContent>
195        </Drawer>
196      </StackItem>
197    </Stack>
198  )
199}
200
201#[derive(Clone, Debug, PartialEq, Properties)]
202pub struct PolicyPageInnerProps {
203  pub offset: usize,
204  pub limit: usize,
205  pub onoffset: Callback<usize>,
206  pub onlimit: Callback<usize>,
207  pub ondelete: Callback<String>,
208  pub onshow: Callback<String>,
209  pub force_refresh: usize,
210}
211
212#[component]
213pub fn PolicyPageInner(props: &PolicyPageInnerProps) -> HtmlResult {
214  let edc_connector_context = use_edc_connector_context();
215
216  let policy_definition_items = use_future_with(
217    (
218      edc_connector_context,
219      props.limit,
220      props.offset,
221      props.force_refresh,
222    ),
223    |parameters| async move {
224      let (edc_connector_context, limit, offset, _) = (*parameters).clone();
225
226      let query = Query::builder()
227        .limit(limit as u32)
228        .offset(offset as u32)
229        .build();
230
231      if let Some(client) = edc_connector_context.get_client() {
232        client
233          .policies(EdcConnectorApiVersion::V4)
234          .query(query)
235          .await
236          .unwrap_or_default()
237          .into_iter()
238          .map(PolicyDefinitionItem::from)
239          .collect::<Vec<_>>()
240      } else {
241        vec![]
242      }
243    },
244  )?;
245
246  let policy_definition_items = (*policy_definition_items).clone();
247
248  Ok(html!(
249    <ListPolicies
250      policy_definition_items={policy_definition_items}
251      offset={props.offset}
252      limit={props.limit}
253      onoffset={props.onoffset.clone()}
254      onlimit={props.onlimit.clone()}
255      ondelete={props.ondelete.clone()}
256      onshow={props.onshow.clone()}
257    />
258  ))
259}