mirror of
https://github.com/railwayapp/cli
synced 2026-04-21 14:07:23 +00:00
Use workspace based queries/mutations (#664)
Team concept is being removed from the platform
This commit is contained in:
parent
00f7ed24a0
commit
a4984de90f
10 changed files with 161 additions and 196 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ query Project($id: String!) {
|
|||
id
|
||||
name
|
||||
deletedAt
|
||||
team {
|
||||
workspace {
|
||||
name
|
||||
}
|
||||
environments {
|
||||
|
|
@ -81,4 +81,4 @@ query Project($id: String!) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue