diff --git a/src/commands/init.rs b/src/commands/init.rs index 9eb39a4..62d15c3 100644 --- a/src/commands/init.rs +++ b/src/commands/init.rs @@ -35,7 +35,7 @@ pub async fn command(args: Args) -> Result<()> { Some(project_name) }, description: None, - team_id: workspace.team_id(), + workspace_id: Some(workspace.id().to_owned()), }; let project_create = post_graphql::(&client, configs.get_backboard(), vars) diff --git a/src/commands/link.rs b/src/commands/link.rs index 7bd30eb..9c361f1 100644 --- a/src/commands/link.rs +++ b/src/commands/link.rs @@ -26,16 +26,36 @@ pub struct Args { #[clap(long, short)] service: Option, - /// The team to link to. + /// The team to link to (deprecated: use --workspace instead). #[clap(long, short)] team: Option, + + /// The workspace to link to. + #[clap(long, short)] + workspace: Option, } pub async fn command(args: Args) -> Result<()> { let mut configs = Configs::new()?; + // Support both team (deprecated) and workspace arguments + let workspace_arg = match (args.team.as_ref(), args.workspace.as_ref()) { + (Some(_), None) => { + eprintln!( + "{}", + "Warning: The --team flag is deprecated. Please use --workspace instead.".yellow() + ); + args.team + } + (None, workspace) => workspace.cloned(), + (Some(_), Some(_)) => { + eprintln!("{}", "Warning: Both --team and --workspace provided. Using --workspace. The --team flag is deprecated.".yellow()); + args.workspace + } + }; + let workspaces = workspaces().await?; - let workspace = select_workspace(args.project.clone(), args.team, workspaces)?; + let workspace = select_workspace(args.project.clone(), workspace_arg, workspaces)?; let project = select_project(workspace, args.project.clone())?; @@ -149,14 +169,14 @@ fn select_project( fake_select("Select a project", &project.to_string()); project } else { - return Err(RailwayError::ProjectNotFoundInTeam( + return Err(RailwayError::ProjectNotFoundInWorkspace( project, workspace.name().to_owned(), ) .into()); } } else { - prompt_team_projects(projects)? + prompt_workspace_projects(projects)? } }); Ok(project) @@ -182,18 +202,18 @@ fn select_workspace( prompt_workspaces(workspaces)? } } - (None, Some(team_arg)) | (Some(_), Some(team_arg)) => { + (None, Some(workspace_arg)) | (Some(_), Some(workspace_arg)) => { if let Some(workspace) = workspaces.iter().find(|w| { - w.team_id() - .is_some_and(|id| id.to_lowercase() == team_arg.to_lowercase()) - || w.name().to_lowercase() == team_arg.to_lowercase() + w.id().to_lowercase() == workspace_arg.to_lowercase() + || w.team_id().map(str::to_lowercase) == Some(workspace_arg.to_lowercase()) + || w.name().to_lowercase() == workspace_arg.to_lowercase() }) { fake_select("Select a workspace", workspace.name()); workspace.clone() - } else if team_arg.to_lowercase() == "personal" { + } else if workspace_arg.to_lowercase() == "personal" { bail!(RailwayError::NoPersonalWorkspace); } else { - return Err(RailwayError::TeamNotFound(team_arg.clone()).into()); + return Err(RailwayError::WorkspaceNotFound(workspace_arg.clone()).into()); } } (None, None) => prompt_workspaces(workspaces)?, @@ -212,7 +232,7 @@ fn prompt_workspaces(workspaces: Vec) -> Result { prompt_options("Select a workspace", workspaces) } -fn prompt_team_projects(projects: Vec) -> Result { +fn prompt_workspace_projects(projects: Vec) -> Result { prompt_options("Select a project", projects) } @@ -277,7 +297,7 @@ impl From for NormalisedProject { }) .collect(), ), - Project::Team(project) => NormalisedProject::new( + Project::Workspace(project) => NormalisedProject::new( project.id, project.name, project diff --git a/src/errors.rs b/src/errors.rs index 0b4654b..5b1bcf2 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -49,8 +49,8 @@ pub enum RailwayError { )] EnvironmentNotFound(String), - #[error("Project \"{0}\" was not found in the \"{1}\" team.")] - ProjectNotFoundInTeam(String, String), + #[error("Project \"{0}\" was not found in the \"{1}\" workspace.")] + ProjectNotFoundInWorkspace(String, String), #[error("Workspace \"{0}\" not found.")] WorkspaceNotFound(String), @@ -58,9 +58,6 @@ pub enum RailwayError { #[error("Service \"{0}\" not found.")] ServiceNotFound(String), - #[error("Team \"{0}\" not found.")] - TeamNotFound(String), - #[error("Project has no services.")] ProjectHasNoServices, diff --git a/src/gql/mutations/strings/ProjectCreate.graphql b/src/gql/mutations/strings/ProjectCreate.graphql index 59e59f5..18884e6 100644 --- a/src/gql/mutations/strings/ProjectCreate.graphql +++ b/src/gql/mutations/strings/ProjectCreate.graphql @@ -1,6 +1,6 @@ -mutation ProjectCreate($name: String, $description: String, $teamId: String) { +mutation ProjectCreate($name: String, $description: String, $workspaceId: String) { projectCreate( - input: { name: $name, description: $description, teamId: $teamId } + input: { name: $name, description: $description, workspaceId: $workspaceId } ) { name id diff --git a/src/gql/queries/strings/Project.graphql b/src/gql/queries/strings/Project.graphql index ea69b48..87bfb28 100644 --- a/src/gql/queries/strings/Project.graphql +++ b/src/gql/queries/strings/Project.graphql @@ -3,7 +3,7 @@ query Project($id: String!) { id name deletedAt - team { + workspace { name } environments { @@ -81,4 +81,4 @@ query Project($id: String!) { } } } -} \ No newline at end of file +} diff --git a/src/gql/queries/strings/Projects.graphql b/src/gql/queries/strings/Projects.graphql index a7272e7..5967137 100644 --- a/src/gql/queries/strings/Projects.graphql +++ b/src/gql/queries/strings/Projects.graphql @@ -1,11 +1,11 @@ -query Projects($teamId: String) { - projects(teamId: $teamId) { +query Projects($workspaceId: String) { + projects(workspaceId: $workspaceId) { edges { node { id name updatedAt - team { + workspace { id name } diff --git a/src/gql/queries/strings/UserProjects.graphql b/src/gql/queries/strings/UserProjects.graphql index c1b3522..70abb9d 100644 --- a/src/gql/queries/strings/UserProjects.graphql +++ b/src/gql/queries/strings/UserProjects.graphql @@ -9,10 +9,6 @@ query UserProjects { createdAt updatedAt deletedAt - team { - id - name - } environments { edges { node { @@ -44,36 +40,32 @@ query UserProjects { name team { id - projects { - edges { - node { - id - name - createdAt - updatedAt - deletedAt - team { - id - name - } - environments { - edges { - node { - id - name - } + } + projects { + edges { + node { + id + name + createdAt + updatedAt + deletedAt + environments { + edges { + node { + id + name } } - services { - edges { - node { - id - name - serviceInstances { - edges { - node { - environmentId - } + } + services { + edges { + node { + id + name + serviceInstances { + edges { + node { + environmentId } } } diff --git a/src/gql/schema.json b/src/gql/schema.json index d436c5c..8a1564f 100644 --- a/src/gql/schema.json +++ b/src/gql/schema.json @@ -142,12 +142,6 @@ { "description": null, "enumValues": [ - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "CEPH_VOLUMES" - }, { "deprecationReason": null, "description": null, @@ -177,6 +171,12 @@ "description": null, "isDeprecated": false, "name": "REPLICA_METRICS_V2" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "UNIFIED_TEMPLATE_EDITOR" } ], "fields": null, @@ -217,13 +217,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "IMAGE_UPDATES" + "name": "MONOREPO_SUPPORT" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "MONOREPO_SUPPORT" + "name": "STRIPE_CONNECT_ONBOARDING" }, { "deprecationReason": null, @@ -2529,6 +2529,30 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "supportedWithdrawalPlatforms", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "WithdrawalPlatformTypes", + "ofType": null + } + } + } + } + }, { "args": [], "deprecationReason": null, @@ -14205,37 +14229,6 @@ } } }, - { - "args": [ - { - "defaultValue": null, - "description": null, - "name": "input", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "TeamPermissionChangeInput", - "ofType": null - } - } - } - ], - "deprecationReason": null, - "description": "Changes a user team permissions.", - "isDeprecated": false, - "name": "teamPermissionChange", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - } - }, { "args": [ { @@ -17491,13 +17484,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "IMAGE_UPDATES" + "name": "MONOREPO_SUPPORT" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "MONOREPO_SUPPORT" + "name": "STRIPE_CONNECT_ONBOARDING" }, { "deprecationReason": null, @@ -19856,9 +19849,9 @@ }, { "args": [], - "deprecationReason": null, + "deprecationReason": "Use workspace", "description": null, - "isDeprecated": false, + "isDeprecated": true, "name": "team", "type": { "kind": "OBJECT", @@ -20008,6 +20001,18 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "workspace", + "type": { + "kind": "OBJECT", + "name": "Workspace", + "ofType": null + } + }, { "args": [], "deprecationReason": null, @@ -32642,9 +32647,9 @@ }, { "args": [], - "deprecationReason": null, + "deprecationReason": "Use workspaceId", "description": null, - "isDeprecated": false, + "isDeprecated": true, "name": "teamId", "type": { "kind": "SCALAR", @@ -32663,6 +32668,18 @@ "name": "String", "ofType": null } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "workspaceId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } } ], "inputFields": null, @@ -34202,59 +34219,6 @@ "name": "TeamPermission", "possibleTypes": null }, - { - "description": null, - "enumValues": null, - "fields": null, - "inputFields": [ - { - "defaultValue": null, - "description": null, - "name": "role", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "TeamRole", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "teamId", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "userId", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - } - ], - "interfaces": null, - "kind": "INPUT_OBJECT", - "name": "TeamPermissionChangeInput", - "possibleTypes": null - }, { "description": null, "enumValues": null, @@ -34829,9 +34793,9 @@ }, { "args": [], - "deprecationReason": null, + "deprecationReason": "Use workspaceId", "description": null, - "isDeprecated": false, + "isDeprecated": true, "name": "teamId", "type": { "kind": "SCALAR", @@ -34854,6 +34818,18 @@ "ofType": null } } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "workspaceId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } } ], "inputFields": null, @@ -35405,16 +35381,6 @@ "ofType": null } } - }, - { - "defaultValue": null, - "description": null, - "name": "teamId", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } } ], "interfaces": null, diff --git a/src/util/retry.rs b/src/util/retry.rs index a96b918..f864198 100644 --- a/src/util/retry.rs +++ b/src/util/retry.rs @@ -23,15 +23,6 @@ impl Default for RetryConfig { } } -pub fn default_retry_logger() -> Box { - Box::new(|attempt, max_attempts, error, delay_ms| { - eprintln!( - "Attempt {}/{} failed: {}. Retrying in {}ms...", - attempt, max_attempts, error, delay_ms - ); - }) -} - pub async fn retry_with_backoff(config: RetryConfig, mut operation: F) -> Result where F: FnMut() -> Fut, diff --git a/src/workspace.rs b/src/workspace.rs index 0c32da4..d0f3e19 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -5,7 +5,7 @@ use std::fmt::Display; use super::{ queries::user_projects::{ UserProjectsExternalWorkspaces, UserProjectsExternalWorkspacesProjects, - UserProjectsMeWorkspaces, UserProjectsMeWorkspacesTeamProjectsEdgesNode, + UserProjectsMeWorkspaces, UserProjectsMeWorkspacesProjectsEdgesNode, }, *, }; @@ -54,24 +54,23 @@ impl Workspace { } } - pub fn team_id(&self) -> Option { + pub fn team_id(&self) -> Option<&str> { match self { - Self::External(w) => w.team_id.clone(), - Self::Member(w) => w.team.as_ref().map(|t| t.id.clone()), + Self::External(w) => w.team_id.as_deref(), + Self::Member(w) => w.team.as_ref().map(|t| t.id.as_str()), } } pub fn projects(&self) -> Vec { - let mut projects = match self { + let mut projects: Vec<_> = match self { Self::External(w) => w.projects.iter().cloned().map(Project::External).collect(), - Self::Member(w) => w.team.as_ref().map_or_else(Vec::new, |t| { - t.projects - .edges - .iter() - .cloned() - .map(|e| Project::Team(e.node)) - .collect() - }), + Self::Member(w) => w + .projects + .edges + .iter() + .cloned() + .map(|e| Project::Workspace(e.node)) + .collect(), }; projects.sort_by_key(|b| std::cmp::Reverse(b.updated_at())); projects @@ -92,32 +91,32 @@ impl Display for Workspace { #[serde(untagged)] pub enum Project { External(UserProjectsExternalWorkspacesProjects), - Team(UserProjectsMeWorkspacesTeamProjectsEdgesNode), + Workspace(UserProjectsMeWorkspacesProjectsEdgesNode), } impl Project { pub fn id(&self) -> &str { match self { Self::External(w) => &w.id, - Self::Team(w) => &w.id, + Self::Workspace(w) => &w.id, } } pub fn name(&self) -> &str { match self { Self::External(w) => &w.name, - Self::Team(w) => &w.name, + Self::Workspace(w) => &w.name, } } pub fn updated_at(&self) -> DateTime { match self { Self::External(w) => w.updated_at, - Self::Team(w) => w.updated_at, + Self::Workspace(w) => w.updated_at, } } pub fn deleted_at(&self) -> Option> { match self { Self::External(w) => w.deleted_at, - Self::Team(w) => w.deleted_at, + Self::Workspace(w) => w.deleted_at, } } } @@ -125,8 +124,8 @@ impl Project { impl Display for Project { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Team(team_project) => write!(f, "{}", team_project.name), - Self::External(team_project) => write!(f, "{}", team_project.name), + Self::Workspace(project) => write!(f, "{}", project.name), + Self::External(project) => write!(f, "{}", project.name), } } }