Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub async fn execute_cmd(opts: MainOpts) -> Result<(), CmdError> {

SubCommand::Dataset(input) => input.exec(ctx).await?,
SubCommand::Job(input) => input.exec(ctx).await?,
SubCommand::Launcher(input) => input.exec(ctx).await?,
SubCommand::Logout(input) => input.exec(&ctx).await?,
};
Ok(())
Expand Down
8 changes: 8 additions & 0 deletions src/cli/cmd.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod dataset;
pub mod job;
pub mod launcher;
pub mod login;
pub mod logout;
pub mod project;
Expand Down Expand Up @@ -85,6 +86,8 @@ pub enum CmdError {

#[snafu(display("Job - {}", source))]
Job { source: job::Error },
#[snafu(display("Launcher - {}", source))]
Launcher { source: launcher::Error },

#[snafu(display("Logout - {}", source))]
Logout { source: logout::Error },
Expand All @@ -95,6 +98,11 @@ impl From<job::Error> for CmdError {
CmdError::Job { source }
}
}
impl From<launcher::Error> for CmdError {
fn from(source: launcher::Error) -> Self {
CmdError::Launcher { source }
}
}

impl From<version::Error> for CmdError {
fn from(source: version::Error) -> Self {
Expand Down
2 changes: 1 addition & 1 deletion src/cli/cmd/job/stop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use snafu::{ResultExt, Snafu};
/// Stop a running non-interactive session.
#[derive(Parser, Debug)]
pub struct Input {
/// The launcher to use for launching the job.
/// The id of the job to stop
#[arg(value_hint=ValueHint::Other, add = ArgValueCompleter::new(complete_job_name))]
pub job_id: String,
}
Expand Down
32 changes: 32 additions & 0 deletions src/cli/cmd/launcher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
pub mod list;

use super::Context;
use clap::Parser;
use snafu::{ResultExt, Snafu};

#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("Error listing launchers: {}", source))]
List { source: list::Error },
}

/// Sub command for managing launchers
#[derive(Parser, Debug)]
pub struct Input {
#[command(subcommand)]
pub subcmd: LauncherCommand,
}

impl Input {
pub async fn exec(&self, ctx: Context) -> Result<(), Error> {
match &self.subcmd {
LauncherCommand::List(input) => input.exec(ctx).await.context(ListSnafu),
}
}
}

#[derive(Parser, Debug)]
pub enum LauncherCommand {
#[command()]
List(list::Input),
}
36 changes: 36 additions & 0 deletions src/cli/cmd/launcher/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use super::Context;
use crate::{
cli::sink::Error as SinkError,
httpclient::{self, data::SessionMode},
};

use clap::Parser;

use snafu::{ResultExt, Snafu};

/// Listing launchers.
///
/// List currently running launchers.
#[derive(Parser, Debug)]
pub struct Input {}

#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("Error writing data: {}", source))]
WriteResult { source: SinkError },

#[snafu(display("Http error: {}", source))]
HttpClient { source: httpclient::Error },
}

impl Input {
pub async fn exec(&self, ctx: Context) -> Result<(), Error> {
let mut result = ctx.client.list_launchers().await.context(HttpClientSnafu)?;

result.retain(|e| e.launcher_type == SessionMode::NonInteractive);
if let Ok(Some(project)) = ctx.resolve_project_context().await {
result.retain(|v| v.project_id == project.id);
}
ctx.write_result(&result).await.context(WriteResultSnafu)
}
}
6 changes: 6 additions & 0 deletions src/cli/cmd/project.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod clone;
pub mod list;

use super::Context;
use clap::Parser;
Expand All @@ -8,6 +9,8 @@ use snafu::{ResultExt, Snafu};
pub enum Error {
#[snafu(display("Error cloning project: {}", source))]
Clone { source: clone::Error },
#[snafu(display("Error listing projects: {}", source))]
List { source: list::Error },
}

/// Sub command for managing projects
Expand All @@ -21,6 +24,7 @@ impl Input {
pub async fn exec(&self, ctx: Context) -> Result<(), Error> {
match &self.subcmd {
ProjectCommand::Clone(input) => input.exec(ctx).await.context(CloneSnafu),
ProjectCommand::List(input) => input.exec(ctx).await.context(ListSnafu),
}
}
}
Expand All @@ -29,4 +33,6 @@ impl Input {
pub enum ProjectCommand {
#[command()]
Clone(clone::Input),
#[command()]
List(list::Input),
}
40 changes: 40 additions & 0 deletions src/cli/cmd/project/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use super::Context;
use crate::{
cli::sink::Error as SinkError,
httpclient::{self},
};

use clap::Parser;

use snafu::{ResultExt, Snafu};

/// Listing projects.
///
/// List all projects owned by a user
#[derive(Parser, Debug)]
pub struct Input {
/// Get all projects, not just ones you're a member of
#[arg(long, short, default_value_t = false)]
pub all: bool,
}

#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("Error writing data: {}", source))]
WriteResult { source: SinkError },

#[snafu(display("Http error: {}", source))]
HttpClient { source: httpclient::Error },
}

