grafbase_local_backend/api/
client.rs

1use super::consts::GRAFBASE_ACCESS_TOKEN_ENV_VAR;
2use super::types::Credentials;
3use super::{consts::CREDENTIALS_FILE, errors::ApiError};
4use crate::consts::USER_AGENT;
5use axum::http::{HeaderMap, HeaderValue};
6use common::environment::get_user_dot_grafbase_path;
7use reqwest::{header, Client};
8use std::env;
9use tokio::fs::read_to_string;
10
11/// # Errors
12///
13/// See [`ApiError`]
14#[allow(clippy::module_name_repetitions)]
15pub async fn create_client() -> Result<reqwest::Client, ApiError> {
16    let token = get_access_token().await?;
17    let mut headers = HeaderMap::new();
18    let mut bearer_token =
19        HeaderValue::from_str(&format!("Bearer {token}")).map_err(|_| ApiError::CorruptAccessToken)?;
20    bearer_token.set_sensitive(true);
21    headers.insert(header::AUTHORIZATION, bearer_token);
22    let mut user_agent = HeaderValue::from_str(USER_AGENT).expect("must be visible ascii");
23    user_agent.set_sensitive(true);
24    headers.insert(header::USER_AGENT, user_agent);
25
26    Ok(Client::builder()
27        .default_headers(headers)
28        .build()
29        .expect("TLS is supported in all targets"))
30}
31
32async fn get_access_token() -> Result<String, ApiError> {
33    match get_access_token_from_file().await {
34        Ok(token) => Ok(token),
35        // attempt to also check GRAFBASE_ACCESS_TOKEN_ENV_VAR, returning the original error if it doesn't exist
36        Err(error) => env::var(GRAFBASE_ACCESS_TOKEN_ENV_VAR).map_err(|_| error),
37    }
38}
39
40async fn get_access_token_from_file() -> Result<String, ApiError> {
41    // needed to bypass the project fallback behavior of Environment's dot grafbase folder
42    // TODO consider removing the fallback
43    let user_dot_grafbase_path = get_user_dot_grafbase_path().ok_or(ApiError::FindUserDotGrafbaseFolder)?;
44
45    match user_dot_grafbase_path.try_exists() {
46        Ok(true) => {}
47        Ok(false) => return Err(ApiError::NotLoggedIn),
48        Err(error) => return Err(ApiError::ReadUserDotGrafbaseFolder(error)),
49    }
50
51    let credentials_file_path = user_dot_grafbase_path.join(CREDENTIALS_FILE);
52
53    match credentials_file_path.try_exists() {
54        Ok(true) => {}
55        Ok(false) => return Err(ApiError::NotLoggedIn),
56        Err(error) => return Err(ApiError::ReadCredentialsFile(error)),
57    }
58
59    let credential_file = read_to_string(user_dot_grafbase_path.join(CREDENTIALS_FILE))
60        .await
61        .map_err(ApiError::ReadCredentialsFile)?;
62
63    let credentials: Credentials<'_> =
64        serde_json::from_str(&credential_file).map_err(|_| ApiError::CorruptCredentialsFile)?;
65
66    Ok(credentials.access_token.to_owned())
67}