Skip to content
This repository has been archived by the owner on Jul 25, 2022. It is now read-only.

Commit

Permalink
Implement GUI for Stratagem reports (#2334)
Browse files Browse the repository at this point in the history
Signed-off-by: Igor Pashev <pashev.igor@gmail.com>

Co-authored-by: Joe Grund <jgrund@whamcloud.io>
  • Loading branch information
ip1981 and jgrund committed Oct 23, 2020
1 parent b03c58b commit 10d8894
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 8 deletions.
18 changes: 15 additions & 3 deletions iml-gui/crate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,11 @@ fn after_mount(url: Url, orders: &mut impl Orders<Msg, GMsg>) -> AfterMount<Mode
pub fn routes(url: Url) -> Option<Msg> {
let pth = url.get_path();

// Urls which start with `static` are files => treat them as external links.
if pth.starts_with(&[STATIC_PATH.into()]) || pth.starts_with(&[HELP_PATH.into()]) {
// Some URLs are files => treat them as external links.
if pth.starts_with(&[STATIC_PATH.into()])
|| pth.starts_with(&[HELP_PATH.into()])
|| pth.starts_with(&["api".into(), "report".into()])
{
return None;
}
Some(Msg::RouteChanged(url))
Expand Down Expand Up @@ -364,7 +367,9 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg, GMsg>)
}
Msg::LoadPage => {
if model.loading.loaded() && !model.page.is_active(&model.route) {
if model.route == Route::Snapshots && !model.conf.use_snapshots {
if (model.route == Route::Snapshots && !model.conf.use_snapshots)
|| (model.route == Route::Stratagem && !model.conf.use_stratagem)
{
model.route = Route::NotFound;
}

Expand Down Expand Up @@ -1014,6 +1019,13 @@ fn view(model: &Model) -> Vec<Node<Msg>> {
.map_msg(page::Msg::Snapshots),
)
.els(),
Page::Stratagem(x) => main_panels(
model,
page::stratagem::view(x, model.auth.get_session())
.els()
.map_msg(page::Msg::Stratagem),
)
.els(),
};

// command modal is the global singleton, therefore is being showed here
Expand Down
13 changes: 13 additions & 0 deletions iml-gui/crate/src/page/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod server_dashboard;
pub mod servers;
pub mod sfa_enclosure;
pub mod snapshot;
pub mod stratagem;
pub mod target;
pub mod target_dashboard;
pub mod targets;
Expand Down Expand Up @@ -79,6 +80,7 @@ pub(crate) enum Page {
Volume(volume::Model),
SfaEnclosure(sfa_enclosure::Model),
Snapshots(snapshot::Model),
Stratagem(stratagem::Model),
}

impl Page {
Expand Down Expand Up @@ -110,6 +112,7 @@ impl Page {
Self::Volume(m) => format!("Volume: {}", &m.id),
Self::SfaEnclosure(m) => format!("Sfa Enclosure: {}", &m.id),
Self::Snapshots(_) => "Snapshots".into(),
Self::Stratagem(_) => "Stratagem".into(),
}
}
}
Expand Down Expand Up @@ -232,6 +235,7 @@ impl<'a> From<(&ArcCache, &Conf, &Route<'a>)> for Page {
.map(|id| Self::SfaEnclosure(sfa_enclosure::Model { id }))
.unwrap_or_default(),
Route::Snapshots => Self::Snapshots(snapshot::Model::default()),
Route::Stratagem => Self::Stratagem(stratagem::Model::default()),
}
}
}
Expand Down Expand Up @@ -301,6 +305,9 @@ impl Page {
Self::Snapshots(m) => {
snapshot::init(cache, m, &mut orders.proxy(Msg::Snapshots));
}
Self::Stratagem(m) => {
stratagem::init(m, &mut orders.proxy(Msg::Stratagem));
}
_ => {}
};
}
Expand Down Expand Up @@ -331,6 +338,7 @@ pub enum Msg {
Volumes(volumes::Msg),
SfaEnclosure(sfa_enclosure::Msg),
Snapshots(snapshot::Msg),
Stratagem(stratagem::Msg),
}

pub(crate) fn update(msg: Msg, page: &mut Page, cache: &ArcCache, orders: &mut impl Orders<Msg, GMsg>) {
Expand Down Expand Up @@ -399,6 +407,11 @@ pub(crate) fn update(msg: Msg, page: &mut Page, cache: &ArcCache, orders: &mut i
snapshot::update(msg, m, &mut orders.proxy(Msg::Snapshots))
}
}
Msg::Stratagem(msg) => {
if let Page::Stratagem(m) = page {
stratagem::update(msg, m, &mut orders.proxy(Msg::Stratagem))
}
}
Msg::About(_)
| Msg::Jobstats(_)
| Msg::OstPool(_)
Expand Down
16 changes: 13 additions & 3 deletions iml-gui/crate/src/page/partial/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn menu_icon<T>(icon_name: &str) -> Node<T> {
)
}

