Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: extra account info in credentials #49

Merged
merged 7 commits into from
May 29, 2024
Merged
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
109 changes: 74 additions & 35 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,98 @@
use graphql_client::Response;
use serde::{de::DeserializeOwned, Serialize};
use reqwest::RequestBuilder;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use url::Url;

use crate::credential::Credentials;
use crate::{constant, credential::AccessToken};

#[derive(Debug, thiserror::Error)]
pub enum ApiError {
pub enum Error {
#[error(transparent)]
ReqwestError(reqwest::Error),
#[error(transparent)]
CredentialsError(anyhow::Error),
ReqwestError(#[from] reqwest::Error),
#[error("Invalid token, authenticate with `slot auth login`")]
Unauthorized,
}

pub struct ApiClient {
base_url: String,
#[derive(Debug)]
pub struct Client {
base_url: Url,
client: reqwest::Client,
access_token: Option<AccessToken>,
}

impl ApiClient {
impl Client {
pub fn new() -> Self {
Self {
base_url: "https://api.cartridge.gg/query".to_string(),
access_token: None,
client: reqwest::Client::new(),
base_url: Url::parse(constant::CARTRIDGE_API_URL).expect("valid url"),
}
}

pub async fn post<R: DeserializeOwned, T: Serialize + ?Sized>(
&self,
body: &T,
) -> Result<Response<R>, ApiError> {
let credentials = Credentials::load()
.map_err(|_| {
anyhow::anyhow!("Failed to load credentials. Login with `slot auth login`.")
})
.map_err(ApiError::CredentialsError)?;

let res = self
.client
.post(&self.base_url)
.header(
"Authorization",
format!("Bearer {}", credentials.access_token),
)
pub fn new_with_token(token: AccessToken) -> Self {
let mut client = Self::new();
client.set_token(token);
client
}

pub fn set_token(&mut self, token: AccessToken) {
self.access_token = Some(token);
}

pub async fn query<R, T>(&self, body: &T) -> Result<Response<R>, Error>
where
R: DeserializeOwned,
T: Serialize + ?Sized,
{
let path = "/query";
let token = self.access_token.as_ref().map(|t| t.token.as_str());

// TODO: return this as an error if token is None
let bearer = format!("Bearer {}", token.unwrap_or_default());

let response = self
.post(path)
.header("Authorization", bearer)
.json(body)
.send()
.await
.map_err(ApiError::ReqwestError)?;
.await?;

if response.status() == 403 {
return Err(Error::Unauthorized);
}

Ok(response.json().await?)
}

if res.status() == 403 {
return Err(ApiError::CredentialsError(anyhow::anyhow!(
"Invalid token, authenticate with `slot auth login`"
)));
pub async fn oauth2(&self, code: &str) -> Result<AccessToken, Error> {
#[derive(Deserialize)]
struct OauthToken {
#[serde(rename(deserialize = "access_token"))]
token: String,
#[serde(rename(deserialize = "token_type"))]
r#type: String,
}

let res: Response<R> = res.json().await.map_err(ApiError::ReqwestError)?;
let path = "/oauth2/token";
let form = [("code", code)];

let response = self.post(path).form(&form).send().await?;
let token: OauthToken = response.json().await?;

Ok(AccessToken {
token: token.token,
r#type: token.r#type,
})
}

fn post(&self, path: &str) -> RequestBuilder {
let url = self.get_url(path);
self.client.post(url)
}

Ok(res)
fn get_url(&self, path: &str) -> Url {
let mut url = self.base_url.clone();
url.path_segments_mut().unwrap().extend(path.split('/'));
url
}
}
7 changes: 6 additions & 1 deletion src/command/auth/info.graphql
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
query Me {
me {
id
contractAddress
name
contractAddress
credentials {
webauthn {
publicKey
}
}
}
}
14 changes: 8 additions & 6 deletions src/command/auth/info.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
use anyhow::Result;
use clap::Args;
use graphql_client::{GraphQLQuery, Response};
use me::{ResponseData, Variables};

use crate::api::ApiClient;

use self::me::{ResponseData, Variables};
use crate::{api::Client, credential::Credentials};

#[derive(GraphQLQuery)]
#[graphql(
schema_path = "schema.json",
query_path = "src/command/auth/info.graphql",
response_derives = "Debug"
response_derives = "Debug, Clone, Serialize"
)]
pub struct Me;

#[derive(Debug, Args)]
pub struct InfoArgs {}

impl InfoArgs {
// TODO: find the account info from `credentials.json` first before making a request
pub async fn run(&self) -> Result<()> {
let credentials = Credentials::load()?;
let client = Client::new_with_token(credentials.access_token);

let request_body = Me::build_query(Variables {});
let res: Response<ResponseData> = client.query(&request_body).await?;

let client = ApiClient::new();
let res: Response<ResponseData> = client.post(&request_body).await?;
if let Some(errors) = res.errors {
for err in errors {
println!("Error: {}", err.message);
Expand Down
2 changes: 1 addition & 1 deletion src/command/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use clap::Subcommand;

use self::{info::InfoArgs, login::LoginArgs};

mod info;
pub mod info;
mod login;

#[derive(Subcommand, Debug)]
Expand Down
9 changes: 6 additions & 3 deletions src/command/deployments/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use katana_primitives::genesis::Genesis;
use katana_primitives::genesis::{allocation::GenesisAccountAlloc, json::GenesisJson};
use std::str::FromStr;

use crate::api::ApiClient;
use crate::api::Client;
use crate::credential::Credentials;

use super::services::KatanaAccountCommands;

Expand Down Expand Up @@ -42,8 +43,10 @@ impl AccountsArgs {
project: self.project.clone(),
});

let client = ApiClient::new();
let res: Response<ResponseData> = client.post(&request_body).await?;
let user = Credentials::load()?;
let client = Client::new_with_token(user.access_token);

let res: Response<ResponseData> = client.query(&request_body).await?;
if let Some(errors) = res.errors.clone() {
for err in errors {
println!("Error: {}", err.message);
Expand Down
9 changes: 6 additions & 3 deletions src/command/deployments/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use clap::Args;
use graphql_client::{GraphQLQuery, Response};

use crate::{
api::ApiClient,
api::Client,
command::deployments::create::create_deployment::{
CreateDeploymentCreateDeployment::{KatanaConfig, MadaraConfig, ToriiConfig},
CreateKatanaConfigInput, CreateMadaraConfigInput, CreateServiceConfigInput,
CreateServiceInput, CreateToriiConfigInput, DeploymentService, DeploymentTier, Variables,
},
credential::Credentials,
};

use super::{services::CreateServiceCommands, Long, Tier};
Expand Down Expand Up @@ -104,8 +105,10 @@ impl CreateArgs {
wait: Some(true),
});

let client = ApiClient::new();
let res: Response<create_deployment::ResponseData> = client.post(&request_body).await?;
let user = Credentials::load()?;
let client = Client::new_with_token(user.access_token);

let res: Response<create_deployment::ResponseData> = client.query(&request_body).await?;
if let Some(errors) = res.errors.clone() {
for err in errors {
println!("Error: {}", err.message);
Expand Down
9 changes: 6 additions & 3 deletions src/command/deployments/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use clap::Args;
use graphql_client::{GraphQLQuery, Response};

use crate::{
api::ApiClient,
api::Client,
command::deployments::delete::delete_deployment::{DeploymentService, Variables},
credential::Credentials,
};

#[derive(GraphQLQuery)]
Expand Down Expand Up @@ -47,8 +48,10 @@ impl DeleteArgs {
service,
});

let client = ApiClient::new();
let res: Response<delete_deployment::ResponseData> = client.post(&request_body).await?;
let user = Credentials::load()?;
let client = Client::new_with_token(user.access_token);

let res: Response<delete_deployment::ResponseData> = client.query(&request_body).await?;
if let Some(errors) = res.errors.clone() {
for err in errors {
println!("Error: {}", err.message);
Expand Down
8 changes: 5 additions & 3 deletions src/command/deployments/describe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::Result;
use clap::Args;
use graphql_client::{GraphQLQuery, Response};

use crate::api::ApiClient;
use crate::{api::Client, credential::Credentials};

use self::describe_deployment::{
DeploymentService,
Expand Down Expand Up @@ -47,8 +47,10 @@ impl DescribeArgs {
service,
});

let client = ApiClient::new();
let res: Response<ResponseData> = client.post(&request_body).await?;
let user = Credentials::load()?;
let client = Client::new_with_token(user.access_token);

let res: Response<ResponseData> = client.query(&request_body).await?;
if let Some(errors) = res.errors.clone() {
for err in errors {
println!("Error: {}", err.message);
Expand Down
9 changes: 6 additions & 3 deletions src/command/deployments/fork.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ use starknet::providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider};
use url::Url;

use crate::{
api::ApiClient,
api::Client,
command::deployments::fork::fork_deployment::{
DeploymentTier, ForkDeploymentForkDeployment::KatanaConfig, Variables,
},
credential::Credentials,
};

use super::{services::ForkServiceCommands, Long, Tier};
Expand Down Expand Up @@ -53,8 +54,10 @@ impl ForkArgs {
wait: Some(true),
});

let client = ApiClient::new();
let res: Response<fork_deployment::ResponseData> = client.post(&request_body).await?;
let user = Credentials::load()?;
let client = Client::new_with_token(user.access_token);

let res: Response<fork_deployment::ResponseData> = client.query(&request_body).await?;
if let Some(errors) = res.errors.clone() {
for err in errors {
println!("Error: {}", err.message);
Expand Down
8 changes: 5 additions & 3 deletions src/command/deployments/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::Result;
use clap::Args;
use graphql_client::{GraphQLQuery, Response};

use crate::api::ApiClient;
use crate::{api::Client, credential::Credentials};

use self::list_deployments::{ResponseData, Variables};

Expand All @@ -24,8 +24,10 @@ impl ListArgs {
pub async fn run(&self) -> Result<()> {
let request_body = ListDeployments::build_query(Variables {});

let client = ApiClient::new();
let res: Response<ResponseData> = client.post(&request_body).await?;
let user = Credentials::load()?;
let client = Client::new_with_token(user.access_token);

let res: Response<ResponseData> = client.query(&request_body).await?;
if let Some(errors) = res.errors.clone() {
for err in errors {
println!("Error: {}", err.message);
Expand Down
13 changes: 9 additions & 4 deletions src/command/deployments/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ use clap::Args;
use graphql_client::{GraphQLQuery, Response};
use tokio::time::sleep;

use crate::{api::ApiClient, command::deployments::logs::deployment_logs::DeploymentService};
use crate::{
api::Client, command::deployments::logs::deployment_logs::DeploymentService,
credential::Credentials,
};

use self::deployment_logs::{DeploymentLogsDeploymentLogs, ResponseData, Variables};

Expand Down Expand Up @@ -66,15 +69,17 @@ impl LogsArgs {
}

pub struct LogReader {
client: ApiClient,
client: Client,
service: Service,
project: String,
}

impl LogReader {
pub fn new(service: Service, project: String) -> Self {
let user = Credentials::load().unwrap();
let client = Client::new_with_token(user.access_token);
LogReader {
client: ApiClient::new(),
client,
service,
project,
}
Expand All @@ -98,7 +103,7 @@ impl LogReader {
limit: Some(limit),
});

let res: Response<ResponseData> = self.client.post(&request_body).await?;
let res: Response<ResponseData> = self.client.query(&request_body).await?;
if let Some(errors) = res.errors {
let error_message = errors
.into_iter()
Expand Down
Loading
Loading