diff --git a/src/commands/environment/list.rs b/src/commands/environment/list.rs new file mode 100644 index 0000000..b654ec7 --- /dev/null +++ b/src/commands/environment/list.rs @@ -0,0 +1,276 @@ +use super::{List as Args, *}; +use crate::{ + Configs, GQLClient, + client::post_graphql, + commands::queries::{self, environments}, +}; +use anyhow::Result; +use chrono_humanize::HumanTime; +use serde::Serialize; + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct EnvironmentListOutput { + environments: Vec, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct EnvironmentOutput { + id: String, + name: String, + is_ephemeral: bool, + is_linked: bool, + restricted: bool, + created_at: String, + updated_at: String, + #[serde(skip_serializing_if = "Option::is_none")] + unmerged_changes_count: Option, + #[serde(skip_serializing_if = "Option::is_none")] + source_environment: Option, + #[serde(skip_serializing_if = "Option::is_none")] + meta: Option, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct SourceEnvironmentOutput { + id: String, + name: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct EnvironmentMetaOutput { + #[serde(skip_serializing_if = "Option::is_none")] + pr_number: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pr_title: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pr_repo: Option, + #[serde(skip_serializing_if = "Option::is_none")] + branch: Option, + #[serde(skip_serializing_if = "Option::is_none")] + base_branch: Option, +} + +const PAGE_SIZE: i64 = 500; + +pub async fn command(args: Args) -> Result<()> { + let configs = Configs::new()?; + let client = GQLClient::new_authorized(&configs)?; + + let linked_project = configs.get_linked_project().await?; + let linked_env_id = linked_project.environment.as_deref(); + + let is_ephemeral = if args.ephemeral { + Some(true) + } else if args.no_ephemeral { + Some(false) + } else { + None + }; + + let mut all_edges = Vec::new(); + let mut after: Option = None; + + loop { + let vars = environments::Variables { + project_id: linked_project.project.clone(), + is_ephemeral, + first: Some(PAGE_SIZE), + after: after.take(), + }; + + let response = + post_graphql::(&client, configs.get_backboard(), vars) + .await?; + + let has_next_page = response.environments.page_info.has_next_page; + after = response.environments.page_info.end_cursor; + + all_edges.extend(response.environments.edges); + + if !has_next_page { + break; + } + } + + let mut persistent = Vec::new(); + let mut ephemeral = Vec::new(); + + for edge in &all_edges { + if edge.node.is_ephemeral { + ephemeral.push(edge); + } else { + persistent.push(edge); + } + } + + persistent.sort_by(|a, b| { + a.node + .created_at + .cmp(&b.node.created_at) + .then_with(|| a.node.name.cmp(&b.node.name)) + }); + ephemeral.sort_by(|a, b| b.node.created_at.cmp(&a.node.created_at)); + + if args.json { + let is_linked = |id: &str| Some(id) == linked_env_id; + + let output = EnvironmentListOutput { + environments: persistent + .iter() + .chain(ephemeral.iter()) + .map(|edge| { + let node = &edge.node; + let meta = node.meta.as_ref().and_then(|m| { + if m.pr_number.is_some() || m.branch.is_some() { + Some(EnvironmentMetaOutput { + pr_number: m.pr_number, + pr_title: m.pr_title.clone(), + pr_repo: m.pr_repo.clone(), + branch: m.branch.clone(), + base_branch: m.base_branch.clone(), + }) + } else { + None + } + }); + + EnvironmentOutput { + id: node.id.clone(), + name: node.name.clone(), + is_ephemeral: node.is_ephemeral, + is_linked: is_linked(&node.id), + restricted: !node.can_access, + created_at: node.created_at.to_rfc3339(), + updated_at: node.updated_at.to_rfc3339(), + unmerged_changes_count: node.unmerged_changes_count, + source_environment: node.source_environment.as_ref().map(|s| { + SourceEnvironmentOutput { + id: s.id.clone(), + name: s.name.clone(), + } + }), + meta, + } + }) + .collect(), + }; + println!("{}", serde_json::to_string_pretty(&output)?); + } else if persistent.is_empty() && ephemeral.is_empty() { + let label = match (args.ephemeral, args.no_ephemeral) { + (true, _) => "ephemeral environments", + (_, true) => "persistent environments", + _ => "environments", + }; + println!("No {label} found"); + } else { + println!(); + if !persistent.is_empty() { + println!("{}", "Environments".bold()); + println!(); + for edge in &persistent { + print_environment(&edge.node, linked_env_id); + } + } + + if !ephemeral.is_empty() { + if !persistent.is_empty() { + println!(); + println!("---"); + println!(); + } + println!("{}", "PR Environments".bold()); + println!(); + for edge in &ephemeral { + print_environment(&edge.node, linked_env_id); + } + } + println!(); + } + + Ok(()) +} + +fn print_environment( + node: &environments::EnvironmentsEnvironmentsEdgesNode, + linked_env_id: Option<&str>, +) { + let is_linked = Some(node.id.as_str()) == linked_env_id; + + if !node.can_access { + if is_linked { + println!( + "{} {} {}", + node.name.dimmed(), + "(linked)".green(), + "(restricted)".dimmed() + ); + } else { + println!("{} {}", node.name.dimmed(), "(restricted)".dimmed()); + } + return; + } + + if is_linked { + println!("{} {}", node.name, "(linked)".green()); + } else { + println!("{}", node.name); + } + + let mut details: Vec = Vec::new(); + + if let Some(source) = &node.source_environment { + details.push(format!("forked from {}", source.name).dimmed().to_string()); + } + + if let Some(meta) = &node.meta { + if let Some(pr_number) = meta.pr_number { + let title_part = match meta.pr_title.as_deref().filter(|t| !t.is_empty()) { + Some(title) => format!(": {title}"), + None => String::new(), + }; + let branch_info = match (&meta.branch, &meta.base_branch) { + (Some(b), Some(base)) => format!(" ({b} <- {base})"), + (Some(b), None) => format!(" ({b})"), + _ => String::new(), + }; + details.push( + format!("PR #{pr_number}{title_part}{branch_info}") + .dimmed() + .to_string(), + ); + } else if let Some(branch) = &meta.branch { + let base_part = match &meta.base_branch { + Some(base) => format!(" <- {base}"), + None => String::new(), + }; + details.push(format!("branch: {branch}{base_part}").dimmed().to_string()); + } + } + + if let Some(count) = node.unmerged_changes_count.filter(|&c| c > 0) { + details.push( + format!( + "{} unmerged {}", + count, + if count == 1 { "change" } else { "changes" } + ) + .yellow() + .to_string(), + ); + } + + if node.updated_at != node.created_at { + let human_time = HumanTime::from(node.updated_at); + details.push(format!("updated {human_time}").dimmed().to_string()); + } + + let last = details.len().saturating_sub(1); + for (i, detail) in details.iter().enumerate() { + let connector = if i < last { "├" } else { "└" }; + println!(" {} {}", connector.dimmed(), detail); + } +} diff --git a/src/commands/environment/mod.rs b/src/commands/environment/mod.rs index e660e08..72a25a5 100644 --- a/src/commands/environment/mod.rs +++ b/src/commands/environment/mod.rs @@ -18,6 +18,7 @@ mod config; mod delete; mod edit; mod link; +mod list; mod new; /// Create, delete or link an environment @@ -111,6 +112,21 @@ structstruck::strike! { #[clap(long, short)] pub environment: Option, + /// Output in JSON format + #[clap(long)] + pub json: bool, + }), + + /// List all environments in the project + List(pub struct { + /// Show only ephemeral (PR) environments + #[clap(long, conflicts_with = "no_ephemeral")] + pub ephemeral: bool, + + /// Hide ephemeral (PR) environments + #[clap(long, conflicts_with = "ephemeral")] + pub no_ephemeral: bool, + /// Output in JSON format #[clap(long)] pub json: bool, @@ -175,6 +191,7 @@ pub async fn command(args: Args) -> Result<()> { Some(Commands::Delete(args)) => delete::delete_environment(args).await, Some(Commands::Edit(args)) => edit::edit_environment(args).await, Some(Commands::Config(args)) => config::command(args).await, + Some(Commands::List(args)) => list::command(args).await, // Legacy: `railway environment ` without subcommand None => link::link_environment(args.environment, args.json).await, } diff --git a/src/gql/queries/mod.rs b/src/gql/queries/mod.rs index b9e0638..c44a7b4 100644 --- a/src/gql/queries/mod.rs +++ b/src/gql/queries/mod.rs @@ -182,6 +182,14 @@ pub struct LatestFunctionVersion; )] pub struct GetEnvironmentConfig; +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "src/gql/schema.json", + query_path = "src/gql/queries/strings/Environments.graphql", + response_derives = "Debug, Serialize, Clone" +)] +pub struct Environments; + #[derive(GraphQLQuery)] #[graphql( schema_path = "src/gql/schema.json", diff --git a/src/gql/queries/strings/Environments.graphql b/src/gql/queries/strings/Environments.graphql new file mode 100644 index 0000000..d39e0f9 --- /dev/null +++ b/src/gql/queries/strings/Environments.graphql @@ -0,0 +1,30 @@ +query Environments($projectId: String!, $isEphemeral: Boolean, $first: Int, $after: String) { + environments(projectId: $projectId, isEphemeral: $isEphemeral, first: $first, after: $after) { + edges { + node { + id + name + isEphemeral + createdAt + updatedAt + canAccess + unmergedChangesCount + sourceEnvironment { + id + name + } + meta { + prNumber + prTitle + prRepo + branch + baseBranch + } + } + } + pageInfo { + hasNextPage + endCursor + } + } +} diff --git a/src/gql/schema.json b/src/gql/schema.json index 7c42897..39b7f28 100644 --- a/src/gql/schema.json +++ b/src/gql/schema.json @@ -154,24 +154,18 @@ "isDeprecated": false, "name": "BUCKET_FILE_BROWSER" }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "CDN_CACHING" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "CONVERSATIONAL_UI" - }, { "deprecationReason": null, "description": null, "isDeprecated": false, "name": "DEBUG_SMART_DIAGNOSIS" }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "IN_DASHBOARD_SUPPORT" + }, { "deprecationReason": null, "description": null, @@ -188,7 +182,7 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "POSTGRES_HA" + "name": "POSTGRES_PGBOUNCER" }, { "deprecationReason": null, @@ -196,12 +190,6 @@ "isDeprecated": false, "name": "PRIORITY_BOARDING" }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "RAW_SQL_QUERIES" - }, { "deprecationReason": null, "description": null, @@ -297,6 +285,18 @@ "isDeprecated": false, "name": "FOCUSED_PR_ENVIRONMENTS" }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "INLINE_NOTIFICATION_PROCESSING" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "KAFKA_DEPLOYMENT_STATUS_CHANGES" + }, { "deprecationReason": null, "description": null, @@ -309,6 +309,12 @@ "isDeprecated": false, "name": "OAUTH_DCR_KILLSWITCH" }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "OAUTH_DEVICE_FLOW_KILLSWITCH" + }, { "deprecationReason": null, "description": null, @@ -403,6 +409,12 @@ "isDeprecated": false, "name": "PLACEHOLDER" }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "SKIPPED_BUILDS" + }, { "deprecationReason": null, "description": null, @@ -9159,6 +9171,22 @@ "deprecationReason": null, "description": null, "isDeprecated": false, + "name": "hasAutomaticDiagnosis", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": "Deprecated in favour of the SpendCommitment schema.", + "description": null, + "isDeprecated": true, "name": "hasBAA", "type": { "kind": "NON_NULL", @@ -9175,6 +9203,22 @@ "deprecationReason": null, "description": null, "isDeprecated": false, + "name": "hasGuardrailsAccess", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": "Deprecated in favour of the SpendCommitment schema.", + "description": null, + "isDeprecated": true, "name": "hasRBAC", "type": { "kind": "NON_NULL", @@ -9314,6 +9358,18 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "subscriptionPlanLimit", + "type": { + "kind": "SCALAR", + "name": "SubscriptionPlanLimit", + "ofType": null + } + }, { "args": [], "deprecationReason": null, @@ -9593,6 +9649,208 @@ "name": "GitHubBranch", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "name", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "status", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "GitHubCheck", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "additions", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "author", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "body", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "changedFiles", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "checks", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "GitHubCheck", + "ofType": null + } + } + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "deletions", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "mergeable", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "state", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "title", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "GitHubPRInfo", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -10108,6 +10366,132 @@ "name": "HerokuImportVariablesInput", "possibleTypes": null }, + { + "description": "The result of an HTTP duration metrics query.", + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": "The samples of HTTP duration metrics.", + "isDeprecated": false, + "name": "samples", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "HttpDurationMetricsSample", + "ofType": null + } + } + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "HttpDurationMetricsResult", + "possibleTypes": null + }, + { + "description": "A single sample of HTTP duration metrics.", + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": "50th percentile (median) request duration in milliseconds.", + "isDeprecated": false, + "name": "p50", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Float", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": "90th percentile request duration in milliseconds.", + "isDeprecated": false, + "name": "p90", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Float", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": "95th percentile request duration in milliseconds.", + "isDeprecated": false, + "name": "p95", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Float", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": "99th percentile request duration in milliseconds.", + "isDeprecated": false, + "name": "p99", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Float", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": "The timestamp of the sample. Represented as number of seconds since the Unix epoch.", + "isDeprecated": false, + "name": "ts", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "HttpDurationMetricsSample", + "possibleTypes": null + }, { "description": "The result of a http logs query.", "enumValues": null, @@ -10439,6 +10823,135 @@ "name": "HttpLog", "possibleTypes": null }, + { + "description": "HTTP metrics grouped by status code.", + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": "The samples of HTTP metrics for this status code.", + "isDeprecated": false, + "name": "samples", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "HttpMetricsSample", + "ofType": null + } + } + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": "The HTTP status code.", + "isDeprecated": false, + "name": "statusCode", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "HttpMetricsByStatusResult", + "possibleTypes": null + }, + { + "description": "The result of an HTTP metrics query.", + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": "The samples of HTTP metrics.", + "isDeprecated": false, + "name": "samples", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "HttpMetricsSample", + "ofType": null + } + } + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "HttpMetricsResult", + "possibleTypes": null + }, + { + "description": "A single sample of an HTTP metric.", + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": "The timestamp of the sample. Represented as number of seconds since the Unix epoch.", + "isDeprecated": false, + "name": "ts", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": "The value of the sample (count of requests).", + "isDeprecated": false, + "name": "value", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Float", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "HttpMetricsSample", + "possibleTypes": null + }, { "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", "enumValues": null, @@ -18080,6 +18593,67 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "enabled", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "INPUT_OBJECT", + "name": "WorkspacePolicyItemUpdateInput", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "policy", + "type": { + "kind": "ENUM", + "name": "WorkspacePolicyName", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "workspaceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Enable or disable a workspace policy. Enterprise workspaces only.", + "isDeprecated": false, + "name": "workspacePolicyItemUpdate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, { "args": [ { @@ -21097,6 +21671,18 @@ "isDeprecated": false, "name": "FOCUSED_PR_ENVIRONMENTS" }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "INLINE_NOTIFICATION_PROCESSING" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "KAFKA_DEPLOYMENT_STATUS_CHANGES" + }, { "deprecationReason": null, "description": null, @@ -21109,6 +21695,12 @@ "isDeprecated": false, "name": "OAUTH_DCR_KILLSWITCH" }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "OAUTH_DEVICE_FLOW_KILLSWITCH" + }, { "deprecationReason": null, "description": null, @@ -28499,6 +29091,47 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "prNumber", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Get info for a GitHub pull request", + "isDeprecated": false, + "name": "githubPRInfo", + "type": { + "kind": "OBJECT", + "name": "GitHubPRInfo", + "ofType": null + } + }, { "args": [ { @@ -28655,6 +29288,119 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": "The end of the period to get metrics for.", + "name": "endDate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": "Filter by HTTP method (e.g., GET, POST)", + "name": "method", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "Filter by request path", + "name": "path", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": "The start of the period to get metrics for.", + "name": "startDate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": "Filter by HTTP status code (e.g., 200, 404, 500)", + "name": "statusCode", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "The frequency of data points in the response. If the `stepSeconds` is 60, then the response will contain one data point per minute.", + "name": "stepSeconds", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + ], + "deprecationReason": null, + "description": "Get HTTP request duration metrics for a service (avg, p50, p90, p95, p99)", + "isDeprecated": false, + "name": "httpDurationMetrics", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "HttpDurationMetricsResult", + "ofType": null + } + } + }, { "args": [ { @@ -28784,6 +29530,230 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": "The end of the period to get metrics for.", + "name": "endDate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": "Filter by HTTP method (e.g., GET, POST)", + "name": "method", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "Filter by request path", + "name": "path", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": "The start of the period to get metrics for.", + "name": "startDate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": "Filter by HTTP status code (e.g., 200, 404, 500)", + "name": "statusCode", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "The frequency of data points in the response. If the `stepSeconds` is 60, then the response will contain one data point per minute.", + "name": "stepSeconds", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + ], + "deprecationReason": null, + "description": "Get HTTP request metrics for a service", + "isDeprecated": false, + "name": "httpMetrics", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "HttpMetricsResult", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": "The end of the period to get metrics for.", + "name": "endDate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": "Filter by HTTP method (e.g., GET, POST)", + "name": "method", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "Filter by request path", + "name": "path", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": "The start of the period to get metrics for.", + "name": "startDate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": "The frequency of data points in the response. If the `stepSeconds` is 60, then the response will contain one data point per minute.", + "name": "stepSeconds", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + ], + "deprecationReason": null, + "description": "Get HTTP request metrics for a service, grouped by status code", + "isDeprecated": false, + "name": "httpMetricsGroupedByStatus", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "HttpMetricsByStatusResult", + "ofType": null + } + } + } + } + }, { "args": [ { @@ -45720,6 +46690,38 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": "Whether automatic deployment diagnosis is enabled for this workspace.", + "isDeprecated": false, + "name": "hasAutomaticDiagnosis", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": "Whether this workspace has access to guardrails policies.", + "isDeprecated": false, + "name": "hasGuardrailsAccess", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, { "args": [], "deprecationReason": null, @@ -46014,6 +47016,18 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "subscriptionPlanLimit", + "type": { + "kind": "SCALAR", + "name": "SubscriptionPlanLimit", + "ofType": null + } + }, { "args": [], "deprecationReason": null, @@ -46672,6 +47686,68 @@ "name": "WorkspacePolicy", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "enabled", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "policy", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "WorkspacePolicyName", + "ofType": null + } + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "WorkspacePolicyItemUpdateInput", + "possibleTypes": null + }, + { + "description": null, + "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "RESTRICT_PUBLIC_TCP_PROXIES" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "RESTRICT_RAILWAY_DOMAIN_GENERATION" + } + ], + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "ENUM", + "name": "WorkspacePolicyName", + "possibleTypes": null + }, { "description": null, "enumValues": null,