fn nav_manage_dropdown(open: bool, use_snapshots: bool) -> Node<Msg> {
fn nav_manage_dropdown(open: bool, conf: &iml_wire_types::Conf) -> Node<Msg> {
if !open {
return empty![];
}
Expand Down Expand Up @@ -78,7 +78,7 @@ fn nav_manage_dropdown(open: bool, use_snapshots: bool) -> Node<Msg> {
At::Href => Route::Mgt.to_href(),
},
],
if use_snapshots {
if conf.use_snapshots {
li![
a![&cls, "Snapshots"],
attrs! {
Expand All @@ -88,6 +88,16 @@ fn nav_manage_dropdown(open: bool, use_snapshots: bool) -> Node<Msg> {
} else {
empty![]
},
if conf.use_stratagem {
li![
a![&cls, "Stratagem"],
attrs! {
At::Href => Route::Stratagem.to_href(),
},
]
} else {
empty![]
},
li![
a![&cls, "Users"],
attrs! {
Expand Down Expand Up @@ -155,7 +165,7 @@ fn main_menu_items(model: &Model) -> Node<Msg> {
),
],
],
nav_manage_dropdown(model.manage_menu_state.is_open(), model.conf.use_snapshots),
nav_manage_dropdown(model.manage_menu_state.is_open(), &model.conf),
]
),
]
Expand Down
4 changes: 2 additions & 2 deletions iml-gui/crate/src/page/snapshot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ mod list_retention;
mod take;

use crate::{
components::restrict,
components::{
attrs, font_awesome, font_awesome_outline, form, paging, panel, resource_links, table, tooltip, Placement,
attrs, font_awesome, font_awesome_outline, form, paging, panel, resource_links, restrict, table, tooltip,
Placement,
},
extensions::{MergeAttrs as _, NodeExt as _},
generated::css_classes::C,
Expand Down
33 changes: 33 additions & 0 deletions iml-gui/crate/src/page/stratagem/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use crate::GMsg;
use iml_wire_types::Session;
use seed::{prelude::*, *};

pub mod report;

#[derive(Default, Debug)]
pub struct Model {
report: report::Model,
}

#[derive(Clone, Debug)]
pub enum Msg {
Report(report::Msg),
}

pub fn view(model: &Model, session: Option<&Session>) -> impl View<Msg> {
report::view(&model.report, session).map_msg(Msg::Report)
}

pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg, GMsg>) {
match msg {
Msg::Report(m) => report::update(m, &mut model.report, &mut orders.proxy(Msg::Report)),
}
}

pub fn init(model: &mut Model, orders: &mut impl Orders<Msg, GMsg>) {
report::init(&mut model.report, &mut orders.proxy(Msg::Report));
}
204 changes: 204 additions & 0 deletions iml-gui/crate/src/page/stratagem/report.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
use crate::{
components::{font_awesome, paging, panel, restrict, table},
extensions::MergeAttrs as _,
generated::css_classes::C,
sleep_with_handle, GMsg, RequestExt,
};
use futures::channel::oneshot;
use iml_graphql_queries::{stratagem, Response};
use iml_wire_types::{GroupType, Session, StratagemReport};
use number_formatter::format_bytes;
use seed::{prelude::*, *};
use std::{cmp::Ordering, time::Duration};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortField {
ModifyTime,
Size,
Filename,
}

impl Default for SortField {
fn default() -> Self {
Self::ModifyTime
}
}

#[derive(Default, Debug)]
pub struct Model {
pager: paging::Model,
rows: Vec<StratagemReport>,
sort: (SortField, paging::Dir),
cancel: Option<oneshot::Sender<()>>,
}

#[derive(Clone, Debug)]
pub enum Msg {
FetchReports,
Reports(fetch::ResponseDataResult<Response<stratagem::list_reports::Resp>>),
Page(paging::Msg),
Sort,
SortBy(table::SortBy<SortField>),
DeleteReport(String),
ReportDeleted(fetch::ResponseDataResult<Response<stratagem::delete_report::Resp>>),
Noop,
}

pub fn view(model: &Model, session: Option<&Session>) -> Node<Msg> {
if model.rows.is_empty() {
return empty![];
}

panel::view(
h3![class![C.py_4, C.font_normal, C.text_lg], "Stratagem Reports"],
div![
table::wrapper_view(vec![
table::thead_view(vec![
table::sort_header("Filename", SortField::Filename, model.sort.0, model.sort.1)
.map_msg(Msg::SortBy),
table::sort_header("Size", SortField::Size, model.sort.0, model.sort.1).map_msg(Msg::SortBy),
table::sort_header("Modify Time", SortField::ModifyTime, model.sort.0, model.sort.1)
.map_msg(Msg::SortBy),
restrict::view(session, GroupType::FilesystemAdministrators, th![]),
]),
tbody![model.rows[model.pager.range()].iter().map(|x| {
tr![
td![
table::td_cls(),
a![
class![C.text_blue_500, C.hover__underline],
attrs! {
At::Download => x.filename,
At::Href => format!("/api/report/{}", x.filename)},
x.filename
]
],
table::td_center(plain![format_bytes(x.size as f64, 0)]),
table::td_center(plain![x.modify_time.format("%m/%d/%Y %H:%M:%S").to_string()]),
restrict::view(
session,
GroupType::FilesystemAdministrators,
button![
class![
C.bg_blue_500,
C.duration_300,
C.flex,
C.hover__bg_blue_400,
C.items_center,
C.px_6,
C.py_2,
C.rounded_sm,
C.text_white,
C.transition_colors,
],
font_awesome(class![C.w_3, C.h_3, C.inline, C.mr_2], "trash"),
"Delete File",
simple_ev(Ev::Click, Msg::DeleteReport(x.filename.clone()))
]
)
]
})]
])
.merge_attrs(class![C.my_6]),
div![
class![C.flex, C.justify_end, C.py_1, C.pr_3],
paging::limit_selection_view(&model.pager).map_msg(Msg::Page),
paging::page_count_view(&model.pager),
paging::next_prev_view(&model.pager).map_msg(Msg::Page)
]
],
)
}

pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg, GMsg>) {
match msg {
Msg::Page(m) => {
paging::update(m, &mut model.pager, &mut orders.proxy(Msg::Page));
}
Msg::FetchReports => {
model.cancel = None;
let query = stratagem::list_reports::build();
let req = fetch::Request::graphql_query(&query);

orders.perform_cmd(req.fetch_json_data(Msg::Reports));
}
Msg::Reports(x) => {
match x {
Ok(Response::Data(d)) => {
model.rows = d.data.stratagem_reports;
orders
.proxy(Msg::Page)
.send_msg(paging::Msg::SetTotal(model.rows.len()));
orders.send_msg(Msg::Sort);
}
Ok(Response::Errors(e)) => {
error!("Stratagem reports were not fetched: ", e);
}
Err(e) => {
error!("Stratagem reports were not fetched: ", e);
}
}
let (cancel, fut) = sleep_with_handle(Duration::from_secs(30), Msg::FetchReports, Msg::Noop);
model.cancel = Some(cancel);
orders.perform_cmd(fut);
}
Msg::DeleteReport(filename) => {
if let Ok(true) = window().confirm_with_message(&format!("Delete {}?", filename)) {
let query = stratagem::delete_report::build(filename);
let req = fetch::Request::graphql_query(&query);

orders.perform_cmd(req.fetch_json_data(Msg::ReportDeleted));
}
}
Msg::ReportDeleted(x) => {
match x {
Ok(Response::Data(_)) => {}
Ok(Response::Errors(e)) => {
error!("Error deleting the report: ", e);
}
Err(e) => {
error!("Error deleting the report: ", e);
}
}
orders.send_msg(Msg::FetchReports);
}
Msg::SortBy(table::SortBy(x)) => {
let dir = if x == model.sort.0 {
model.sort.1.next()
} else {
paging::Dir::default()
};

model.sort = (x, dir);

orders.send_msg(Msg::Sort);
}
Msg::Sort => {
let sort_fn = match model.sort.0 {
SortField::Filename => {
|a: &StratagemReport, b: &StratagemReport| natord::compare(&a.filename, &b.filename)
}
SortField::Size => |a: &StratagemReport, b: &StratagemReport| a.size.cmp(&b.size),
SortField::ModifyTime => |a: &StratagemReport, b: &StratagemReport| a.modify_time.cmp(&b.modify_time),
};

let sort_dir_fn = if paging::Dir::Desc == model.sort.1 {
Box::new(|a: &StratagemReport, b: &StratagemReport| sort_fn(b, a))
as Box<dyn FnMut(&StratagemReport, &StratagemReport) -> Ordering>
} else {
Box::new(|a: &StratagemReport, b: &StratagemReport| sort_fn(a, b))
};

model.rows.sort_by(sort_dir_fn);
}
Msg::Noop => {}
}
}

pub fn init(model: &mut Model, orders: &mut impl Orders<Msg, GMsg>) {
model.sort.1 = paging::Dir::Desc;
orders.send_msg(Msg::FetchReports);
}
Loading

0 comments on commit 10d8894

Please sign in to comment.