Skip to content

Commit 4e44d98

Browse files
author
James Brundage
committed
feat: Get-GQL ( Fixes #2 )
1 parent 367aeb1 commit 4e44d98

File tree

1 file changed

+202
-0
lines changed

1 file changed

+202
-0
lines changed

Commands/Get-GQL.ps1

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
function Get-GQL
2+
{
3+
<#
4+
.SYNOPSIS
5+
Gets a GraphQL query.
6+
.DESCRIPTION
7+
Gets a GraphQL query and returns the results as a PowerShell object.
8+
.EXAMPLE
9+
# Getting git sponsorship information from GitHub GraphQL.
10+
# **To use this example, we'll need to provide `$MyPat` with a Personal Access Token.**
11+
Get-GQL -Query ./Examples/GitSponsors.gql -PersonalAccessToken $myPat
12+
.EXAMPLE
13+
# We can decorate graph object results to customize them.
14+
15+
# Let's add a Sponsors property to the output object that returns the sponsor nodes.
16+
Update-TypeData -TypeName 'GitSponsors' -MemberName 'Sponsors' -MemberType ScriptProperty -Value {
17+
$this.viewer.sponsors.nodes
18+
} -Force
19+
20+
# And let's add a Sponsoring property to the output object that returns the sponsoring nodes.
21+
Update-TypeData -TypeName 'GitSponsors' -MemberName 'Sponsoring' -MemberType ScriptProperty -Value {
22+
$this.viewer.sponsoring.nodes
23+
} -Force
24+
25+
# And let's display sponsoring and sponsors by default
26+
Update-TypeData -TypeName 'GitSponsors' -DefaultDisplayPropertySet 'Sponsors','Sponsoring' -Force
27+
28+
# Now we can run the query and get the results.
29+
Get-GQL -Query ./Examples/GitSponsors.gql -PersonalAccessToken $myPat -PSTypeName 'GitSponsors' |
30+
Select-Object -Property Sponsors,Sponsoring
31+
#>
32+
[Alias('GQL','GraphAPI','GraphQL','GraphQueryLanguage')]
33+
[CmdletBinding(SupportsShouldProcess)]
34+
param(
35+
# One or more queries to run.
36+
[Parameter(ValueFromPipelineByPropertyName)]
37+
[Alias('FullName')]
38+
[string[]]
39+
$Query,
40+
41+
# The Personal Access Token to use for the query.
42+
[Parameter(ValueFromPipelineByPropertyName)]
43+
[Alias('Token','PAT','AccessToken')]
44+
[string]
45+
$PersonalAccessToken,
46+
47+
# The GraphQL endpoint to query.
48+
[Parameter(ValueFromPipelineByPropertyName)]
49+
[Alias('uri')]
50+
[uri]
51+
$GraphQLUri = "https://api.github.com/graphql",
52+
53+
# Any variables or parameters to provide to the query.
54+
[Parameter(ValueFromPipelineByPropertyName)]
55+
[Alias('Parameters','Variable','Variables')]
56+
[Collections.IDictionary]
57+
$Parameter,
58+
59+
# Any additional headers to include in the request
60+
[Alias('Headers')]
61+
[Collections.IDictionary]
62+
$Header,
63+
64+
# Adds PSTypeName(s) to use for the output object, making it a decorated object.
65+
# By decorating an object with one or more typenames, we can:
66+
#
67+
# * Add additional properties and methods to the object
68+
# * Format the output object any way we want
69+
[Alias('Decorate','Decoration','PSTypeNames','TypeName','TypeNames','Type')]
70+
[string[]]
71+
$PSTypeName
72+
)
73+
74+
process {
75+
#region Handle Input
76+
# Capture the input object
77+
$inputObject = $_
78+
if ($inputObject -is [IO.FileInfo]) {
79+
if ($inputObject.Extension -notin '.gql','.graphql') {
80+
Write-Verbose "Skipping non-GQL file: $($inputObject.FullName)"
81+
continue
82+
}
83+
}
84+
#endregion Handle Input
85+
86+
#region Optionally Determine GraphQLUri from InvocationName
87+
if (-not $PSBoundParameters['GraphQLUri'] -and
88+
$MyInvocation.InvocationName -match '\w+\.\w+/') {
89+
$GraphQLUri = $MyInvocation.InvocationName
90+
}
91+
#endregion Optionally Determine GraphQLUri from InvocationName
92+
93+
#region Cache the Access Token
94+
if (-not $PSBoundParameters['PersonalAccessToken']) {
95+
if ($script:GraphQLTokenCache -is [Collections.IDictionary] -and
96+
$script:GraphQLTokenCache.Contains($GraphQLUri)) {
97+
$PersonalAccessToken = $script:GraphQLTokenCache[$GraphQLUri]
98+
}
99+
} elseif ($PSBoundParameters['PersonalAccessToken']) {
100+
if (-not $script:GraphQLTokenCache) {
101+
$script:GraphQLTokenCache = [Ordered]@{}
102+
}
103+
$script:GraphQLTokenCache[$GraphQLUri] = $PersonalAccessToken
104+
}
105+
#endregion Cache the Access Token
106+
107+
#region Prepare the REST Parameters
108+
$invokeSplat = @{
109+
Headers = if ($header) {
110+
$invokeSplat.Headers = [Ordered]@{} + $header
111+
} else {
112+
[Ordered]@{}
113+
}
114+
Uri = $GraphQLUri
115+
Method = 'POST'
116+
}
117+
$invokeSplat.Headers.Authorization = "bearer $PersonalAccessToken"
118+
#endregion Prepare the REST Parameters
119+
120+
#region Handle Each Query
121+
:nextQuery foreach ($gqlQuery in $Query) {
122+
$queryLines = @($gqlQuery -split '(?>\r\n|\n)')
123+
#region Check for File or Cached Query
124+
if ($queryLines.Length -eq 1) {
125+
if ($script:GraphQLQueries -is [Collections.IDictionary] -and
126+
$script:GraphQLQueries.Contains($gqlQuery)) {
127+
$gqlQuery = $script:GraphQLQueries[$gqlQuery]
128+
}
129+
130+
if (Test-Path $gqlQuery) {
131+
$newQuery = Get-Content -Path $gqlQuery -Raw
132+
$gqlQuery = $newQuery
133+
} elseif ($query -match '[\\/]') {
134+
$psCmdlet.WriteError(
135+
[Management.Automation.ErrorRecord]::new(
136+
[Exception]::new("Query file not found: '$gqlQuery'"),
137+
'NotFound',
138+
'ObjectNotFound',
139+
$gqlQuery
140+
)
141+
)
142+
continue nextQuery
143+
}
144+
}
145+
#endregion Check for File or Cached Query
146+
147+
#region Run the Query
148+
$invokeSplat.Body = [Ordered]@{query = $gqlQuery}
149+
if ($Parameter) {
150+
$invokeSplat.Body.variables = $Parameter
151+
}
152+
if ($WhatIfPreference) {
153+
$invokeSplat.Headers.Clear()
154+
$invokeSplat
155+
continue nextQuery
156+
}
157+
$invokeSplat.Body = ConvertTo-Json -InputObject $invokeSplat.Body -Depth 10
158+
$shouldProcessMessage = "Querying $GraphQLUri with $gqlQuery"
159+
if (-not $PSCmdlet.ShouldProcess($shouldProcessMessage)) {
160+
continue nextQuery
161+
}
162+
$gqlOutput = Invoke-RestMethod @invokeSplat *>&1
163+
if ($gqlOutput -is [Management.Automation.ErrorRecord]) {
164+
$PSCmdlet.WriteError($gqlOutput)
165+
continue nextQuery
166+
}
167+
elseif ($gqlOutput.errors) {
168+
foreach ($gqlError in $gqlOutput.errors) {
169+
$psCmdlet.WriteError((
170+
[Management.Automation.ErrorRecord]::new(
171+
[Exception]::new($gqlError.message),
172+
'GQLError',
173+
'NotSpecified', $gqlError
174+
)
175+
))
176+
}
177+
continue nextQuery
178+
}
179+
elseif ($gqlOutput.data) {
180+
if ($PSTypeName) {
181+
$gqlOutput.data.pstypenames.clear()
182+
for ($goBackwards = $pstypename.Length - 1; $goBackwards -ge 0; $goBackwards--) {
183+
$gqlOutput.data.pstypenames.add($pstypename[$goBackwards])
184+
}
185+
}
186+
$gqlOutput.data
187+
}
188+
elseif ($gqlOutput) {
189+
if ($PSTypeName) {
190+
$gqlOutput.pstypenames.clear()
191+
for ($goBackwards = $pstypename.Length - 1; $goBackwards -ge 0; $goBackwards--) {
192+
$gqlOutput.pstypenames.add($pstypename[$goBackwards])
193+
}
194+
}
195+
$gqlOutput
196+
}
197+
#endregion Run the Query
198+
#endregion Handle Each Query
199+
200+
}
201+
}
202+
}

0 commit comments

Comments
 (0)