Skip to content

Commit

Permalink
refactor(auth): move api_client to its own module and create it at th…
Browse files Browse the repository at this point in the history
…e app level
  • Loading branch information
adriencaccia committed Jun 6, 2024
1 parent ad4ba4d commit 2d87ed5
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 95 deletions.
89 changes: 89 additions & 0 deletions src/api_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use crate::app::Cli;
use crate::prelude::*;
use gql_client::{Client as GQLClient, ClientConfig};
use nestify::nest;
use serde::{Deserialize, Serialize};

pub struct CodSpeedAPIClient {
pub gql_client: GQLClient,
}

impl From<&Cli> for CodSpeedAPIClient {
fn from(args: &Cli) -> Self {
Self {
gql_client: build_gql_api_client(args.api_url.clone()),
}
}
}

const CODSPEED_GRAPHQL_ENDPOINT: &str = "https://gql.codspeed.io/";

fn build_gql_api_client(api_url: Option<String>) -> GQLClient {
let endpoint = api_url.unwrap_or_else(|| CODSPEED_GRAPHQL_ENDPOINT.to_string());

GQLClient::new_with_config(ClientConfig {
endpoint,
timeout: Some(10),
headers: Default::default(),
proxy: None,
})
}

nest! {
#[derive(Debug, Deserialize, Serialize)]*
#[serde(rename_all = "camelCase")]*
struct CreateLoginSessionData {
create_login_session: pub struct CreateLoginSessionPayload {
pub callback_url: String,
pub session_id: String,
}
}
}

nest! {
#[derive(Debug, Deserialize, Serialize)]*
#[serde(rename_all = "camelCase")]*
struct ConsumeLoginSessionData {
consume_login_session: pub struct ConsumeLoginSessionPayload {
pub token: Option<String>
}
}
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct ConsumeLoginSessionVars {
session_id: String,
}

impl CodSpeedAPIClient {
pub async fn create_login_session(&self) -> Result<CreateLoginSessionPayload> {
let response = self
.gql_client
.query_unwrap::<CreateLoginSessionData>(include_str!("queries/CreateLoginSession.gql"))
.await;
match response {
Ok(response) => Ok(response.create_login_session),
Err(err) => bail!("Failed to create login session: {}", err),
}
}

pub async fn consume_login_session(
&self,
session_id: &str,
) -> Result<ConsumeLoginSessionPayload> {
let response = self
.gql_client
.query_with_vars_unwrap::<ConsumeLoginSessionData, ConsumeLoginSessionVars>(
include_str!("queries/ConsumeLoginSession.gql"),
ConsumeLoginSessionVars {
session_id: session_id.to_string(),
},
)
.await;
match response {
Ok(response) => Ok(response.consume_login_session),
Err(err) => bail!("Failed to use login session: {}", err),
}
}
}
11 changes: 8 additions & 3 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use crate::{auth, prelude::*, run};
use crate::{api_client::CodSpeedAPIClient, auth, prelude::*, run};
use clap::{Parser, Subcommand};

#[derive(Parser, Debug)]
struct Cli {
pub struct Cli {
/// The URL of the CodSpeed GraphQL API
#[arg(long, env = "CODSPEED_API_URL", global = true, hide = true)]
pub api_url: Option<String>,

#[command(subcommand)]
command: Commands,
}
Expand All @@ -17,10 +21,11 @@ enum Commands {

pub async fn run() -> Result<()> {
let cli = Cli::parse();
let api_client = CodSpeedAPIClient::from(&cli);

match cli.command {
Commands::Run(args) => run::run(args).await?,
Commands::Auth(args) => auth::run(args).await?,
Commands::Auth(args) => auth::run(args, &api_client).await?,
}
Ok(())
}
96 changes: 4 additions & 92 deletions src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
use std::time::Duration;

use crate::{config::Config, logger::get_local_logger, prelude::*};
use crate::logger::get_local_logger;
use crate::{api_client::CodSpeedAPIClient, config::Config, prelude::*};
use clap::{Args, Subcommand};
use gql_client::{Client as GQLClient, ClientConfig};
use nestify::nest;
use serde::{Deserialize, Serialize};
use simplelog::CombinedLogger;
use tokio::time::{sleep, Instant};

#[derive(Debug, Args)]
pub struct AuthArgs {
/// The URL of the CodSpeed GraphQL API
#[arg(long, env = "CODSPEED_API_URL", global = true, hide = true)]
api_url: Option<String>,

#[command(subcommand)]
command: AuthCommands,
}
Expand All @@ -31,100 +25,18 @@ fn init_logger() -> Result<()> {
Ok(())
}

pub async fn run(args: AuthArgs) -> Result<()> {
pub async fn run(args: AuthArgs, api_client: &CodSpeedAPIClient) -> Result<()> {
init_logger()?;
let api_client = CodSpeedAPIClient::from(&args);

match args.command {
AuthCommands::Login => login(api_client).await?,
}
Ok(())
}

nest! {
#[derive(Debug, Deserialize, Serialize)]*
#[serde(rename_all = "camelCase")]*
struct CreateLoginSessionData {
create_login_session: struct CreateLoginSessionPayload {
callback_url: String,
session_id: String,
}
}
}

nest! {
#[derive(Debug, Deserialize, Serialize)]*
#[serde(rename_all = "camelCase")]*
struct ConsumeLoginSessionData {
consume_login_session: struct ConsumeLoginSessionPayload {
token: Option<String>
}
}
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct ConsumeLoginSessionVars {
session_id: String,
}

struct CodSpeedAPIClient {
gql_client: GQLClient,
}

impl From<&AuthArgs> for CodSpeedAPIClient {
fn from(args: &AuthArgs) -> Self {
Self {
gql_client: build_gql_api_client(args.api_url.clone()),
}
}
}

const CODSPEED_GRAPHQL_ENDPOINT: &str = "https://gql.codspeed.io/";

fn build_gql_api_client(api_url: Option<String>) -> GQLClient {
let endpoint = api_url.unwrap_or_else(|| CODSPEED_GRAPHQL_ENDPOINT.to_string());

GQLClient::new_with_config(ClientConfig {
endpoint,
timeout: Some(10),
headers: Default::default(),
proxy: None,
})
}

impl CodSpeedAPIClient {
async fn create_login_session(&self) -> Result<CreateLoginSessionPayload> {
let response = self
.gql_client
.query_unwrap::<CreateLoginSessionData>(include_str!("queries/CreateLoginSession.gql"))
.await;
match response {
Ok(response) => Ok(response.create_login_session),
Err(err) => bail!("Failed to create login session: {}", err),
}
}

async fn consume_login_session(&self, session_id: &str) -> Result<ConsumeLoginSessionPayload> {
let response = self
.gql_client
.query_with_vars_unwrap::<ConsumeLoginSessionData, ConsumeLoginSessionVars>(
include_str!("queries/ConsumeLoginSession.gql"),
ConsumeLoginSessionVars {
session_id: session_id.to_string(),
},
)
.await;
match response {
Ok(response) => Ok(response.consume_login_session),
Err(err) => bail!("Failed to use login session: {}", err),
}
}
}

const LOGIN_SESSION_MAX_DURATION: Duration = Duration::from_secs(60 * 5); // 5 minutes

async fn login(api_client: CodSpeedAPIClient) -> Result<()> {
async fn login(api_client: &CodSpeedAPIClient) -> Result<()> {
debug!("Login to CodSpeed");
debug!("Creating login session...");
let login_session_payload = api_client.create_login_session().await?;
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod api_client;
mod app;
mod auth;
mod config;
Expand Down

0 comments on commit 2d87ed5

Please sign in to comment.