Use workspace based queries/mutations (#664)

Team concept is being removed from the platform
This commit is contained in:
Paulo Cabral Sanz 2025-09-11 19:59:51 -03:00 committed by GitHub
parent 00f7ed24a0
commit a4984de90f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 161 additions and 196 deletions

View file

@ -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::<mutations::ProjectCreate, _>(&client, configs.get_backboard(), vars)

View file

@ -26,16 +26,36 @@ pub struct Args {
#[clap(long, short)]
service: Option<String>,
/// The team to link to.
/// The team to link to (deprecated: use --workspace instead).
#[clap(long, short)]
team: Option<String>,
/// The workspace to link to.
#[clap(long, short)]
workspace: Option<String>,
}
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<Workspace>) -> Result<Workspace> {
prompt_options("Select a workspace", workspaces)
}
fn prompt_team_projects(projects: Vec<Project>) -> Result<Project, anyhow::Error> {
fn prompt_workspace_projects(projects: Vec<Project>) -> Result<Project, anyhow::Error> {
prompt_options("Select a project", projects)
}
@ -277,7 +297,7 @@ impl From<Project> for NormalisedProject {
})
.collect(),
),
Project::Team(project) => NormalisedProject::new(
Project::Workspace(project) => NormalisedProject::new(
project.id,
project.name,
project

View file

@ -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,

View file

@ -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

View file

@ -3,7 +3,7 @@ query Project($id: String!) {
id
name
deletedAt
team {
workspace {
name
}
environments {
@ -81,4 +81,4 @@ query Project($id: String!) {
}
}
}
}
}

View file

@ -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
}

View file

@ -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
}
}
}

View file

@ -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,

View file

@ -23,15 +23,6 @@ impl Default for RetryConfig {
}
}
pub fn default_retry_logger() -> Box<dyn Fn(u32, u32, &anyhow::Error, u64) + Send + Sync> {
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<F, Fut, T>(config: RetryConfig, mut operation: F) -> Result<T>
where
F: FnMut() -> Fut,

View file

@ -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<String> {
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<Project> {
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<Utc> {
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<DateTime<Utc>> {
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),
}
}
}