Skip to content

Commit

Permalink
Refactor endpoints URL functions (#367)
Browse files Browse the repository at this point in the history
This patch combines job.rs, project.rs, and common.rs into one file,
endpoints.rs, so that we can see all of the API URLs in one location.

It also removes a bit of unused code.
  • Loading branch information
kylewillmon authored May 11, 2022
1 parent 52845c9 commit 472c151
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 143 deletions.
1 change: 0 additions & 1 deletion cli/src/api/common.rs

This file was deleted.

64 changes: 64 additions & 0 deletions cli/src/api/endpoints.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/// API endpoint paths
use super::{JobId, PackageDescriptor};

const API_PATH: &str = "api/v0";

/// PUT /job
pub fn put_submit_package(api_uri: &str) -> String {
format!("{api_uri}/{API_PATH}/job")
}

/// GET /job/heartbeat
pub fn get_ping(api_uri: &str) -> String {
format!("{api_uri}/{API_PATH}/job/heartbeat")
}

/// GET /job/
pub fn get_all_jobs_status(api_uri: &str, limit: u32) -> String {
format!("{api_uri}/{API_PATH}/job/?limit={limit}&verbose=1")
}

/// GET /job/<job_id>
pub fn get_job_status(api_uri: &str, job_id: &JobId, verbose: bool) -> String {
if verbose {
format!("{api_uri}/{API_PATH}/job/{job_id}?verbose=True")
} else {
format!("{api_uri}/{API_PATH}/job/{job_id}")
}
}

/// GET /job/packages/<type>/<name>/<version>
pub fn get_package_status(api_uri: &str, pkg: &PackageDescriptor) -> String {
let name_escaped = pkg.name.replace('/', "~");
let PackageDescriptor {
package_type,
version,
..
} = pkg;
format!("{api_uri}/{API_PATH}/job/packages/{package_type}/{name_escaped}/{version}")
}

/// GET /job/projects/name/<pkg_id>
pub fn get_project_details(api_uri: &str, pkg_id: &str) -> String {
format!("{api_uri}/{API_PATH}/job/projects/name/{pkg_id}")
}

/// GET /job/projects/overview
pub fn get_project_summary(api_uri: &str) -> String {
format!("{api_uri}/{API_PATH}/job/projects/overview")
}

/// PUT /job/projects
pub fn put_create_project(api_uri: &str) -> String {
format!("{api_uri}/{API_PATH}/job/projects")
}

/// GET /settings/current-user
pub(crate) fn get_user_settings(api_uri: &str) -> String {
format!("{api_uri}/{API_PATH}/settings/current-user")
}

/// PUT /settings/current-user
pub(crate) fn put_user_settings(api_uri: &str) -> String {
format!("{api_uri}/{API_PATH}/settings/current-user")
}
52 changes: 0 additions & 52 deletions cli/src/api/job.rs

This file was deleted.

70 changes: 15 additions & 55 deletions cli/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,13 @@ use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use thiserror::Error as ThisError;

mod common;
mod job;
mod project;
mod user_settings;
mod endpoints;

use crate::auth::fetch_oidc_server_settings;
use crate::auth::handle_auth_flow;
use crate::auth::handle_refresh_tokens;
use crate::auth::{AuthAction, UserInfo};
use crate::config::AuthInfo;
use crate::types::AuthStatusResponse;
use crate::types::PingResponse;

type Result<T> = std::result::Result<T, PhylumApiError>;
Expand Down Expand Up @@ -73,10 +69,6 @@ impl PhylumApi {
self.send_request(Method::PUT, path, Some(s)).await
}

async fn delete<T: DeserializeOwned>(&self, path: String) -> Result<T> {
self.send_request::<_, ()>(Method::DELETE, path, None).await
}

async fn send_request<T: DeserializeOwned, B: Serialize>(
&self,
method: Method,
Expand Down Expand Up @@ -171,19 +163,11 @@ impl PhylumApi {
/// Ping the system and verify it's up
pub async fn ping(&mut self) -> Result<String> {
Ok(self
.get::<PingResponse>(job::get_ping(&self.api_uri))
.get::<PingResponse>(endpoints::get_ping(&self.api_uri))
.await?
.msg)
}

/// Check auth status of the current user
pub async fn auth_status(&mut self) -> Result<bool> {
Ok(self
.get::<AuthStatusResponse>(job::get_auth_status(&self.api_uri))
.await?
.authenticated)
}

/// Get information about the authenticated user
pub async fn user_info(&self, auth_info: &AuthInfo) -> Result<UserInfo> {
let oidc_settings = fetch_oidc_server_settings(auth_info).await?;
Expand All @@ -194,7 +178,7 @@ impl PhylumApi {
pub async fn create_project(&mut self, name: &str) -> Result<ProjectId> {
Ok(self
.put::<CreateProjectResponse, _>(
project::put_create_project(&self.api_uri),
endpoints::put_create_project(&self.api_uri),
CreateProjectRequest {
name: name.to_string(),
group_name: None,
Expand All @@ -206,19 +190,19 @@ impl PhylumApi {

/// Get a list of projects
pub async fn get_projects(&mut self) -> Result<Vec<ProjectSummaryResponse>> {
self.get(project::get_project_summary(&self.api_uri)).await
self.get(endpoints::get_project_summary(&self.api_uri))
.await
}

/// Get user settings
pub async fn get_user_settings(&mut self) -> Result<UserSettings> {
self.get(user_settings::get_user_settings(&self.api_uri))
.await
self.get(endpoints::get_user_settings(&self.api_uri)).await
}

/// Put updated user settings
pub async fn put_user_settings(&mut self, settings: &UserSettings) -> Result<bool> {
self.client
.put(user_settings::put_user_settings(&self.api_uri))
.put(endpoints::put_user_settings(&self.api_uri))
.json(&settings)
.send()
.await?
Expand All @@ -245,7 +229,7 @@ impl PhylumApi {
};
log::debug!("==> Sending package submission: {:?}", req);
let resp: SubmitPackageResponse = self
.put(job::put_submit_package(&self.api_uri), req)
.put(endpoints::put_submit_package(&self.api_uri), req)
.await?;
Ok(resp.job_id)
}
Expand All @@ -255,7 +239,7 @@ impl PhylumApi {
&mut self,
job_id: &JobId,
) -> Result<JobStatusResponse<PackageStatus>> {
self.get(job::get_job_status(&self.api_uri, job_id, false))
self.get(endpoints::get_job_status(&self.api_uri, job_id, false))
.await
}

Expand All @@ -264,21 +248,22 @@ impl PhylumApi {
&mut self,
job_id: &JobId,
) -> Result<JobStatusResponse<PackageStatusExtended>> {
self.get(job::get_job_status(&self.api_uri, job_id, true))
self.get(endpoints::get_job_status(&self.api_uri, job_id, true))
.await
}

/// Get the status of all jobs
pub async fn get_status(&mut self) -> Result<AllJobsStatusResponse> {
self.get(job::get_all_jobs_status(&self.api_uri, 30)).await
self.get(endpoints::get_all_jobs_status(&self.api_uri, 30))
.await
}

/// Get the details of a specific project
pub async fn get_project_details(
&mut self,
project_name: &str,
) -> Result<ProjectDetailsResponse> {
self.get(job::get_project_details(&self.api_uri, project_name))
self.get(endpoints::get_project_details(&self.api_uri, project_name))
.await
}

Expand All @@ -287,12 +272,8 @@ impl PhylumApi {
&mut self,
pkg: &PackageDescriptor,
) -> Result<PackageStatusExtended> {
self.get(job::get_package_status(&self.api_uri, pkg)).await
}

/// Cancel a job currently in progress
pub async fn cancel(&mut self, job_id: &JobId) -> Result<CancelJobResponse> {
self.delete(job::delete_job(&self.api_uri, job_id)).await
self.get(endpoints::get_package_status(&self.api_uri, pkg))
.await
}
}

Expand Down Expand Up @@ -655,27 +636,6 @@ mod tests {
Ok(())
}

#[tokio::test]
async fn cancel() -> Result<()> {
let body = r#"{"msg": "Job deleted"}"#;

let mock_server = build_mock_server().await;
Mock::given(method("DELETE"))
.and(path_regex(
r"^/api/v0/job/[-\dabcdef]+$".to_string().to_string(),
))
.respond_with_fn(move |_| ResponseTemplate::new(200).set_body_string(body))
.mount(&mock_server)
.await;

let mut client = build_phylum_api(&mock_server).await?;

let job = JobId::from_str("59482a54-423b-448d-8325-f171c9dc336b").unwrap();
client.cancel(&job).await?;

Ok(())
}

#[tokio::test]
async fn user_info() -> Result<()> {
let body = r#"
Expand Down
9 changes: 0 additions & 9 deletions cli/src/api/project.rs

This file was deleted.

10 changes: 0 additions & 10 deletions cli/src/api/user_settings.rs

This file was deleted.

16 changes: 0 additions & 16 deletions cli/src/bin/phylum.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::path::PathBuf;
use std::process;
use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH};

use anyhow::{anyhow, Context};
Expand All @@ -21,7 +20,6 @@ use phylum_cli::config::*;
use phylum_cli::print::*;
use phylum_cli::update;
use phylum_cli::{print_user_failure, print_user_success, print_user_warning};
use phylum_types::types::common::JobId;
use phylum_types::types::job::Action;

/// Print a warning message to the user before exiting with exit code 0.
Expand Down Expand Up @@ -194,12 +192,6 @@ async fn handle_commands() -> CommandResult {
let should_submit = matches.subcommand_matches("analyze").is_some()
|| matches.subcommand_matches("batch").is_some();

// TODO this panicks with the type-checked `App` since the "cancel"
// subcommand is undefined. Is the backend feature implemented, or
// should we just keep this short circuited for now?
let should_cancel = false;
// let should_cancel = matches.subcommand_matches("cancel").is_some();

// TODO: switch from if/else to non-exhaustive pattern match
if let Some(matches) = matches.subcommand_matches("project") {
handle_project(&mut api, matches).await?;
Expand All @@ -209,14 +201,6 @@ async fn handle_commands() -> CommandResult {
return handle_submission(&mut api, config, &matches).await;
} else if let Some(matches) = matches.subcommand_matches("history") {
return handle_history(&mut api, matches).await;
} else if should_cancel {
if let Some(matches) = matches.subcommand_matches("cancel") {
let request_id = matches.value_of("request_id").unwrap().to_string();
let request_id = JobId::from_str(&request_id)
.context("Received invalid request id. Request id's should be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")?;
let resp = api.cancel(&request_id).await;
print_response(&resp, true, None);
}
}

Ok(ExitCode::Ok.into())
Expand Down

0 comments on commit 472c151

Please sign in to comment.