Last active
October 22, 2024 20:29
-
-
Save FH-Inway/9f1de243fbbdd612590935d0d46c8275 to your computer and use it in GitHub Desktop.
Draft for some module functions for powershelling the GitHub ProjectV2 GraphQL API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Example showcasing the use of the GitHub-Project-Manager.ps1 | |
| # Iterates over all cmdlets of the d365fo.tools module | |
| # and creates a draft issue for each cmdlet that is missing a draft issue. | |
| Import-Module .\TempPoint\GitHub-Project-Manager.ps1 | |
| $project = Get-UserProject -User FH-Inway -ProjectNumber 2 | |
| $existingItems = Get-ProjectItems -ProjectId $project.id | |
| # Import-Module d365fo.tools | |
| Import-Module -Name .\d365fo.tools -Verbose -Force | |
| $cmdlets = Get-Command -Module d365fo.tools | |
| # Test with only 5 cmdlets | |
| $cmdlets = $cmdlets | Select-Object -First 5 | |
| Write-Host "Creating draft issues for $($cmdlets.Count) cmdlets" | |
| foreach ($cmdlet in $cmdlets) { | |
| $cmdletName = $cmdlet.Name | |
| New-ProjectDraftItem -ProjectId $project.id -Title $cmdletName -Items $existingItems | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Remove-Module GitHub-Project-Manager | |
| Import-Module .\GitHub-Project-Manager.ps1 | |
| $project = Get-UserProject -User FH-Inway -ProjectNumber 2 | |
| $items = Get-ProjectItems -ProjectId $project.id | |
| $fields = Get-ProjectFields -ProjectId $project.id | |
| $fieldTypes = Get-ProjectFieldTypes -ProjectId $project.id | |
| $fieldValues = @( | |
| @{ Name = "Status"; Value = "Todo"}, | |
| @{ Name = "Support"; Value = "Triage pending"}, | |
| @{ Name = "Complexity"; Value = "Triage pending" } | |
| ) | |
| $params = @{ | |
| ProjectId = $project.id | |
| Title = "New-D365Bacpac" | |
| Items = $items | |
| Fields = $fields | |
| FieldTypes = $fieldTypes | |
| FieldValues = $fieldValues | |
| } | |
| $startTime = Get-Date | |
| $item = New-ProjectDraftItem @params | |
| $endTime = Get-Date | |
| $elapsedTime = $endTime - $startTime | |
| Write-Host "Elapsed time: $($elapsedTime.TotalSeconds) seconds" | |
| $item |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # A PowerShell module file that contains the functions for the GitHub Project Manager module. | |
| # It requires the GitHub CLI to be installed and authenticated. | |
| function Get-Node { | |
| param ( | |
| [string] $NodeId, | |
| [string] $Type, | |
| [string[]] $Fields = @("id") | |
| ) | |
| $GitHubGraphQLAPIQuery = " | |
| query(`$node_id: ID!){ | |
| node(id: `$node_id) { | |
| ... on $Type { | |
| $Fields | |
| } | |
| } | |
| }" | |
| $node = gh api graphql -f query=$GitHubGraphQLAPIQuery -f node_id=$NodeId | |
| $node = $node | ConvertFrom-Json | |
| $node.data.node | |
| } | |
| function Get-UserProject { | |
| [CmdletBinding()] | |
| param ( | |
| [string] $User, | |
| [int] $ProjectNumber, | |
| [string[]] $Fields = @("id", "title") | |
| ) | |
| $GitHubGraphQLAPIQuery = " | |
| query(`$user: String! `$project_number: Int!){ | |
| user(login: `$user){ | |
| projectV2(number: `$project_number) { | |
| $Fields | |
| } | |
| } | |
| } | |
| " | |
| $project = gh api graphql -f query=$GitHubGraphQLAPIQuery -f user=$User -F project_number=$ProjectNumber | |
| $project = $project | ConvertFrom-Json | |
| $project.data.user.projectV2 | |
| } | |
| function Get-ProjectFields { | |
| [CmdletBinding()] | |
| param ( | |
| [string] $ProjectId | |
| ) | |
| $GitHubGraphQLAPIQuery = " | |
| query(`$project_id: ID!){ | |
| node(id: `$project_id) { | |
| ... on ProjectV2 { | |
| fields(first: 20) { | |
| nodes { | |
| ... on ProjectV2Field { | |
| id | |
| name | |
| } | |
| ... on ProjectV2IterationField { | |
| id | |
| name | |
| configuration { | |
| iterations { | |
| startDate | |
| id | |
| } | |
| } | |
| } | |
| ... on ProjectV2SingleSelectField { | |
| id | |
| name | |
| options { | |
| id | |
| name | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| " | |
| $projectFields = gh api graphql -f query=$GitHubGraphQLAPIQuery -f project_id=$ProjectId | |
| $projectFields = $projectFields | ConvertFrom-Json | |
| $projectFields.data.node.fields.nodes | |
| } | |
| function Get-ProjectFieldTypes { | |
| param ( | |
| [string] $ProjectId | |
| ) | |
| $GitHubGraphQLAPIQuery = " | |
| query(`$project_id: ID!){ | |
| node(id: `$project_id) { | |
| ... on ProjectV2 { | |
| fields(first: 100) { | |
| nodes { | |
| ... on ProjectV2FieldCommon { | |
| __typename | |
| id | |
| name | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }" | |
| $fieldTypes = gh api graphql -f query=$GitHubGraphQLAPIQuery -f project_id=$ProjectId | |
| $fieldTypes = $fieldTypes | ConvertFrom-Json | |
| $fieldTypes.data.node.fields.nodes | |
| } | |
| function New-ProjectDraftItem { | |
| param ( | |
| [string] $ProjectId, | |
| [string] $Title, | |
| [Object[]] $FieldValues, | |
| [Object[]] $Items = $null, | |
| [Object[]] $Fields = $null, | |
| [Object[]] $FieldTypes = $null | |
| ) | |
| $existingItem = Get-ProjectItemByTitle -ProjectId $ProjectId -Title $Title -Items $Items | |
| if ($existingItem) { | |
| Write-Host "Draft item with title: $Title already exists with id: $($existingItem.id)" | |
| return | |
| } | |
| Write-Host "Creating new draft item with title: $Title" | |
| $GitHubGraphQLAPIQuery = " | |
| mutation(`$project_id: ID! `$title: String!){ | |
| addProjectV2DraftIssue(input: {projectId: `$project_id title: `$title}) { | |
| projectItem { | |
| id | |
| } | |
| } | |
| }" | |
| $item = gh api graphql -f query=$GitHubGraphQLAPIQuery -f project_id=$ProjectId -f title=$Title | |
| $item = $item | ConvertFrom-Json | |
| Write-Host "Draft item created with id: $($item.data.addProjectV2DraftIssue.projectItem.id)" | |
| if (-not $FieldValues) { | |
| return $item | |
| } | |
| $params = @{ | |
| ProjectId = $ProjectId | |
| ItemId = $item.data.addProjectV2DraftIssue.projectItem.id | |
| } | |
| $projectFields = $Fields | |
| if (-not $projectFields) { | |
| $projectFields = Get-ProjectFields -ProjectId $ProjectId | |
| } | |
| $fieldValuesIds = @() | |
| foreach ($field in $FieldValues) { | |
| $projectField = $projectFields | Where-Object { $_.name -eq $field.Name } | |
| $fieldId = $projectField.id | |
| if ($projectField.options) { | |
| $projectFieldOption = $projectField.options | Where-Object { $_.name -eq $field.Value } | |
| $fieldValuesIds += @{ FieldId = $fieldId; Value = $projectFieldOption.id } | |
| } | |
| else { | |
| $fieldValuesIds += @{ FieldId = $fieldId; Value = $field.Value } | |
| } | |
| } | |
| $params.FieldValues = $fieldValuesIds | |
| if (-not $FieldTypes) { | |
| $FieldTypes = Get-ProjectFieldTypes -ProjectId $ProjectId | |
| } | |
| $params.FieldTypes = $FieldTypes | |
| Write-Host "Updating draft item fields with values" | |
| $item = Update-ProjectDraftItemFieldsWithValues @params | |
| $item = $item | ConvertFrom-Json | |
| Write-Host "Draft item updated with id: $($item.data.fieldUpdate2.projectV2Item.id)" | |
| $item | |
| } | |
| function Get-ProjectItems { | |
| param ( | |
| [string] $ProjectId | |
| ) | |
| $firstPage = Get-ProjectItemsPage -ProjectId $ProjectId | |
| $items = $firstPage.data.node.items.nodes | |
| $pageInfo = $firstPage.data.node.items.pageInfo | |
| while ($pageInfo.hasNextPage) { | |
| $nextPage = Get-ProjectItemsPage -ProjectId $ProjectId -After $pageInfo.endCursor | |
| $items += $nextPage.data.node.items.nodes | |
| $pageInfo = $nextPage.data.node.items.pageInfo | |
| } | |
| $items | |
| } | |
| function Get-ProjectItemsPage { | |
| param ( | |
| [string] $ProjectId, | |
| [string] $After = "null" | |
| ) | |
| $GitHubGraphQLAPIQuery = " | |
| query(`$project_id: ID! `$after: String){ | |
| node(id: `$project_id) { | |
| ... on ProjectV2 { | |
| items(first: 100, after: `$after) { | |
| nodes { | |
| id | |
| content{ | |
| ... on DraftIssue { | |
| title | |
| } | |
| ...on Issue { | |
| title | |
| } | |
| ...on PullRequest { | |
| title | |
| } | |
| } | |
| } | |
| pageInfo { | |
| endCursor | |
| startCursor | |
| hasNextPage | |
| hasPreviousPage | |
| } | |
| } | |
| } | |
| } | |
| }" | |
| $itemsPage = gh api graphql -f query=$GitHubGraphQLAPIQuery -f project_id=$ProjectId -f after=$After | |
| $itemsPage = $itemsPage | ConvertFrom-Json | |
| $itemsPage | |
| } | |
| function Get-ProjectItemByTitle { | |
| param ( | |
| [string] $ProjectId, | |
| [string] $Title, | |
| [Object[]] $Items = $null | |
| ) | |
| if (-not $Items) { | |
| $Items = Get-ProjectItems -ProjectId $ProjectId | |
| } | |
| $item = $Items | Where-Object { $_.content.title -eq $Title } | |
| $item | |
| } | |
| function Update-ProjectDraftItemFieldValue { | |
| param ( | |
| [string] $ProjectId, | |
| [string] $ItemId, | |
| [string] $FieldId, | |
| [string] $Value | |
| ) | |
| $field = Get-Node -NodeId $FieldId -Type "ProjectV2FieldCommon" -Fields "__typename" | |
| $fieldValue = "" | |
| switch ($field.__typename) { | |
| "ProjectV2SingleSelectField" { | |
| $fieldValue = "{ singleSelectOptionId: `$value }" | |
| } | |
| "ProjectV2IterationField" { | |
| $fieldValue = "{ iterationId: `$value }" | |
| } | |
| "ProjectV2TextField" { | |
| $fieldValue = "{ text: `$value }" | |
| } | |
| Default { | |
| $fieldValue = "{ text: `$value }" | |
| } | |
| } | |
| $GitHubGraphQLAPIQuery = " | |
| mutation(`$project_id: ID! `$item_id: ID! `$field_id: ID! `$value: String!){ | |
| updateProjectV2ItemFieldValue( | |
| input: { | |
| projectId: `$project_id | |
| itemId: `$item_id | |
| fieldId: `$field_id | |
| value: $fieldValue | |
| } | |
| ) { | |
| projectV2Item { | |
| id | |
| } | |
| } | |
| }" | |
| gh api graphql -f query=$GitHubGraphQLAPIQuery -f project_id=$ProjectId -f item_id=$ItemId -f field_id=$FieldId -f value=$Value | |
| } | |
| function Update-ProjectDraftItemFieldsWithValues { | |
| param ( | |
| [string] $ProjectId, | |
| [string] $ItemId, | |
| [Object[]] $FieldValues, | |
| [Object[]] $FieldTypes | |
| ) | |
| # Define the base GraphQL mutation | |
| $baseMutation = @" | |
| mutation(`$projectId: ID!, `$itemId: ID! {0}) {{ | |
| {1} | |
| }} | |
| "@ # Has to finish on first column | |
| # Initialize the dynamic parts of the mutation and parameters | |
| $fieldMutations = @() | |
| $fieldVars = @() | |
| $fieldParams = @{} | |
| # Loop through the dynamic fields to construct the mutation and parameters | |
| for ($i = 0; $i -lt $FieldValues.Count; $i++) { | |
| $field = $FieldValues[$i] | |
| $fieldIdParam = "fieldId$i" | |
| $valueParam = "value$i" | |
| $typename = "" | |
| if ($FieldTypes) { | |
| $typename = $FieldTypes | Where-Object { $_.id -eq $field.FieldId } | Select-Object -ExpandProperty __typename | |
| } | |
| else { | |
| $params = @{ | |
| NodeId = $field.FieldId | |
| Type = "ProjectV2FieldCommon" | |
| Fields = @("__typename") | |
| FieldTypes = $FieldTypes | |
| } | |
| $fieldNode = Get-Node @params | |
| $typename = $fieldNode.__typename | |
| } | |
| $fieldValue = "" | |
| switch ($typename) { | |
| "ProjectV2SingleSelectField" { | |
| $fieldValue = "{ singleSelectOptionId: `$$valueParam }" | |
| } | |
| "ProjectV2IterationField" { | |
| $fieldValue = "{ iterationId: `$$valueParam }" | |
| } | |
| "ProjectV2TextField" { | |
| $fieldValue = "{ text: `$$valueParam }" | |
| } | |
| Default { | |
| $fieldValue = "{ text: `$$valueParam }" | |
| } | |
| } | |
| # Append to the field mutations | |
| $fieldMutations += "fieldUpdate$($i): updateProjectV2ItemFieldValue(input: {projectId: `$projectId, itemId: `$itemId, fieldId: `$$fieldIdParam, value: $fieldValue }) { projectV2Item { id } }" | |
| # Add to the field parameters | |
| $fieldParams["$fieldIdParam"] = $field.FieldId | |
| $fieldParams["$valueParam"] = $field.Value | |
| # Add to the field variables | |
| $fieldVars += "`$$($fieldIdParam): ID!" | |
| $fieldVars += "`$$($valueParam): String!" | |
| } | |
| # Construct the final mutation | |
| $variables = (", " + ($fieldVars -join ", ")) | |
| $mutations = ($fieldMutations -join "`n") | |
| $finalMutation = $baseMutation -f $variables, $mutations | |
| # Add static parameters | |
| $fieldParams["projectId"] = $ProjectId | |
| $fieldParams["itemId"] = $ItemId | |
| # Execute the gh api graphql command | |
| $ghParams = @("-f", "query=$finalMutation") | |
| foreach ($key in $fieldParams.Keys) { | |
| $ghParams += @("-f", "$key=$($fieldParams[$key])") | |
| } | |
| # Run the command | |
| & gh api graphql @ghParams | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment