edc_web_components/pages/
policy_page.rs1use 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}