impl Input {
pub async fn exec(&self, ctx: Context) -> Result<(), Error> {
let result = ctx
.client
.list_projects(!self.all)
.await
.context(HttpClientSnafu)?;

ctx.write_result(&result).await.context(WriteResultSnafu)
}
}
1 change: 1 addition & 0 deletions src/cli/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ pub fn complete_job_launcher_id(current: &ffi::OsStr) -> Vec<CompletionCandidate
None => None,
};
for launcher in launchers
.0
.iter()
.filter(|e| e.launcher_type == SessionMode::NonInteractive)
.filter(|e| match &project_id {
Expand Down
2 changes: 2 additions & 0 deletions src/cli/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ pub enum SubCommand {

#[command()]
Job(job::Input),
#[command()]
Launcher(launcher::Input),

#[command()]
Logout(logout::Input),
Expand Down
2 changes: 2 additions & 0 deletions src/cli/sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ impl From<serde_json::Error> for Error {
}

impl Sink for ProjectDetails {}
impl Sink for ProjectList {}
impl Sink for SimpleMessage {}
impl Sink for BuildInfo {}
impl Sink for PathEntry {}
impl Sink for UserCode {}
impl Sink for Response {}
impl Sink for SessionStartResponse {}
impl Sink for SessionList {}
impl Sink for LauncherList {}
impl Sink for SessionLogs {}
impl Sink for VersionInfo {}
14 changes: 12 additions & 2 deletions src/httpclient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,16 @@ impl Client {
})
}
}
pub async fn list_projects(&self, direct_member: bool) -> Result<ProjectList, Error> {
let mut url = self.make_url("/api/data/projects")?;
url.query_pairs_mut()
.append_pair("direct_member", &direct_member.to_string());
let req = self.set_bearer_token(self.client.get(url.clone())).await?;

self.run_request::<Vec<ProjectDetails>>(req, url)
.await
.map(ProjectList)
}

pub async fn get_namespace(
&self,
Expand Down Expand Up @@ -455,10 +465,10 @@ impl Client {
Ok(r)
}

pub async fn list_launchers(&self) -> Result<Vec<SessionLauncher>, Error> {
pub async fn list_launchers(&self) -> Result<LauncherList, Error> {
let path = "/api/data/session_launchers";
let result = self.json_get::<Vec<SessionLauncher>>(path).await?;
Ok(result)
Ok(LauncherList(result))
}

pub async fn get_launcher(&self, id: &str) -> Result<Option<SessionLauncher>, Error> {
Expand Down
94 changes: 94 additions & 0 deletions src/httpclient/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,52 @@ impl fmt::Display for SessionStartRequest {
)
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct LauncherList(pub Vec<SessionLauncher>);
impl LauncherList {
pub fn filter<F>(self, f: F) -> LauncherList
where
F: Fn(&SessionLauncher) -> bool,
{
let l: Vec<SessionLauncher> = self.0.into_iter().filter(|v| f(v)).collect();
LauncherList(l)
}

pub fn retain<F>(&mut self, f: F)
where
F: Fn(&SessionLauncher) -> bool,
{
self.0.retain(|v| f(v));
}
}
fn create_launcher_table<'a, I>(data: I) -> Table
where
I: IntoIterator<Item = &'a SessionLauncher>,
{
let mut builder = Builder::default();
for r in data {
let data = vec![&r.name, r.id.as_str(), &r.project_id];
builder.push_record(data);
}
builder.insert_record(0, vec!["Launcher", "Id", "Project Id"]);

let mut table = builder.build();
let settings = Settings::default().with(Style::sharp());

table.with(settings);
table
}

impl fmt::Display for LauncherList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.is_empty() {
write!(f, "No launchers found.")
} else {
let table = create_launcher_table(&self.0);
write!(f, "{}", table)
}
}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SessionList(pub Vec<SessionStartResponse>);
Expand Down Expand Up @@ -290,6 +336,54 @@ impl fmt::Display for ProjectDetails {
}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ProjectList(pub Vec<ProjectDetails>);
impl ProjectList {
pub fn filter<F>(self, f: F) -> ProjectList
where
F: Fn(&ProjectDetails) -> bool,
{
let l: Vec<ProjectDetails> = self.0.into_iter().filter(|v| f(v)).collect();
ProjectList(l)
}

pub fn retain<F>(&mut self, f: F)
where
F: Fn(&ProjectDetails) -> bool,
{
self.0.retain(|v| f(v));
}
}

fn create_project_table<'a, I>(data: I) -> Table
where
I: IntoIterator<Item = &'a ProjectDetails>,
{
let mut builder = Builder::default();
for r in data {
let data = vec![&r.name, r.id.as_str(), &r.namespace, &r.slug];
builder.push_record(data);
}
builder.insert_record(0, vec!["Project", "Id", "Namespace", "Slug"]);

let mut table = builder.build();
let settings = Settings::default().with(Style::sharp());

table.with(settings);
table
}

impl fmt::Display for ProjectList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.is_empty() {
write!(f, "No projects found.")
} else {
let table = create_project_table(&self.0);
write!(f, "{}", table)
}
}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RenkuError {
pub code: i32,
Expand Down
Loading