grafbase_local_backend/api/
link.rs

1use super::{
2    client::create_client,
3    consts::{API_URL, PROJECT_METADATA_FILE},
4    errors::ApiError,
5    graphql::queries::viewer::{PersonalAccount, Viewer},
6    types::{AccountWithProjects, Project, ProjectMetadata},
7    utils::project_linked,
8};
9use common::environment::Environment;
10use cynic::{http::ReqwestExt, QueryBuilder};
11use std::iter;
12
13/// # Errors
14///
15/// see [`ApiError`]
16#[allow(clippy::module_name_repetitions)]
17pub async fn get_viewer_data_for_link() -> Result<Vec<AccountWithProjects>, ApiError> {
18    // TODO consider if we want to do this elsewhere
19    if project_linked().await? {
20        return Err(ApiError::ProjectAlreadyLinked);
21    }
22
23    let client = create_client().await?;
24
25    let query = Viewer::build(());
26
27    let response = client.post(API_URL).run_graphql(query).await?;
28
29    let response = response.data.expect("must exist");
30
31    let viewer_response = response.viewer.ok_or(ApiError::UnauthorizedOrDeletedUser)?;
32
33    let PersonalAccount {
34        id,
35        name,
36        slug,
37        projects,
38    } = viewer_response
39        .personal_account
40        .ok_or(ApiError::IncorrectlyScopedToken)?;
41
42    let personal_account_id = id;
43
44    let personal_account = AccountWithProjects {
45        id: personal_account_id.inner().to_owned(),
46        name,
47        slug,
48        personal: true,
49        projects: projects
50            .nodes
51            .into_iter()
52            .map(|project| Project {
53                id: project.id.into_inner(),
54                slug: project.slug,
55            })
56            .collect(),
57    };
58
59    let accounts = iter::once(personal_account)
60        .chain(viewer_response.organizations.nodes.iter().map(|organization| {
61            AccountWithProjects {
62                id: organization.id.inner().to_owned(),
63                name: organization.name.clone(),
64                slug: organization.slug.clone(),
65                personal: false,
66                projects: organization
67                    .projects
68                    .nodes
69                    .iter()
70                    .cloned()
71                    .map(|project| Project {
72                        id: project.id.into_inner(),
73                        slug: project.slug,
74                    })
75                    .collect(),
76            }
77        }))
78        .collect();
79
80    Ok(accounts)
81}
82
83/// # Errors
84///
85/// see [`ApiError`]
86#[allow(clippy::module_name_repetitions)]
87pub async fn link_project(account_id: String, project_id: String) -> Result<(), ApiError> {
88    let environment = Environment::get();
89    let project_metadata_path = environment.project_dot_grafbase_path.join(PROJECT_METADATA_FILE);
90    tokio::fs::write(
91        &project_metadata_path,
92        ProjectMetadata { account_id, project_id }.to_string(),
93    )
94    .await
95    .map_err(ApiError::WriteProjectMetadataFile)
96}