Functions/Get-GraphQLVariableList.ps1
function Get-GraphQLVariableList { <# .SYNOPSIS Gets a list of variable definitions from a GraphQL query. .DESCRIPTION Gets a list of variable (argument) names, types, and nullable status from a GraphQL operation. .PARAMETER Query The GraphQL operation (query, mutation, subscription, or fragment) to obtain the variable definitions from. .PARAMETER FilePath The path to a file containing a GraphQL query to obtain the variable definitions from. .EXAMPLE $query = ' query RollDice($dice: Int!, $sides: Int) { rollDice(numDice: $dice, numSides: $sides) }' Get-GraphQLVariableList -Query $query | Format-Table Gets a list of variable definitions from a GraphQL query and renders the results to the console as a table. .EXAMPLE $queryFilePath = "./queries/rolldice.gql" Get-GraphQLVariableList -FilePath $queryFilePath Gets a list of variable definitions from a file containing a GraphQL query and renders the results to the console as a table. .EXAMPLE $fragment = ' fragment UserInfo($includeEmail: Boolean!) on User { name email @include(if: $includeEmail) }' Get-GraphQLVariableList -Query $fragment Gets a list of variable definitions from a GraphQL fragment. .INPUTS System.String .LINK https://graphql.org/ Format-Table Invoke-GraphQLQuery #> [CmdletBinding()] [Alias('ggqlvl')] [OutputType([GraphQLVariable], [System.Collections.Hashtable])] <##> Param ( [Parameter(Mandatory = $true, ParameterSetName = "Query", ValueFromPipeline = $true, Position = 0)][ValidateLength(12, 1073741791)][Alias("Operation", "Mutation", "Fragment")][System.String]$Query, [Parameter(Mandatory = $false, ParameterSetName = "FilePath", Position = 0)][ValidateNotNullOrEmpty()][Alias('f', 'Path')][System.IO.FileInfo]$FilePath ) BEGIN { class GraphQLVariable { [bool]$HasVariables = $false [string]$Operation = "" [string]$OperationType = "" [string]$Parameter = "" [string]$Type = "" [nullable[bool]]$Nullable = $null [nullable[bool]]$IsArray = $null [string]$RawType = "" } } PROCESS { # Exception to be used through the function in the case that an invalid GraphQL query or mutation is passed: $ArgumentException = New-Object -TypeName ArgumentException -ArgumentList "Not a valid GraphQL operation (query, mutation, subscription, or fragment). Verify syntax and try again." # Get the raw GraphQL query content [string]$graphQlQuery = $Query if ($PSBoundParameters.ContainsKey("FilePath")) { if (Test-Path -Path $FilePath) { $graphQlQuery = Get-Content -Path $FilePath -Raw } else { Write-Error "Unable to read file at path: $FilePath" -Category ReadError -ErrorAction Stop } } # Ensure we have valid input if ([string]::IsNullOrWhiteSpace($graphQlQuery)) { Write-Error -Exception $ArgumentException -Category InvalidArgument -ErrorAction Stop } # Remove comments and normalize whitespace $cleanQuery = $graphQlQuery -replace '#[^\r\n]*', '' -replace '\s+', ' ' # Simple check for operation keywords if (-not ($cleanQuery -match '(query|mutation|subscription|fragment)')) { Write-Error -Exception $ArgumentException -Category InvalidArgument -ErrorAction Stop } # List of objects that are returned by default: $results = [System.Collections.Generic.List[GraphQLVariable]]::new() # Parse the entire query at once using more comprehensive regex # This regex captures: operation type, optional name, optional variables in parentheses, optional "on Type" for fragments $fullOperationPattern = '(query|mutation|subscription|fragment)\s*([a-zA-Z_][a-zA-Z0-9_]*)?\s*(\([^)]+\))?\s*(on\s+[a-zA-Z_][a-zA-Z0-9_]*)?\s*\{' # Variable pattern to extract individual variables from the parentheses $variablePattern = '\$([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*([a-zA-Z0-9_\[\]!]+)(?:\s*=\s*[^,)]+)?' $operationMatches = [regex]::Matches($cleanQuery, $fullOperationPattern, 'IgnoreCase') if ($operationMatches.Count -eq 0) { Write-Error -Exception $ArgumentException -Category InvalidArgument -ErrorAction Stop } foreach ($operationMatch in $operationMatches) { $operationType = $operationMatch.Groups[1].Value.ToLower() $operationName = if ($operationMatch.Groups[2].Success) { $operationMatch.Groups[2].Value } else { $operationType } $variablesSection = if ($operationMatch.Groups[3].Success) { $operationMatch.Groups[3].Value } else { "" } $onType = if ($operationMatch.Groups[4].Success) { $operationMatch.Groups[4].Value } else { "" } # For fragments, include the "on Type" part in the operation name if present if ($operationType -eq "fragment" -and $onType) { $operationName = if ($operationMatch.Groups[2].Success) { "$($operationMatch.Groups[2].Value) $onType" } else { $onType } } # Extract variables from the variables section if ($variablesSection) { $variableMatches = [regex]::Matches($variablesSection, $variablePattern) if ($variableMatches.Count -gt 0) { foreach ($variableMatch in $variableMatches) { $paramName = $variableMatch.Groups[1].Value $rawType = $variableMatch.Groups[2].Value $gqlVariable = [GraphQLVariable]::new() $gqlVariable.HasVariables = $true $gqlVariable.Operation = $operationName $gqlVariable.OperationType = $operationType $gqlVariable.Parameter = $paramName $gqlVariable.RawType = $rawType # Parse type information $cleanType = $rawType -replace '[\[\]!]', '' $gqlVariable.Type = $cleanType # Check if nullable (! means non-null, so nullable = false if ! is present) $gqlVariable.Nullable = -not $rawType.Contains('!') # Check if array $gqlVariable.IsArray = $rawType.Contains('[') -and $rawType.Contains(']') $results.Add($gqlVariable) } } else { # Operation exists but no variables $gqlVariable = [GraphQLVariable]::new() $gqlVariable.HasVariables = $false $gqlVariable.Operation = $operationName $gqlVariable.OperationType = $operationType $results.Add($gqlVariable) } } else { # Operation exists but no variables section $gqlVariable = [GraphQLVariable]::new() $gqlVariable.HasVariables = $false $gqlVariable.Operation = $operationName $gqlVariable.OperationType = $operationType $results.Add($gqlVariable) } } if ($results.Count -eq 0) { Write-Error -Exception $ArgumentException -Category InvalidArgument -ErrorAction Stop } return $results } } |