edc-web-components 0.8.0

A Rust Web Components with patternfly-yew client for EDC
Documentation
use crate::components::SelectedFederatedCatalogOffer;
use crate::models::AssetItem;
use edc_federated_catalog_client::{FederatedCatalogClient, FederatedCatalogClientVersion};
use std::{ops::Deref, rc::Rc};
use yew::prelude::*;
use yew::suspense::use_future_with;
use yew_oauth2::hook::use_latest_access_token;

#[derive(Debug, PartialEq, Clone)]
pub enum EdcFederatedCatalogAssetsAction {
  SetAssetsAndSelectedOffers(Vec<(AssetItem, SelectedFederatedCatalogOffer)>),
}

#[derive(Clone, PartialEq)]
pub struct EdcFederatedCatalogAssetsContextState {
  asset_items: Vec<AssetItem>,
  selected_offers: Vec<SelectedFederatedCatalogOffer>,
}

impl EdcFederatedCatalogAssetsContextState {
  pub fn asset(&self, asset_id: &str) -> Option<&AssetItem> {
    self
      .asset_items
      .iter()
      .find(|asset_item| asset_item.id == asset_id)
  }

  pub fn asset_items(&self) -> &[AssetItem] {
    &self.asset_items
  }

  pub fn selected_offers(&self) -> &[SelectedFederatedCatalogOffer] {
    &self.selected_offers
  }
}

impl Reducible for EdcFederatedCatalogAssetsContextState {
  type Action = EdcFederatedCatalogAssetsAction;

  fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
    let mut new_self = self.deref().clone();

    let EdcFederatedCatalogAssetsAction::SetAssetsAndSelectedOffers(assets_and_selected_offers) =
      action;

    let (assets, selected_offers) = assets_and_selected_offers.into_iter().unzip();

    new_self.asset_items = assets;
    new_self.selected_offers = selected_offers;

    new_self.into()
  }
}

#[derive(Properties, PartialEq)]
pub struct EdcFederatedCatalogAssetsContextProviderProps {
  #[prop_or_default]
  pub children: Html,
}

#[component]
pub fn EdcFederatedCatalogAssetsContextProvider(
  props: &EdcFederatedCatalogAssetsContextProviderProps,
) -> Html {
  let did_resolver_context = use_reducer(move || EdcFederatedCatalogAssetsContextState {
    asset_items: vec![],
    selected_offers: vec![],
  });

  html! {
    <ContextProvider<EdcFederatedCatalogAssetsContext> context={did_resolver_context}>
      { props.children.clone() }
    </ContextProvider<EdcFederatedCatalogAssetsContext >>
  }
}

pub type EdcFederatedCatalogAssetsContext = UseReducerHandle<EdcFederatedCatalogAssetsContextState>;

#[hook]
pub fn use_edc_federated_catalog_assets_context() -> Option<EdcFederatedCatalogAssetsContext> {
  use_context::<EdcFederatedCatalogAssetsContext>()
}

#[derive(Properties, PartialEq)]
pub struct EdcFederatedCatalogAssetsFetcherProps {
  #[prop_or("/federated-catalog".to_string())]
  pub federated_catalog_endpoint: String,
  #[prop_or_default]
  pub search: Option<String>,
  #[prop_or_default]
  pub dcterm_types: Vec<String>,
  #[prop_or_default]
  pub force_refresh: usize,
}

#[component]
pub fn EdcFederatedCatalogAssetsFetcher(
  props: &EdcFederatedCatalogAssetsFetcherProps,
) -> HtmlResult {
  let latest_access_token_context = use_latest_access_token().unwrap();
  let edc_federated_catalog_assets_context = use_edc_federated_catalog_assets_context();

  let _ = use_future_with(
    (
      props.force_refresh,
      props.federated_catalog_endpoint.clone(),
      props.search.clone(),
      props.dcterm_types.clone(),
      latest_access_token_context.clone(),
      edc_federated_catalog_assets_context.clone(),
    ),
    |parameters| async move {
      let (
        _,
        federated_catalog_endpoint,
        search,
        dcterm_types,
        latest_access_token_context,
        edc_federated_catalog_assets_context,
      ) = (*parameters).clone();

      if let Some(edc_federated_catalog_assets_context) = edc_federated_catalog_assets_context {
        let server_url = web_sys::window().unwrap().location().origin().unwrap();
        let federated_catalog_client = FederatedCatalogClient::new(
          reqwest::Client::new(),
          format!("{server_url}{}", federated_catalog_endpoint),
          latest_access_token_context.access_token(),
          FederatedCatalogClientVersion::V4,
        );

        let assets_and_selected_offers = federated_catalog_client
          .list_offers()
          .await
          .unwrap_or_default()
          .iter()
          .flat_map(|federated_catalog_offer| {
            federated_catalog_offer
              .dataset
              .clone()
              .into_iter()
              .map(|dataset| {
                let dataset_id = dataset.id.clone();
                let asset_item = AssetItem::from(dataset);

                let selected_offer = SelectedFederatedCatalogOffer {
                  originator: federated_catalog_offer.originator.clone(),
                  provider_id: federated_catalog_offer.participant_id.id.clone(),
                  dataset_id,
                };

                (asset_item, selected_offer)
              })
              .filter(|(asset_item, _)| asset_item.is_filtered(&search, &dcterm_types))
              .collect::<Vec<_>>()
          })
          .collect::<Vec<_>>();

        edc_federated_catalog_assets_context.dispatch(
          EdcFederatedCatalogAssetsAction::SetAssetsAndSelectedOffers(assets_and_selected_offers),
        );
      }
    },
  )?;

  Ok(html!())
}