diff --git a/.gitignore b/.gitignore index 27aab8b..571b283 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode/** -.DS_Store \ No newline at end of file +.DS_Store +*.patch \ No newline at end of file diff --git a/Test/include/callPrivateContext.ps1 b/Test/include/callPrivateContext.ps1 new file mode 100644 index 0000000..f2fa515 --- /dev/null +++ b/Test/include/callPrivateContext.ps1 @@ -0,0 +1,29 @@ +# CALL PRIVATE FUNCTIONS +# +# This file enables tests to invoke private (non-exported) functions +# defined inside the main module by evaluating a provided scriptblock +# in the module context. +# +# It relies on variables initialized by module.helper.ps1. +# REQUIRED: +# $MODULE_PATH - Full path to the module .psm1 file (or a file inside its folder) +# +# THIS INCLUDE REQUIRES module.helper.ps1 +if(-not $MODULE_PATH){ throw "Missing MODULE_PATH variable initialization. Check for module.helper.ps1 file." } + +function Invoke-PrivateContext { + param ( + [Parameter(Mandatory, Position = 0)] + [scriptblock]$ScriptBlock + ) + + $modulePath = $MODULE_PATH | Split-Path -Parent + $module = Import-Module -Name $modulePath -PassThru + + if ($null -eq $module) { + throw "Failed to import the main module." + } + + & $module $ScriptBlock +} Export-ModuleMember -Function Invoke-PrivateContext + diff --git a/Test/private/MockCall_GitHubOrgProjectWithFields.ps1 b/Test/private/MockCall_GitHubOrgProjectWithFields.ps1 index ddc94d6..bd807fe 100644 --- a/Test/private/MockCall_GitHubOrgProjectWithFields.ps1 +++ b/Test/private/MockCall_GitHubOrgProjectWithFields.ps1 @@ -1,5 +1,5 @@ -function MockCall_GitHubOrgProjectWithFields{ +function MockCall_GitHubOrgProjectWithFields { Param( [string]$Owner, [string]$ProjectNumber, @@ -23,7 +23,7 @@ function MockCall_GitHubOrgProjectWithFields{ MockCallJson -Command $cmd -Filename $FileName } -function MockCall_GitHubOrgProjectWithFields_Null{ +function MockCall_GitHubOrgProjectWithFields_Null { Param( [string]$Owner, [string]$ProjectNumber diff --git a/Test/private/MockCall_GitHubUpdateItemValues.ps1 b/Test/private/MockCall_GitHubUpdateItemValues.ps1 new file mode 100644 index 0000000..ca255d9 --- /dev/null +++ b/Test/private/MockCall_GitHubUpdateItemValues.ps1 @@ -0,0 +1,43 @@ +function MockCall_GitHubUpdateItemValues { + [CmdletBinding()] + param( + [parameter(Mandatory)][string]$ProjectId, + [parameter(Mandatory)][string]$ItemId, + [parameter(Mandatory)][string]$FieldId, + [parameter(Mandatory)][string]$Value, + [parameter(Mandatory)][string]$Type, + [parameter()][switch]$Async + ) + + $resultFile = "invoke-GitHubUpdateItemValue-$ItemId-$FieldId.json" + + $command = 'Invoke-GitHubUpdateItemValues -ProjectId {ProjectId} -ItemId {ItemId} -FieldId {FieldId} -Value "{Value}" -Type text' + + if ($Async) { + $modulePath = $MODULE_PATH | split-path -Parent + $moduleTestPath = Join-Path -Path $modulePath -ChildPath 'Test' + + $command = 'Import-Module {modulepath} ; ' + $command + $command = $command -replace '{modulepath}', $modulePath + } + + $command = $command -replace '{ProjectId}', $ProjectId + $command = $command -replace '{ItemId}', $ItemId + $command = $command -replace '{FieldId}', $FieldId + $command = $command -replace '{Value}', $Value + + Set-InvokeCommandMock -Command "Import-Module $moduleTestPath ; Get-MockFileContentJson -filename $($ResultFile)" -Alias $command + +} + +function MockCall_GetItem{ + [cmdletbinding()] + param( + [parameter(Mandatory)][string]$ItemId + ) + + $command = "Invoke-GetItem -ItemId $ItemId" + $resultFile = "invoke-getitem-$ItemId.json" + + Set-InvokeCommandMock -Command "Get-MockFileContentJson -filename $($ResultFile)" -Alias $command +} \ No newline at end of file diff --git a/Test/private/MockCall_Project.ps1 b/Test/private/MockCall_Project.ps1 new file mode 100644 index 0000000..467a6e5 --- /dev/null +++ b/Test/private/MockCall_Project.ps1 @@ -0,0 +1,268 @@ +function Get-Mock_Project_625 { + + $project = @{} + + <# + Reset-InvokeCommandMock + Enable-InvokeCommandAliasModule + $cmd = 'Invoke-GitHubOrgProjectWithFields -Owner octodemo -ProjectNumber 625 -afterFields "" -afterItems ""' + save-invokeAsMockFile $cmd "invoke-GitHubOrgProjectWithFields-octodemo-625.json" + #> + + $name = "invoke-GitHubOrgProjectWithFields-octodemo-625" + $project.projectFile = $name + ".json" + $project.projectFile_skipitems = $name + "-skipitems.json" + + $content = Get-MockFileContentJson -FileName $project.projectFile + $p = $content.data.organization.projectV2 + + # Project info + $project.id = $p.id + $project.owner = $p.owner.login + $project.number = $p.number + $project.url = $p.url + + # Add Items to mock + Add-ItemsToMock -project $project + + ############################# + # Update Status on DueDate # + ############################# + <# + $prj = Get-Project -Owner octodemo -ProjectNumber 625 + $prj.items.values | Select id,title,DueDate,Status,Comment | Sort-Object title | ft + + id Title DueDate Status Comment + -- ----- ------- ------ ------- + PVTI_lADOAlIw4c4A0Lf4zgYNTyM draft0 2025-03-01 Planned Change AR Past + PVTI_lADOAlIw4c4A0Lf4zgYVsJc draft1 9999-12-12 Planned Ignore as Future + PVTI_lADOAlIw4c4A0Lf4zgfNuvM draft2 9999-12-12 ActionRequired Change to P as AR+Future + PVTI_lADOAlIw4c4A0Lf4zgfNum4 draft3 2025-03-03 ActionRequired Ignore as Past and AR + PVTI_lADOAlIw4c4A0Lf4zgYNTxI draft4 2025-03-09 In Progress (ignore not P) | (Changed AR as Past) + PVTI_lADOAlIw4c4A0Lf4zgfN77A draft5 2025-03-15 Todo (ignore not P) | (Change AR as Today) + PVTI_lADOAlIw4c4A0Lf4zgfN-44 draft6 2025-03-15 Planned Change to AR as P and Today + PVTI_lADOAlIw4c4A0Lf4zgYNTc0 draft7 2025-03-05 Done Ignore as Done | (Change AR as Past) + PVTI_lADOAlIw4c4A0Lf4zgYNTwo draft8 9999-12-12 In Progress Ignore future + PVTI_lADOAlIw4c4A0Lf4zgfOmpo draft9 9999-12-12 Todo (Ignore not P) + PVTI_lADOAlIw4c4A0Lf4zgfJYv4 Issue for development skip no DueDate + PVTI_lADOAlIw4c4A0Lf4zgfJYvk PullRequest for development skip no DueDate + + #> + $statusFieldName = "Status" + $dateFieldName = "DueDate" + $statusAction = "ActionRequired" + $statusPlanned = "Planned" + + $sf = ($content.data.organization.projectV2.fields.nodes | Where-Object { $_.name -eq $statusFieldName }) + $df = ($content.data.organization.projectV2.fields.nodes | Where-Object { $_.name -eq $dateFieldName }) + $project.updateStatusOnDueDate = @{ + statusAction = $statusAction + statusPlanned = $statusPlanned + statusDoneOther = "Todo" + fields = @{ status = $sf ; dueDate = $df } + staged = @{ + "PVTI_lADOAlIw4c4A0Lf4zgYNTyM" = @{ $($sf.id) = $statusAction } # draft0 + "PVTI_lADOAlIw4c4A0Lf4zgfN-44" = @{ $($sf.id) = $statusAction } # draft5 + "PVTI_lADOAlIw4c4A0Lf4zgfNuvM" = @{ $($sf.id) = $statusPlanned } # draft2 + } + anyStatus = @{ + "PVTI_lADOAlIw4c4A0Lf4zgYNTxI" = @{ $($sf.id) = $statusAction } # draft4 + "PVTI_lADOAlIw4c4A0Lf4zgfN77A" = @{ $($sf.id) = $statusAction } # draft9 + + } + includeDone = @{ + "PVTI_lADOAlIw4c4A0Lf4zgYNTc0" = @{ $($df.id) = "" } # draft8 + + } + includeDoneOther = @{ + "PVTI_lADOAlIw4c4A0Lf4zgfN77A" = @{ $($df.id) = "" } # draft5 + "PVTI_lADOAlIw4c4A0Lf4zgfOmpo" = @{ $($df.id) = "" } # draft9 + } + anyStatus_and_includeDoneOther = @{ + "PVTI_lADOAlIw4c4A0Lf4zgYNTxI" = @{ $($sf.id) = $statusAction } # draft4 + "PVTI_lADOAlIw4c4A0Lf4zgfN77A" = @{ $($df.id) = "" } # draft5 + "PVTI_lADOAlIw4c4A0Lf4zgfOmpo" = @{ $($df.id) = "" } # draft9 + } + } + + ############################# + # Update With Integration # + ############################# + + $project.updateWithIntegration = @{ + fieldSlug = "sf_" + integrationField = "sfUrl" + fields = @("sf_Int2","sf_Text1") + + integrationCommand = "Get-SfAccount" + + mockdata1 = @{ + command = 'Get-SfAccount "https://some.com/1234/viuew"' + data = @{ + "Text1" = "value11" + "Text2" = "value12" + "Number1" = 11 + "Int2" = 111 + } + } + + mockdata2 = @{ + command = 'Get-SfAccount "https://some.com/4321/viuew"' + data = @{ + "Text1" = "value21" + "Text2" = "value22" + "Number1" = 22 + "Int2" = 222 + } + } + + staged =@{ + PVTI_lADOAlIw4c4A0Lf4zgfJYv4 = @{ + PVTF_lADOAlIw4c4A0Lf4zg15NKg = 222 + PVTF_lADOAlIw4c4A0Lf4zg15NMg = "value21" + } + PVTI_lADOAlIw4c4A0Lf4zgfJYvk = @{ + PVTF_lADOAlIw4c4A0Lf4zg15NKg = 111 + PVTF_lADOAlIw4c4A0Lf4zg15NMg = "value11" + } + + } + } + + ############################# + # Update With Injection # + ############################# + + $project.updateWithInjection = @{ + item1 = @{ + id = "PVTI_lADOAlIw4c4A0Lf4zgYNTxI" + fieldName = "sf_Text1" + } + item2 = @{ + id = "PVTI_lADOAlIw4c4A0Lf4zgYNTc0" + fieldName = "Text1" + } + field1 = @{ + id = "PVTF_lADOAlIw4c4A0Lf4zg15NMg" + name = "sf_Text1" + } + field2 = @{ + id = "PVTF_lADOAlIw4c4A0Lf4zgp6aFk" + name = "Text1" + } + } + + + + return $project + +} + + +function Get-Mock_Project_626 { + + $project = @{} + + $project.projectFile = "invoke-GitHubOrgProjectWithFields-octodemo-626.json" + $project.projectFile_skipitems = "invoke-GitHubOrgProjectWithFields-octodemo-626-skipitems.json" + + $content = Get-MockFileContentJson -FileName $project.projectFile + $p = $content.data.organization.projectV2 + + # Project info + $project.id = $p.id + $project.owner = $p.owner.login + $project.number = $p.number + $project.url = $p.url + + # Add Items to mock + Add-ItemsToMock -project $project + + # Sync with 625 + + $project.syncBtwPrj_625 = @{} + $project.syncBtwPrj_625.staged = @{ + PVTI_lADOAlIw4c4A0QAozgfJYqo = @{ + PVTF_lADOAlIw4c4A0QAozgqofEM = 33 + PVTF_lADOAlIw4c4A0QAozgqoeOo = "Issue Text1 Value" + } + PVTI_lADOAlIw4c4A0QAozgfJYqk = @{ + PVTF_lADOAlIw4c4A0QAozgqofEM = 11 + PVTF_lADOAlIw4c4A0QAozgqoeOo = "PR Text1 Value" + } + } + + + return $project +} + +function MockCall_GetProject { + [CmdletBinding()] + param( + [parameter(Position = 0)][object]$MockProject, + [parameter()][switch]$SkipItems, + [parameter()][switch]$Cache + ) + + $p = $MockProject ; $owner = $p.owner ; $projectNumber = $p.number + + if ( $SkipItems ) { + $filename = $p.projectFile_skipitems + } + else { + $filename = $p.projectFile + } + + MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName $filename -SkipItems:$SkipItems + + if ($Cache) { + $null = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber -SkipItems:$SkipItems + } +} + +function Add-ItemsToMock { + [CmdletBinding()] + param( + [parameter(Mandatory, Position = 0)][object] $project + ) + + # Items + $project.items = @{} + $project.items.totalCount = $pActual.items.totalcount + $project.items.doneCount = 6 # too complicated to read from structure + + # Issues to find + $project.issueToFind = @{} + $project.issueToFind.Ids = ($pActual.items.nodes | Where-Object { $_.content.title -eq "Issue to find" }).Id + + # Issue for developer + $issue = $pActual.items.nodes | Where-Object { $_.content.title -eq "Issue for development" } + $project.issue = @{ + id = $issue.id + contentId = $issue.content.id + title = $issue.content.title + status = ($issue.fieldValues.nodes | Where-Object { $_.field.name -eq "Status" }).name + fieldtext = ($issue.fieldValues.nodes | Where-Object { $_.field.id -eq $($fieldtext.id) }).text + } + + # PullRequest for developer + $pullRequest = $pActual.items.nodes | Where-Object { $_.content.title -eq "PullRequest for development" } + $project.pullrequest = @{ + id = $pullRequest.id + contentId = $pullRequest.content.id + title = $pullRequest.content.title + status = ($pullRequest.fieldValues.nodes | Where-Object { $_.field.name -eq "Status" }).name + fieldtext = ($pullRequest.fieldValues.nodes | Where-Object { $_.field.id -eq $($fieldtext.id) }).text + } + + # DraftIssue for developer + $draftIssue = $pActual.items.nodes | Where-Object { $_.content.title -eq "DraftIssue for development" } + $project.draftissue = @{ + id = $draftIssue.id + contentId = $draftIssue.content.id + title = $draftIssue.content.title + status = ($draftIssue.fieldValues.nodes | Where-Object { $_.field.name -eq "Status" }).name + fieldtext = ($draftIssue.fieldValues.nodes | Where-Object { $_.field.id -eq $($fieldtext.id) }).text + + } +} \ No newline at end of file diff --git a/Test/private/MockCall_Project700.ps1 b/Test/private/MockCall_Project700.ps1 new file mode 100644 index 0000000..f4bbd84 --- /dev/null +++ b/Test/private/MockCall_Project700.ps1 @@ -0,0 +1,169 @@ + +function Get-Mock_Project_700 { + + <# + Reset-InvokeCommandMock + Enable-InvokeCommandAliasModule + $cmd = 'Invoke-GitHubOrgProjectWithFields -Owner octodemo -ProjectNumber 700 -afterFields "" -afterItems ""' + save-invokeAsMockFile $cmd "invoke-GitHubOrgProjectWithFields-octodemo-700.json" + #> + + $project = @{} + + $project.projectFile = "invoke-GitHubOrgProjectWithFields-octodemo-700.json" + $project.projectFile_skipitems = "invoke-GitHubOrgProjectWithFields-octodemo-700-skipitems.json" + + # Version of the project file modified manually to have two items with same id case sensitive + # this is used to test case sensitivity of item ids in hashtables + $project.projectFile_caseSensitive = "invoke-GitHubOrgProjectWithFields-octodemo-700-caseSensitive.json" + + $content = Get-MockFileContentJson -FileName $project.projectFile + $pActual = $content.data.organization.projectV2 + + $fieldtext = $pActual.fields.nodes | Where-Object { $_.name -eq "field-text" } + $fieldnumber = $pActual.fields.nodes | Where-Object { $_.name -eq "field-number" } + $fielddate = $pActual.fields.nodes | Where-Object { $_.name -eq "field-date" } + $fieldsingleselect = $pActual.fields.nodes | Where-Object { $_.name -eq "field-singleselect" } + + # Project info + $project.id = $pActual.id + $project.owner = $pActual.owner.login + $project.number = $pActual.number + $project.url = $pActual.url + + # Fields info + $project.fieldtext = @{ id = $fieldtext.id ; name = $fieldtext.name } + $project.fieldnumber = @{ id = $fieldnumber.id ; name = $fieldnumber.name } + $project.fielddate = @{ id = $fielddate.id ; name = $fielddate.name } + $project.fieldsingleselect = @{ id = $fieldsingleselect.id ; name = $fieldsingleselect.name ; options = $fieldsingleselect.options } + + # Items + $project.items = @{} + $project.items.totalCount = $pActual.items.totalcount + $project.items.doneCount = 6 # too complicated to read from structure + + # Issues to find + $project.issueToFind = @{} + $project.issueToFind.Ids = ($pActual.items.nodes | Where-Object { $_.content.title -eq "Issue to find" }).Id + + # Issue for developer + $issue = $pActual.items.nodes | Where-Object { $_.content.title -eq "Issue for development" } + $fss = $issue.fieldValues.nodes | Where-Object { $_.field.id -eq $($fieldsingleselect.id) } + $project.issue = @{ + id = $issue.id + contentId = $issue.content.id + title = $issue.content.title + status = ($issue.fieldValues.nodes | Where-Object { $_.field.name -eq "Status" }).name + fieldtext = ($issue.fieldValues.nodes | Where-Object { $_.field.id -eq $($fieldtext.id) }).text + fieldsingleselect = @{ + name = $fss.name + optionId = ($fss.field.options | where-object {$_.name -eq $fss.name}).Id + } + } + + # PullRequest for developer + $pullRequest = $pActual.items.nodes | Where-Object { $_.content.title -eq "PullRequest for development" } + $fss = $pullRequest.fieldValues.nodes | Where-Object { $_.field.id -eq $($fieldsingleselect.id) } + $project.pullrequest = @{ + id = $pullRequest.id + contentId = $pullRequest.content.id + title = $pullRequest.content.title + status = ($pullRequest.fieldValues.nodes | Where-Object { $_.field.name -eq "Status" }).name + fieldtext = ($pullRequest.fieldValues.nodes | Where-Object { $_.field.id -eq $($fieldtext.id) }).text + fieldsingleselect = @{ + name = $fss.name + optionId = ($fss.field.options | where-object {$_.name -eq $fss.name}).Id + } + } + + # DraftIssue for developer + $draftIssue = $pActual.items.nodes | Where-Object { $_.content.title -eq "DraftIssue for development" } + $fss = $draftIssue.fieldValues.nodes | Where-Object { $_.field.id -eq $($fieldsingleselect.id) } + $project.draftissue = @{ + id = $draftIssue.id + contentId = $draftIssue.content.id + title = $draftIssue.content.title + status = ($draftIssue.fieldValues.nodes | Where-Object { $_.field.name -eq "Status" }).name + fieldtext = ($draftIssue.fieldValues.nodes | Where-Object { $_.field.id -eq $($fieldtext.id) }).text + fieldsingleselect = @{ + name = $fss.name + optionId = ($fss.field.options | where-object {$_.name -eq $fss.name}).Id + } + } + + # Search tests + $project.searchInTitle = @{} + $project.searchInTitle.titleFilter = "development" + $project.searchInTitle.Titles = $pActual.items.nodes.content.title | Where-Object { $_ -like "*development*" } + $project.searchInTitle.totalCount = $project.searchInTitle.Titles.Count + + $project.searchInAnyField = @{} + $project.searchInAnyField."development" = @{} + $project.searchInAnyField."development".Titles = @( + "Implement caching strategy" + "Configure CI/CD pipeline" + "Issue for development" + "Create comprehensive web API setup with Node.js and Express" + "PullRequest for development" + "Create comprehensive .NET Web API development initialization documentation with 10 structured tasks" + "DraftIssue for development" + ) + $project.searchInAnyField."development".totalCount = $project.searchInAnyField."development".Titles.Count + + $project.searchInAnyField."96" = @{} + $project.searchInAnyField."96".Titles = @( + "Implement health checks and monitoring" + "Implement logging and error handling" + ) + $project.searchInAnyField."96".totalCount = $project.searchInAnyField."96".Titles.Count + + # All items except Drafts that do not have repository + $project.searchInAnyField."rulasg-dev-1" = @{} + $i = $content.data.organization.projectV2.items.nodes.content | Where-Object { $_.repository.nameWithOwner -like "$($project.owner)/rulasg-dev-1" } + $project.searchInAnyField."rulasg-dev-1".totalCount = $i.Count + $project.searchInAnyField."rulasg-dev-1".Titles = $i.title + + return $project +} + +function MockCall_GetProject_700 { + [CmdletBinding()] + param( + [parameter()][switch]$SkipItems, + [parameter()][switch]$Cache + ) + + $p = Get-Mock_Project_700 ; $owner = $p.owner ; $projectNumber = $p.number + + if( $SkipItems ){ + $filename = $p.projectFile_skipitems + } else { + $filename = $p.projectFile + } + + MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName $filename -SkipItems:$SkipItems + + if ($Cache) { + $null = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber -SkipItems:$SkipItems + } +} + +function MockCall_GetProject_700_CaseSensitive { + [CmdletBinding()] + param( + # [parameter()][switch]$SkipItems, + [parameter()][switch]$Cache + ) + + $p = Get-Mock_Project_700 ; $owner = $p.owner ; $projectNumber = $p.number + # $filenameTag = $SkipItems ? "-skipitems" : $null + # $filename = "invoke-GitHubOrgProjectWithFields-octodemo-700$filenameTag.json" + $filename = $p.projectFile_caseSensitive + + MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName $filename -SkipItems:$SkipItems + + if ($Cache) { + $null = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber -SkipItems:$SkipItems + } +} + diff --git a/Test/private/mocks/Itesm700.md b/Test/private/mocks/Itesm700.md index d7bcfd9..421bae3 100644 --- a/Test/private/mocks/Itesm700.md +++ b/Test/private/mocks/Itesm700.md @@ -1,3 +1,7 @@ +invoke-GitHubOrgProjectWithFields-octodemo-700.json +invoke-GitHubOrgProjectWithFields-octodemo-700-skipitems.json + + # Project - PVT_kwDOAlIw4c4BCe3V Name Value @@ -13,7 +17,7 @@ fields {System.Management.Automation.OrderedHashtable} number 700 url https://github.com/orgs/octodemo/projects/700 public False - + ## field-Text ```json diff --git a/Test/private/mocks/getitemdirect_1.json b/Test/private/mocks/getitemdirect_1.json deleted file mode 100644 index e60c995..0000000 --- a/Test/private/mocks/getitemdirect_1.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "data": { - "node": { - "id": "PVTI_lADNJr_OADU3Ys4GAgVO", - "type": "ISSUE", - "content": { - "id": "I_kwDOAFbrpM6s_fNK", - "url": "https://github.com/github/sales/issues/11742" - } - } - } -} diff --git a/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-625.json b/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-625.json index a9c82e4..c22ace9 100644 --- a/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-625.json +++ b/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-625.json @@ -7,29 +7,38 @@ "shortDescription": null, "public": false, "closed": false, - "title": "rulasg-dev-project", + "title": "rulasg-dev-project-625", "id": "PVT_kwDOAlIw4c4A0Lf4", "readme": null, "items": { "pageInfo": { - "endCursor": "Y3Vyc29yOnYyOpKrMDAwMDAwMDAuMjXOBg1PIw==", + "endCursor": "Y3Vyc29yOnYyOpK5MDAwMDAwMDAuMTExMTExMTExMTExMTExMc4Hzpqa", "hasNextPage": false }, - "totalCount": 4, + "totalCount": 12, "nodes": [ { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, "content": { "__typename": "DraftIssue", "id": "DI_lADOAlIw4c4A0Lf4zgJQEVU", "body": "", - "title": "Target Item" + "title": "draft7", + "updatedAt": "2025-09-26T19:06:31Z", + "createdAt": "2025-03-12T10:36:25Z" }, "id": "PVTI_lADOAlIw4c4A0Lf4zgYNTc0", + "fullDatabaseId": "101535181", "fieldValues": { "nodes": [ { "__typename": "ProjectV2ItemFieldTextValue", - "text": "Target Item", + "text": "draft7", "field": { "__typename": "ProjectV2Field", "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTg", @@ -39,60 +48,317 @@ }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "https://some.com/1234/viuew", + "text": "Value 1", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lpw", - "name": "sfUrl", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp6aFk", + "name": "Text1", "dataType": "TEXT" } }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Option1", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp9CRc", + "name": "IntSS", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "ae9ef0df", + "name": "Option1" + }, + { + "id": "9f685ff0", + "name": "Option2" + }, + { + "id": "e8a519ad", + "name": "Option3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 0.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp6aIw", + "name": "Int2", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-03-05", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqDG_s", + "name": "DueDate", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Done", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp2lTo", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "9893bd57", + "name": "ActionRequired" + }, + { + "id": "59a06e8d", + "name": "Overdue" + }, + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + }, + { + "id": "b057b134", + "name": "Planned" + }, + { + "id": "090b4a67", + "name": "Follow-up" + } + ] + } + }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "Text1_value_target", + "text": "Ignore as Done | (Change AR as Past)", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lxM", - "name": "sf_Text1", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", "dataType": "TEXT" } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4A0Lf4zgJQEX0", + "body": "", + "title": "draft8", + "updatedAt": "2025-09-26T19:06:36Z", + "createdAt": "2025-03-12T10:38:18Z" + }, + "id": "PVTI_lADOAlIw4c4A0Lf4zgYNTwo", + "fullDatabaseId": "101535498", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "draft8", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTg", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "9999-12-12", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqDG_s", + "name": "DueDate", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "In Progress", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp2lTo", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "9893bd57", + "name": "ActionRequired" + }, + { + "id": "59a06e8d", + "name": "Overdue" + }, + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + }, + { + "id": "b057b134", + "name": "Planned" + }, + { + "id": "090b4a67", + "name": "Follow-up" + } + ] + } }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "Text2_value", + "text": "Ignore future", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2l3o", - "name": "sf_Text2", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", "dataType": "TEXT" } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4A0Lf4zgJQEX4", + "body": "", + "title": "draft4", + "updatedAt": "2025-09-26T18:56:09Z", + "createdAt": "2025-03-12T10:38:22Z" + }, + "id": "PVTI_lADOAlIw4c4A0Lf4zgYNTxI", + "fullDatabaseId": "101535506", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "draft4", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTg", + "name": "Title", + "dataType": "TITLE" + } }, { - "__typename": "ProjectV2ItemFieldNumberValue", - "number": 666.0, + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-03-09", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2mBs", - "name": "sf_Number1", - "dataType": "NUMBER" + "id": "PVTF_lADOAlIw4c4A0Lf4zgqDG_s", + "name": "DueDate", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "In Progress", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp2lTo", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "9893bd57", + "name": "ActionRequired" + }, + { + "id": "59a06e8d", + "name": "Overdue" + }, + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + }, + { + "id": "b057b134", + "name": "Planned" + }, + { + "id": "090b4a67", + "name": "Follow-up" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "(ignore not P) | (Changed AR as Past)", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", + "dataType": "TEXT" } } ] } }, { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, "content": { "__typename": "DraftIssue", - "id": "DI_lADOAlIw4c4A0Lf4zgJQEX0", + "id": "DI_lADOAlIw4c4A0Lf4zgJQEYA", "body": "", - "title": "draft 1" + "title": "draft0", + "updatedAt": "2025-09-26T18:56:18Z", + "createdAt": "2025-03-12T10:38:30Z" }, - "id": "PVTI_lADOAlIw4c4A0Lf4zgYNTwo", + "id": "PVTI_lADOAlIw4c4A0Lf4zgYNTyM", + "fullDatabaseId": "101535523", "fieldValues": { "nodes": [ { "__typename": "ProjectV2ItemFieldTextValue", - "text": "draft 1", + "text": "draft0", "field": { "__typename": "ProjectV2Field", "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTg", @@ -100,47 +366,270 @@ "dataType": "TITLE" } }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-03-01", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqDG_s", + "name": "DueDate", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Planned", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp2lTo", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "9893bd57", + "name": "ActionRequired" + }, + { + "id": "59a06e8d", + "name": "Overdue" + }, + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + }, + { + "id": "b057b134", + "name": "Planned" + }, + { + "id": "090b4a67", + "name": "Follow-up" + } + ] + } + }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "Text1_value_1", + "text": "Change AR Past", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lxM", - "name": "sf_Text1", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", "dataType": "TEXT" } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4A0Lf4zgJRF5k", + "body": "", + "title": "draft1", + "updatedAt": "2025-09-26T18:56:50Z", + "createdAt": "2025-03-15T11:26:32Z" + }, + "id": "PVTI_lADOAlIw4c4A0Lf4zgYVsJc", + "fullDatabaseId": "102084759", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "draft1", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTg", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "9999-12-12", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqDG_s", + "name": "DueDate", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Planned", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp2lTo", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "9893bd57", + "name": "ActionRequired" + }, + { + "id": "59a06e8d", + "name": "Overdue" + }, + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + }, + { + "id": "b057b134", + "name": "Planned" + }, + { + "id": "090b4a67", + "name": "Follow-up" + } + ] + } }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "Text2_value_1", + "text": "Ignore as Future", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2l3o", - "name": "sf_Text2", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", "dataType": "TEXT" } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4A0Lf4zgJzZyY", + "body": "", + "title": "draft3", + "updatedAt": "2025-09-26T18:57:44Z", + "createdAt": "2025-09-26T16:39:41Z" + }, + "id": "PVTI_lADOAlIw4c4A0Lf4zgfNum4", + "fullDatabaseId": "130923118", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "draft3", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTg", + "name": "Title", + "dataType": "TITLE" + } }, { - "__typename": "ProjectV2ItemFieldNumberValue", - "number": 100.0, + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "ActionRequired", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp2lTo", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "9893bd57", + "name": "ActionRequired" + }, + { + "id": "59a06e8d", + "name": "Overdue" + }, + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + }, + { + "id": "b057b134", + "name": "Planned" + }, + { + "id": "090b4a67", + "name": "Follow-up" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-03-03", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2mBs", - "name": "sf_Number1", - "dataType": "NUMBER" + "id": "PVTF_lADOAlIw4c4A0Lf4zgqDG_s", + "name": "DueDate", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Ignore as Past and AR", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", + "dataType": "TEXT" } } ] } }, { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, "content": { "__typename": "DraftIssue", - "id": "DI_lADOAlIw4c4A0Lf4zgJQEX4", + "id": "DI_lADOAlIw4c4A0Lf4zgJzZy4", "body": "", - "title": "draft2" + "title": "draft2", + "updatedAt": "2025-09-26T18:57:03Z", + "createdAt": "2025-09-26T16:40:23Z" }, - "id": "PVTI_lADOAlIw4c4A0Lf4zgYNTxI", + "id": "PVTI_lADOAlIw4c4A0Lf4zgfNuvM", + "fullDatabaseId": "130923251", "fieldValues": { "nodes": [ { @@ -153,52 +642,403 @@ "dataType": "TITLE" } }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "ActionRequired", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp2lTo", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "9893bd57", + "name": "ActionRequired" + }, + { + "id": "59a06e8d", + "name": "Overdue" + }, + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + }, + { + "id": "b057b134", + "name": "Planned" + }, + { + "id": "090b4a67", + "name": "Follow-up" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "9999-12-12", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqDG_s", + "name": "DueDate", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Change to P as AR+Future", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", + "dataType": "TEXT" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, + "content": { + "__typename": "PullRequest", + "id": "PR_kwDOPrRnkc6nndcE", + "body": "", + "title": "PullRequest for development", + "updatedAt": "2025-09-24T08:56:12Z", + "createdAt": "2025-09-09T13:56:05Z", + "number": 25, + "url": "https://github.com/octodemo/rulasg-dev-1/pull/25", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4A0Lf4zgfJYvk", + "fullDatabaseId": "130638585", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rulasg" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTk", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lT4", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 1: Project Setup", + "description": "Initial project setup and basic infrastructure", + "dueOn": "2025-09-20T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lT0", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "PullRequest for development", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTg", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "PR Text1 Value", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp6aFk", + "name": "Text1", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 11.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp6aIw", + "name": "Int2", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Option1", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp9CRc", + "name": "IntSS", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "ae9ef0df", + "name": "Option1" + }, + { + "id": "9f685ff0", + "name": "Option2" + }, + { + "id": "e8a519ad", + "name": "Option3" + } + ] + } + }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "Text1_value_2", + "text": "skip no DueDate", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lxM", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "https://some.com/1234/viuew", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lpw", + "name": "sfUrl", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 1111.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zg15NKg", + "name": "sf_Int2", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "PR Sf Test1 field", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zg15NMg", "name": "sf_Text1", "dataType": "TEXT" } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KkwSq", + "body": "Body of issue for development", + "title": "Issue for development", + "updatedAt": "2025-09-24T08:29:13Z", + "createdAt": "2025-09-09T14:01:17Z", + "number": 26, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/26", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4A0Lf4zgfJYv4", + "fullDatabaseId": "130638590", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lT4", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 3: Quality and Deployment", + "description": "Testing, documentation, and deployment pipeline setup", + "dueOn": "2025-10-18T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lT0", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Issue for development", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTg", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Issue Text1 Value", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp6aFk", + "name": "Text1", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 33.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp6aIw", + "name": "Int2", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Option3", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp9CRc", + "name": "IntSS", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "ae9ef0df", + "name": "Option1" + }, + { + "id": "9f685ff0", + "name": "Option2" + }, + { + "id": "e8a519ad", + "name": "Option3" + } + ] + } }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "Text2_value_2", + "text": "skip no DueDate", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2l3o", - "name": "sf_Text2", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "https://some.com/4321/viuew", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lpw", + "name": "sfUrl", "dataType": "TEXT" } }, { "__typename": "ProjectV2ItemFieldNumberValue", - "number": 200.0, + "number": 3333.0, "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2mBs", - "name": "sf_Number1", + "id": "PVTF_lADOAlIw4c4A0Lf4zg15NKg", + "name": "sf_Int2", "dataType": "NUMBER" } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Issue sf Text1 field", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zg15NMg", + "name": "sf_Text1", + "dataType": "TEXT" + } } ] } }, { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, "content": { "__typename": "DraftIssue", - "id": "DI_lADOAlIw4c4A0Lf4zgJQEYA", + "id": "DI_lADOAlIw4c4A0Lf4zgJzau0", "body": "", - "title": "draft3" + "title": "draft5", + "updatedAt": "2025-09-26T19:06:21Z", + "createdAt": "2025-09-26T18:26:19Z" }, - "id": "PVTI_lADOAlIw4c4A0Lf4zgYNTyM", + "id": "PVTI_lADOAlIw4c4A0Lf4zgfN77A", + "fullDatabaseId": "130936752", "fieldValues": { "nodes": [ { "__typename": "ProjectV2ItemFieldTextValue", - "text": "draft3", + "text": "draft5", "field": { "__typename": "ProjectV2Field", "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTg", @@ -206,34 +1046,248 @@ "dataType": "TITLE" } }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp2lTo", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "9893bd57", + "name": "ActionRequired" + }, + { + "id": "59a06e8d", + "name": "Overdue" + }, + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + }, + { + "id": "b057b134", + "name": "Planned" + }, + { + "id": "090b4a67", + "name": "Follow-up" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-03-15", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqDG_s", + "name": "DueDate", + "dataType": "DATE" + } + }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "Text1_value_3", + "text": " (ignore not P) | (Change AR as Today)", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lxM", - "name": "sf_Text1", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", "dataType": "TEXT" } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4A0Lf4zgJzbDY", + "body": "", + "title": "draft6", + "updatedAt": "2025-09-26T19:06:28Z", + "createdAt": "2025-09-26T18:52:16Z" + }, + "id": "PVTI_lADOAlIw4c4A0Lf4zgfN-44", + "fullDatabaseId": "130939790", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "draft6", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTg", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Planned", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp2lTo", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "9893bd57", + "name": "ActionRequired" + }, + { + "id": "59a06e8d", + "name": "Overdue" + }, + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + }, + { + "id": "b057b134", + "name": "Planned" + }, + { + "id": "090b4a67", + "name": "Follow-up" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-03-15", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqDG_s", + "name": "DueDate", + "dataType": "DATE" + } }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "Text2_value_3", + "text": "Change to AR as P and Today", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2l3o", - "name": "sf_Text2", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", "dataType": "TEXT" } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4A0Lf4", + "number": 625, + "title": "rulasg-dev-project-625", + "url": "https://github.com/orgs/octodemo/projects/625" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4A0Lf4zgJzefU", + "body": "", + "title": "draft9", + "updatedAt": "2025-09-27T08:47:06Z", + "createdAt": "2025-09-27T08:47:06Z" + }, + "id": "PVTI_lADOAlIw4c4A0Lf4zgfOmpo", + "fullDatabaseId": "130980506", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "draft9", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lTg", + "name": "Title", + "dataType": "TITLE" + } }, { - "__typename": "ProjectV2ItemFieldNumberValue", - "number": 300.0, + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp2lTo", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "9893bd57", + "name": "ActionRequired" + }, + { + "id": "59a06e8d", + "name": "Overdue" + }, + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + }, + { + "id": "b057b134", + "name": "Planned" + }, + { + "id": "090b4a67", + "name": "Follow-up" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "9999-12-12", "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2mBs", - "name": "sf_Number1", - "dataType": "NUMBER" + "id": "PVTF_lADOAlIw4c4A0Lf4zgqDG_s", + "name": "DueDate", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "(Ignore not P) ", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", + "dataType": "TEXT" } } ] @@ -242,7 +1296,7 @@ ] }, "fields": { - "totalCount": 14, + "totalCount": 18, "nodes": [ { "__typename": "ProjectV2Field", @@ -262,6 +1316,14 @@ "name": "Status", "dataType": "SINGLE_SELECT", "options": [ + { + "id": "9893bd57", + "name": "ActionRequired" + }, + { + "id": "59a06e8d", + "name": "Overdue" + }, { "id": "f75ad846", "name": "Todo" @@ -273,6 +1335,14 @@ { "id": "98236657", "name": "Done" + }, + { + "id": "b057b134", + "name": "Planned" + }, + { + "id": "090b4a67", + "name": "Follow-up" } ] }, @@ -326,25 +1396,63 @@ }, { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2lxM", - "name": "sf_Text1", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp6aFk", + "name": "Text1", "dataType": "TEXT" }, { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2l3o", - "name": "sf_Text2", + "id": "PVTF_lADOAlIw4c4A0Lf4zgp6aIw", + "name": "Int2", + "dataType": "NUMBER" + }, + { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4A0Lf4zgp9CRc", + "name": "IntSS", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "ae9ef0df", + "name": "Option1" + }, + { + "id": "9f685ff0", + "name": "Option2" + }, + { + "id": "e8a519ad", + "name": "Option3" + } + ] + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqDG_s", + "name": "DueDate", + "dataType": "DATE" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zgqoEIo", + "name": "Comment", "dataType": "TEXT" }, { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0Lf4zgp2mBs", - "name": "sf_Number1", + "id": "PVTF_lADOAlIw4c4A0Lf4zg15NKg", + "name": "sf_Int2", "dataType": "NUMBER" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0Lf4zg15NMg", + "name": "sf_Text1", + "dataType": "TEXT" } ], "pageInfo": { - "endCursor": "MTQ", + "endCursor": "MTg", "hasNextPage": false } }, diff --git a/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-626.syncprj.json b/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-626.json similarity index 53% rename from Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-626.syncprj.json rename to Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-626.json index 4136347..f99241e 100644 --- a/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-626.syncprj.json +++ b/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-626.json @@ -7,73 +7,61 @@ "shortDescription": null, "public": false, "closed": false, - "title": "rulasg-dev-project-2", + "title": "rulasg-dev-project-626", "id": "PVT_kwDOAlIw4c4A0QAo", "readme": null, "items": { "pageInfo": { - "endCursor": "Y3Vyc29yOnYyOpK5MDAwMDAwMDAuMzMzMzMzMzMzMzMzMzMzM84GEKUn", + "endCursor": "Y3Vyc29yOnYyOpKqMDAwMDAwMDAuNc4HyWKq", "hasNextPage": false }, - "totalCount": 3, + "totalCount": 2, "nodes": [ { + "project": { + "id": "PVT_kwDOAlIw4c4A0QAo", + "number": 626, + "title": "rulasg-dev-project-626", + "url": "https://github.com/orgs/octodemo/projects/626" + }, "content": { - "__typename": "Issue", + "__typename": "PullRequest", + "id": "PR_kwDOPrRnkc6nndcE", "body": "", - "title": "Issue1", - "number": 1, - "url": "https://github.com/octodemo/rulasg-dev-repo/issues/1", + "title": "PullRequest for development", + "updatedAt": "2025-09-24T08:56:12Z", + "createdAt": "2025-09-09T13:56:05Z", + "number": 25, + "url": "https://github.com/octodemo/rulasg-dev-1/pull/25", + "state": "OPEN", "repository": { - "nameWithOwner": "octodemo/rulasg-dev-repo" + "nameWithOwner": "octodemo/rulasg-dev-1" } }, - "id": "PVTI_lADOAlIw4c4A0QAozgYQpP0", + "id": "PVTI_lADOAlIw4c4A0QAozgfJYqk", + "fullDatabaseId": "130638505", "fieldValues": { "nodes": [ { - "__typename": "ProjectV2ItemFieldRepositoryValue", - "repository": { - "url": "https://github.com/octodemo/rulasg-dev-repo" + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rulasg" + } + ] }, "field": { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0QAozgp6Zx0", - "name": "Repository", - "dataType": "REPOSITORY" + "id": "PVTF_lADOAlIw4c4A0QAozgp6Zxg", + "name": "Assignees", + "dataType": "ASSIGNEES" } }, - { - "__typename": "ProjectV2ItemFieldTextValue", - "text": "Issue1", - "field": { - "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0QAozgp6Zxc", - "name": "Title", - "dataType": "TITLE" - } - } - ] - } - }, - { - "content": { - "__typename": "Issue", - "body": "", - "title": "Issue2", - "number": 2, - "url": "https://github.com/octodemo/rulasg-dev-repo/issues/2", - "repository": { - "nameWithOwner": "octodemo/rulasg-dev-repo" - } - }, - "id": "PVTI_lADOAlIw4c4A0QAozgYQpRY", - "fieldValues": { - "nodes": [ { "__typename": "ProjectV2ItemFieldRepositoryValue", "repository": { - "url": "https://github.com/octodemo/rulasg-dev-repo" + "url": "https://github.com/octodemo/rulasg-dev-1" }, "field": { "__typename": "ProjectV2Field", @@ -82,9 +70,23 @@ "dataType": "REPOSITORY" } }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 1: Project Setup", + "description": "Initial project setup and basic infrastructure", + "dueOn": "2025-09-20T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0QAozgp6Zxw", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "Issue2", + "text": "PullRequest for development", "field": { "__typename": "ProjectV2Field", "id": "PVTF_lADOAlIw4c4A0QAozgp6Zxc", @@ -96,23 +98,34 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4A0QAo", + "number": 626, + "title": "rulasg-dev-project-626", + "url": "https://github.com/orgs/octodemo/projects/626" + }, "content": { "__typename": "Issue", - "body": "", - "title": "Issue2", - "number": 3, - "url": "https://github.com/octodemo/rulasg-dev-repo/issues/3", + "id": "I_kwDOPrRnkc7KkwSq", + "body": "Body of issue for development", + "title": "Issue for development", + "updatedAt": "2025-09-24T08:29:13Z", + "createdAt": "2025-09-09T14:01:17Z", + "number": 26, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/26", + "state": "OPEN", "repository": { - "nameWithOwner": "octodemo/rulasg-dev-repo" + "nameWithOwner": "octodemo/rulasg-dev-1" } }, - "id": "PVTI_lADOAlIw4c4A0QAozgYQpSc", + "id": "PVTI_lADOAlIw4c4A0QAozgfJYqo", + "fullDatabaseId": "130638506", "fieldValues": { "nodes": [ { "__typename": "ProjectV2ItemFieldRepositoryValue", "repository": { - "url": "https://github.com/octodemo/rulasg-dev-repo" + "url": "https://github.com/octodemo/rulasg-dev-1" }, "field": { "__typename": "ProjectV2Field", @@ -121,9 +134,23 @@ "dataType": "REPOSITORY" } }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 3: Quality and Deployment", + "description": "Testing, documentation, and deployment pipeline setup", + "dueOn": "2025-10-18T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0QAozgp6Zxw", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "Issue3", + "text": "Issue for development", "field": { "__typename": "ProjectV2Field", "id": "PVTF_lADOAlIw4c4A0QAozgp6Zxc", @@ -137,7 +164,7 @@ ] }, "fields": { - "totalCount": 12, + "totalCount": 20, "nodes": [ { "__typename": "ProjectV2Field", @@ -215,19 +242,67 @@ }, { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0QAozgp6aGw", - "name": "Int1", + "id": "PVTF_lADOAlIw4c4A0QAozgqoeOo", + "name": "pr1_Text1", "dataType": "TEXT" }, { "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4A0QAozgp6aK4", - "name": "Int2", + "id": "PVTF_lADOAlIw4c4A0QAozgqofEM", + "name": "pr1_Int2", "dataType": "NUMBER" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0QAozgqohhs", + "name": "Id", + "dataType": "TEXT" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0QAozgqonbw", + "name": "csv_Column2", + "dataType": "TEXT" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0QAozgqooJw", + "name": "csv_Column1", + "dataType": "TEXT" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0QAozgqooNM", + "name": "csv_Column3", + "dataType": "TEXT" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0QAozgqo0EI", + "name": "sf_Industry", + "dataType": "TEXT" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0QAozgqo0K8", + "name": "SfUrl", + "dataType": "TEXT" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0QAozgqwxks", + "name": "sf_Country_Name__c", + "dataType": "TEXT" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4A0QAozgqw6M8", + "name": "sf_Account_Segment__c", + "dataType": "TEXT" } ], "pageInfo": { - "endCursor": "MTI", + "endCursor": "MjA", "hasNextPage": false } }, diff --git a/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-700-caseSensitive.json b/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-700-caseSensitive.json new file mode 100644 index 0000000..98bddb6 --- /dev/null +++ b/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-700-caseSensitive.json @@ -0,0 +1,4188 @@ +{ + "data": { + "organization": { + "projectV2": { + "number": 700, + "url": "https://github.com/orgs/octodemo/projects/700", + "shortDescription": "Project used as development environment by rulasg", + "public": false, + "closed": false, + "title": "rulasg-dev-700", + "id": "PVT_kwDOAlIw4c4BCe3V", + "readme": null, + "items": { + "pageInfo": { + "endCursor": "Y3Vyc29yOnYyOpK5MDAwMDAwMDAuMDMzMzMzMzMzMzMzMzMzM84HwRmY", + "hasNextPage": false + }, + "totalCount": 28, + "nodes": [ + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJA58", + "body": "## Description\nSet up continuous integration and continuous deployment pipeline for the Web API project.\n\n## Tasks\n- [ ] Create GitHub Actions workflow files\n- [ ] Configure build process\n- [ ] Set up test execution in CI\n- [ ] Configure code quality checks (linting, formatting)\n- [ ] Set up security scanning\n- [ ] Configure deployment to development/staging environments\n- [ ] Add status badges to README\n\n## Acceptance Criteria\n- CI pipeline runs on each push and pull request\n- Tests are executed automatically\n- Code quality checks are enforced\n- Secure deployments to target environments\n- Pipeline results are visible in GitHub", + "title": "Configure CI/CD pipeline", + "updatedAt": "2025-09-07T10:22:16Z", + "createdAt": "2025-09-07T08:27:29Z", + "number": 13, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/13", + "state": "CLOSED", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pU", + "fullDatabaseId": "127726229", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "devops" + }, + { + "name": "automation" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 3: Quality and Deployment", + "description": "Testing, documentation, and deployment pipeline setup", + "dueOn": "2025-10-18T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Configure CI/CD pipeline", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 76.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-24", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Done", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 1", + "startDate": "2025-09-07", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJA25", + "body": "## Description\nConfigure unit, integration, and API testing infrastructure for the Web API project.\n\n## Tasks\n- [ ] Set up test project with appropriate framework (xUnit, NUnit, MSTest)\n- [ ] Configure mocking library (Moq, NSubstitute)\n- [ ] Create base classes for different test types\n- [ ] Set up test database for integration tests\n- [ ] Implement API endpoint tests\n- [ ] Configure code coverage reporting\n- [ ] Set up continuous testing in CI/CD\n\n## Acceptance Criteria\n- Unit tests for service and business logic\n- Integration tests for database operations\n- API tests for endpoint functionality\n- Test configuration is environment-independent\n- Code coverage reports are generated", + "title": "Set up testing infrastructure", + "updatedAt": "2025-09-07T09:46:59Z", + "createdAt": "2025-09-07T08:27:12Z", + "number": 12, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/12", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pu", + "fullDatabaseId": "127726230", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "testing" + }, + { + "name": "quality" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 3: Quality and Deployment", + "description": "Testing, documentation, and deployment pipeline setup", + "dueOn": "2025-10-18T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Set up testing infrastructure", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 66.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-3", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text1", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-27", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 1", + "startDate": "2025-09-07", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJA0S", + "body": "## Description\nSet up comprehensive logging and error handling mechanisms for the Web API.\n\n## Tasks\n- [ ] Configure logging provider (Serilog, NLog, etc.)\n- [ ] Implement global exception handling middleware\n- [ ] Create custom exception types for domain-specific errors\n- [ ] Set up structured logging with appropriate log levels\n- [ ] Configure log storage (file, database, cloud service)\n- [ ] Implement error response standardization\n\n## Acceptance Criteria\n- All exceptions are properly caught and handled\n- Error responses follow a consistent format\n- Logging provides adequate information for troubleshooting\n- Different environments have appropriate logging levels\n- Sensitive data is not logged", + "title": "Implement logging and error handling", + "updatedAt": "2025-09-07T09:46:58Z", + "createdAt": "2025-09-07T08:26:59Z", + "number": 11, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/11", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pc", + "fullDatabaseId": "127726231", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "setup" + }, + { + "name": "maintenance" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 3: Quality and Deployment", + "description": "Testing, documentation, and deployment pipeline setup", + "dueOn": "2025-10-18T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Implement logging and error handling", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 96.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text3", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-09", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 1", + "startDate": "2025-09-07", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJAyT", + "body": "## Description\nImplement and configure Swagger/OpenAPI documentation for the Web API to provide interactive API documentation.\n\n## Tasks\n- [ ] Install Swashbuckle.AspNetCore NuGet package\n- [ ] Configure Swagger generation in Program.cs/Startup.cs\n- [ ] Add XML comments support for documentation\n- [ ] Customize Swagger UI with API information\n- [ ] Add security definitions for authentication\n- [ ] Configure Swagger document versions\n\n## Acceptance Criteria\n- Swagger UI accessible at /swagger endpoint\n- API operations are properly documented\n- Authentication is integrated with Swagger UI\n- Models have proper descriptions\n- Swagger page is customized with API info", + "title": "Configure Swagger/OpenAPI documentation", + "updatedAt": "2025-09-07T09:47:29Z", + "createdAt": "2025-09-07T08:26:47Z", + "number": 10, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/10", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pg", + "fullDatabaseId": "127726232", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rauldibildos" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "documentation" + }, + { + "name": "setup" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 1: Project Setup", + "description": "Initial project setup and basic infrastructure", + "dueOn": "2025-09-20T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Configure Swagger/OpenAPI documentation", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 29.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-04", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "In Progress", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 3", + "startDate": "2025-10-05", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJAvo", + "body": "## Description\nSet up authentication and authorization mechanisms for the Web API using JWT, OAuth, or Identity.\n\n## Tasks\n- [ ] Configure authentication middleware\n- [ ] Implement JWT token generation and validation\n- [ ] Set up authorization policies\n- [ ] Add role-based access control\n- [ ] Secure endpoints with appropriate attributes\n- [ ] Create user authentication endpoints (login, register)\n\n## Acceptance Criteria\n- Authentication works for protected endpoints\n- JWT tokens are properly generated and validated\n- Role-based permissions are enforced\n- User identity is accessible in controllers\n- Auth configuration follows security best practices", + "title": "Implement API authentication and authorization", + "updatedAt": "2025-09-07T10:25:18Z", + "createdAt": "2025-09-07T08:26:31Z", + "number": 9, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/9", + "state": "CLOSED", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pk", + "fullDatabaseId": "127726233", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rauldibildos" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "high-priority" + }, + { + "name": "security" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 2: Core Functionality", + "description": "Implementation of core API features and data handling", + "dueOn": "2025-10-04T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Implement API authentication and authorization", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 14.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-11", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Done", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 3", + "startDate": "2025-10-05", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJAtI", + "body": "## Description\nConfigure database access using Entity Framework Core as the ORM (Object-Relational Mapping) tool.\n\n## Tasks\n- [ ] Install required EF Core NuGet packages\n- [ ] Create DbContext class with entity configurations\n- [ ] Configure connection string in appsettings.json\n- [ ] Set up migrations for database versioning\n- [ ] Implement repository pattern for data access\n- [ ] Configure database provider (SQL Server, PostgreSQL, etc.)\n\n## Acceptance Criteria\n- Database connection works correctly\n- Entity configurations follow best practices\n- Migration system is set up properly\n- Data access follows repository pattern\n- Connection strings are properly secured", + "title": "Set up database integration with Entity Framework Core", + "updatedAt": "2025-09-07T09:46:43Z", + "createdAt": "2025-09-07T08:26:17Z", + "number": 8, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/8", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8po", + "fullDatabaseId": "127726234", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rulasg" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "setup" + }, + { + "name": "high-priority" + }, + { + "name": "database" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 2: Core Functionality", + "description": "Implementation of core API features and data handling", + "dueOn": "2025-10-04T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Set up database integration with Entity Framework Core", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 34.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-10-05", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 3", + "startDate": "2025-10-05", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJAq7", + "body": "## Description\nSet up Dependency Injection (DI) to handle service registration and lifecycle management for the API.\n\n## Tasks\n- [ ] Configure built-in DI container in Program.cs/Startup.cs\n- [ ] Register services with appropriate lifetimes (Singleton, Scoped, Transient)\n- [ ] Implement service interfaces and their implementations\n- [ ] Set up options pattern for configuration objects\n- [ ] Document service dependencies\n\n## Acceptance Criteria\n- All services properly registered in DI container\n- Services have appropriate lifetime scopes\n- Service interfaces follow SOLID principles\n- Dependencies are properly injected into controllers", + "title": "Configure Dependency Injection and services", + "updatedAt": "2025-09-07T09:46:24Z", + "createdAt": "2025-09-07T08:26:06Z", + "number": 7, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/7", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8ps", + "fullDatabaseId": "127726235", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rauldibildos" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "setup" + }, + { + "name": "architecture" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 1: Project Setup", + "description": "Initial project setup and basic infrastructure", + "dueOn": "2025-09-20T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Configure Dependency Injection and services", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text1", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-05", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 3", + "startDate": "2025-10-05", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJApV", + "body": "## Description\nCreate data models and DTOs (Data Transfer Objects) to represent resources in the API.\n\n## Tasks\n- [ ] Define domain models representing business entities\n- [ ] Create DTOs for request/response objects\n- [ ] Implement validation attributes for data models\n- [ ] Add mapping between domain models and DTOs\n- [ ] Document model properties with appropriate comments\n\n## Acceptance Criteria\n- Clear separation between domain models and DTOs\n- Models have appropriate validation rules\n- Automated mapping between models is implemented\n- Models follow C# naming conventions and best practices", + "title": "Define and implement data models", + "updatedAt": "2025-09-07T09:46:42Z", + "createdAt": "2025-09-07T08:25:56Z", + "number": 6, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/6", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pw", + "fullDatabaseId": "127726236", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rulasg" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "feature" + }, + { + "name": "data-modeling" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 2: Core Functionality", + "description": "Implementation of core API features and data handling", + "dueOn": "2025-10-04T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Define and implement data models", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-3", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-08-29", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "In Progress", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJAm8", + "body": "## Description\nCreate controllers and endpoints for the Web API to handle HTTP requests.\n\n## Tasks\n- [ ] Create a base controller class with common functionality\n- [ ] Implement CRUD controllers for main resources\n- [ ] Add appropriate routing attributes\n- [ ] Implement proper HTTP status code responses\n- [ ] Document endpoints using XML comments for Swagger/OpenAPI\n\n## Acceptance Criteria\n- Controllers properly handle GET, POST, PUT, DELETE operations\n- Routes follow RESTful principles\n- Appropriate HTTP status codes are returned\n- Controller methods have XML documentation", + "title": "Implement API endpoints and controllers", + "updatedAt": "2025-09-07T09:46:41Z", + "createdAt": "2025-09-07T08:25:44Z", + "number": 5, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/5", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8p0", + "fullDatabaseId": "127726237", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rauldibildos" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "feature" + }, + { + "name": "api-design" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 2: Core Functionality", + "description": "Implementation of core API features and data handling", + "dueOn": "2025-10-04T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Implement API endpoints and controllers", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-1", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text1", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-29", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJAku", + "body": "## Description\nCreate the initial project structure for a .NET Web API application.\n\n## Tasks\n- [ ] Create a new .NET Web API project using `dotnet new webapi`\n- [ ] Set up proper folder structure (Controllers, Models, Services, etc.)\n- [ ] Configure project properties and settings\n- [ ] Add solution file for better organization\n- [ ] Document project structure\n\n## Acceptance Criteria\n- Project builds successfully\n- Basic folder structure is established\n- Solution file includes the API project", + "title": "Set up .NET Web API project structure", + "updatedAt": "2025-09-07T09:46:23Z", + "createdAt": "2025-09-07T08:25:30Z", + "number": 4, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/4", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8p8", + "fullDatabaseId": "127726239", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rulasg" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "setup" + }, + { + "name": "high-priority" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 1: Project Setup", + "description": "Initial project setup and basic infrastructure", + "dueOn": "2025-09-20T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Set up .NET Web API project structure", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-26", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "PullRequest", + "id": "PR_kwDOPrRnkc6nPf2s", + "body": "This PR implements a complete web API setup for the project, transforming an empty repository into a fully functional RESTful API service.\n\n## What was implemented\n\n**Core API Infrastructure:**\n- Express.js server with proper middleware configuration\n- CORS support for cross-origin requests\n- Environment-based configuration with `.env` files\n- Request logging and comprehensive error handling\n- JSON request/response handling with standardized formats\n\n**API Endpoints:**\n- `GET /health` - Health check endpoint for monitoring\n- `GET /api` - API information and endpoint documentation\n- Complete user management CRUD operations:\n - `GET /api/users` - List all users\n - `GET /api/users/:id` - Get specific user\n - `POST /api/users` - Create new user\n - `PUT /api/users/:id` - Update existing user\n - `DELETE /api/users/:id` - Delete user\n\n**Developer Experience:**\n- Comprehensive documentation in `README.md`\n- API usage examples in `API_EXAMPLES.md` with curl, JavaScript, and Python samples\n- Automated setup script (`setup.sh`) for quick development environment setup\n- Proper `.gitignore` for Node.js projects\n- Environment configuration templates\n\n## Key Features\n\n**Response Format Standardization:**\nAll API responses follow a consistent format for both success and error cases:\n```json\n{\n \"success\": true,\n \"data\": {...},\n \"message\": \"Optional message\"\n}\n```\n\n**Error Handling:**\nComprehensive error handling with appropriate HTTP status codes and descriptive error messages.\n\n**Security:**\n- All dependencies verified against GitHub Advisory Database (no vulnerabilities)\n- CodeQL security analysis passed\n- Input validation on all endpoints\n- Environment variable configuration for sensitive data\n\n## Testing\n\nThe API has been thoroughly tested with all endpoints verified to work correctly:\n\n![API Response](https://github.com/user-attachments/assets/6c2b0c37-3ec0-4d62-ad71-44335d0f567b)\n\n## Quick Start\n\nAfter merging, developers can get started immediately:\n```bash\n./setup.sh # Automated setup\nnpm run dev # Start development server\n```\n\nThe API will be available at `http://localhost:3000` with full documentation accessible via the `/api` endpoint.\n\n## Future Ready\n\nThe implementation provides a solid foundation that can be easily extended with:\n- Database integration (structure already supports it)\n- Authentication and authorization\n- Additional business logic endpoints\n- API versioning\n- Rate limiting and caching\n\nThis setup follows best practices and provides everything needed to begin API development immediately.\n\n\n\n\n\n\n---\n\n✨ Let Copilot coding agent [set things up for you](https://github.com/octodemo/rulasg-dev-1/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.\n", + "title": "Create comprehensive web API setup with Node.js and Express", + "updatedAt": "2025-09-07T08:09:00Z", + "createdAt": "2025-09-07T07:55:41Z", + "number": 1, + "url": "https://github.com/octodemo/rulasg-dev-1/pull/1", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8qA", + "fullDatabaseId": "127726240", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rulasg" + }, + { + "login": "Copilot" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldReviewerValue", + "reviewers": { + "nodes": [ + { + "__typename": "User", + "login": "rulasg" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-s", + "name": "Reviewers", + "dataType": "REVIEWERS" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Create comprehensive web API setup with Node.js and Express", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 31.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-13", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "PullRequest", + "id": "PR_kwDOPrRnkc6nPgQb", + "body": "This PR addresses the requirement to create 10 issues that describe the tasks needed to initialize the development of a web API on .NET. Since direct GitHub issue creation is not possible through automation, this solution provides comprehensive documentation that can be used to manually create the required issues.\n\n## What's Added\n\n### 📋 Four comprehensive documentation files:\n\n1. **`DEVELOPMENT_TASKS.md`** - A detailed 13,400+ character guide containing:\n - 10 structured development tasks with full descriptions\n - Clear acceptance criteria for each task\n - Implementation guidance with code examples\n - Technical considerations and best practices\n - Estimated effort and priority levels\n\n2. **`GITHUB_ISSUES_TEMPLATES.md`** - Ready-to-use templates for creating GitHub issues:\n - Complete issue templates with titles, labels, and descriptions\n - Proper dependency tracking between issues\n - Suggested labels and priorities for project management\n\n3. **`README_SUMMARY.md`** - Quick reference guide with:\n - Overview of all 10 tasks organized by phases\n - Total effort estimation (35-50 hours)\n - Getting started instructions\n - Key benefits of the structured approach\n\n4. **Updated `README.md`** - Enhanced main README with links to all documentation\n\n## 🎯 10 Essential Tasks Covered\n\nThe tasks are organized into three phases for systematic implementation:\n\n**Phase 1: Foundation (High Priority)**\n- Initialize .NET Web API Project Structure\n- Configure Basic API Framework and Middleware\n- Implement Configuration Management System\n\n**Phase 2: Core Features (Medium-High Priority)**\n- Set Up Database Integration and Entity Framework\n- Implement Authentication and Authorization\n- Configure Logging and Monitoring\n\n**Phase 3: Quality & Deployment (Medium Priority)**\n- Create Comprehensive Testing Infrastructure\n- Generate API Documentation with Swagger/OpenAPI\n- Set Up CI/CD Pipeline\n- Configure Deployment and Infrastructure\n\n## 🚀 Key Features\n\n- **Production-Ready**: Covers all aspects from development to deployment\n- **Best Practices**: Incorporates modern .NET development standards\n- **Dependency Management**: Clear task dependencies and implementation order\n- **Code Examples**: Practical implementation snippets for each task\n- **Comprehensive Coverage**: Includes security, testing, monitoring, and documentation\n\n## 📈 Usage\n\n1. Review the detailed tasks in `DEVELOPMENT_TASKS.md`\n2. Use templates from `GITHUB_ISSUES_TEMPLATES.md` to create actual GitHub issues\n3. Follow the phased approach starting with foundation tasks\n4. Reference the summary guide for quick navigation\n\nThis documentation provides a complete roadmap for building a professional-grade .NET Web API from initial setup to production deployment, ensuring nothing is overlooked in the development process.\n\n\n\n\n\n\n---\n\n💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.", + "title": "Create comprehensive .NET Web API development initialization documentation with 10 structured tasks", + "updatedAt": "2025-09-07T08:05:58Z", + "createdAt": "2025-09-07T07:58:55Z", + "number": 3, + "url": "https://github.com/octodemo/rulasg-dev-1/pull/3", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8qE", + "fullDatabaseId": "127726241", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rulasg" + }, + { + "login": "Copilot" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldReviewerValue", + "reviewers": { + "nodes": [ + { + "__typename": "User", + "login": "rulasg" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-s", + "name": "Reviewers", + "dataType": "REVIEWERS" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Create comprehensive .NET Web API development initialization documentation with 10 structured tasks", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 99.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text3", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-17", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KI7jH", + "body": "Add comprehensive error handling to the web API, ensuring consistent error responses and logging.", + "title": "Implement error handling", + "updatedAt": "2025-09-07T07:57:21Z", + "createdAt": "2025-09-07T07:57:21Z", + "number": 2, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/2", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgec8qI", + "fullDatabaseId": "127726242", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "enhancement" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Implement error handling", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 94.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-06", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4BCe3VzgJwMLs", + "body": "Draft plan for comprehensive API documentation strategy including developer docs, sample code, tutorials, and integration guides.", + "title": "API Documentation Strategy", + "updatedAt": "2025-09-07T10:06:58Z", + "createdAt": "2025-09-07T10:06:58Z" + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgedAHE", + "fullDatabaseId": "127729777", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "API Documentation Strategy", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 40.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text3", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-16", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Done", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4BCe3VzgJwML8", + "body": "Draft plan for optimizing API performance including response time improvements, database query optimization, caching strategy implementation, and resource usage monitoring.", + "title": "API Performance Optimization Plan", + "updatedAt": "2025-09-07T10:07:11Z", + "createdAt": "2025-09-07T10:07:11Z" + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgedAHc", + "fullDatabaseId": "127729783", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "API Performance Optimization Plan", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 38.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text3", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-10-01", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "In Progress", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4BCe3VzgJwMMA", + "body": "Draft roadmap for implementing comprehensive API security including authentication mechanisms review, authorization strategy, data encryption approach, API key management, and security monitoring.", + "title": "Security Implementation Roadmap", + "updatedAt": "2025-09-07T10:07:24Z", + "createdAt": "2025-09-07T10:07:24Z" + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgedAIE", + "fullDatabaseId": "127729793", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Security Implementation Roadmap", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 84.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-3", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text1", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-11", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "In Progress", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4BCe3VzgJwMME", + "body": "Draft plan for containerizing the API and setting up deployment infrastructure including Docker containerization, Kubernetes orchestration, and CI/CD pipeline configuration.", + "title": "Containerization and Deployment Strategy", + "updatedAt": "2025-09-07T10:07:40Z", + "createdAt": "2025-09-07T10:07:40Z" + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgedAIc", + "fullDatabaseId": "127729799", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Containerization and Deployment Strategy", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-1", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text1", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-07", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Done", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4BCe3VzgJwMMM", + "body": "Draft plan for implementing analytics and comprehensive monitoring for the API including usage metrics, performance tracking, error monitoring, and alerting systems.", + "title": "API Analytics and Monitoring Plan", + "updatedAt": "2025-09-07T10:07:56Z", + "createdAt": "2025-09-07T10:07:56Z" + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgedAJI", + "fullDatabaseId": "127729810", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "API Analytics and Monitoring Plan", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 76.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text3", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-10-06", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "In Progress", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJQwB", + "body": "## Description\nImplement health checks and monitoring for the Web API to ensure system reliability and performance.\n\n## Tasks\n- [ ] Add health check endpoints with ASP.NET Core Health Checks\n- [ ] Configure health check UI for visual monitoring\n- [ ] Implement custom health checks for database, external services, etc.\n- [ ] Set up reporting and alerting mechanisms\n- [ ] Configure metrics collection (e.g., request duration, error rates)\n- [ ] Document health check endpoints and monitoring procedures\n\n## Acceptance Criteria\n- Health check endpoints respond with appropriate status codes\n- Custom health checks verify critical dependencies\n- Health check UI provides overview of system health\n- Metrics are collected for performance monitoring\n- Alerting is configured for critical health issues", + "title": "Implement health checks and monitoring", + "updatedAt": "2025-09-07T10:24:47Z", + "createdAt": "2025-09-07T09:49:08Z", + "number": 14, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/14", + "state": "CLOSED", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgedAUw", + "fullDatabaseId": "127729996", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "maintenance" + }, + { + "name": "devops" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 2: Core Functionality", + "description": "Implementation of core API features and data handling", + "dueOn": "2025-10-04T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Implement health checks and monitoring", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 40.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-08-31", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Done", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJQyo", + "body": "## Description\nImplement a versioning strategy for the API to support backward compatibility and future changes.\n\n## Tasks\n- [ ] Research versioning strategies (URL path, query string, header, media type)\n- [ ] Implement chosen versioning approach using ASP.NET Core API Versioning\n- [ ] Set up version-specific routes and controllers\n- [ ] Configure Swagger to support multiple API versions\n- [ ] Create documentation for API version lifecycle policy\n- [ ] Implement version deprecation notifications\n\n## Acceptance Criteria\n- API supports multiple versions simultaneously\n- Versioning information is clearly communicated in API responses\n- Swagger UI displays different versions properly\n- Version lifecycle policy is documented\n- API consumers are notified of deprecated versions", + "title": "Implement API versioning strategy", + "updatedAt": "2025-09-07T10:24:55Z", + "createdAt": "2025-09-07T09:49:20Z", + "number": 15, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/15", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgedAVU", + "fullDatabaseId": "127730005", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "feature" + }, + { + "name": "api-design" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 2: Core Functionality", + "description": "Implementation of core API features and data handling", + "dueOn": "2025-10-04T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Implement API versioning strategy", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 87.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-29", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJQ0r", + "body": "## Description\nImplement rate limiting and throttling for the API to prevent abuse and ensure fair usage.\n\n## Tasks\n- [ ] Add rate limiting middleware (e.g., AspNetCoreRateLimit)\n- [ ] Configure client-based rate limits\n- [ ] Implement endpoint-specific throttling rules\n- [ ] Set up IP-based rate limiting\n- [ ] Add rate limit information to response headers\n- [ ] Configure response handling for rate-limited requests\n- [ ] Document rate limiting policy for API consumers\n\n## Acceptance Criteria\n- Rate limiting prevents excessive requests from single clients\n- Different limits can be applied based on client identity\n- Rate limit headers are included in API responses\n- Appropriate status codes (429 Too Many Requests) are returned\n- Rate limiting policy is clearly documented", + "title": "Configure rate limiting and throttling", + "updatedAt": "2025-09-07T10:24:55Z", + "createdAt": "2025-09-07T09:49:32Z", + "number": 16, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/16", + "state": "CLOSED", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgedAWU", + "fullDatabaseId": "127730021", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "api-design" + }, + { + "name": "security" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 2: Core Functionality", + "description": "Implementation of core API features and data handling", + "dueOn": "2025-10-04T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Configure rate limiting and throttling", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 77.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text1", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-21", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Done", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJQ3G", + "body": "## Description\nSet up a background processing system with message queues for handling long-running or resource-intensive tasks asynchronously.\n\n## Tasks\n- [ ] Select and configure message broker (RabbitMQ, Azure Service Bus, etc.)\n- [ ] Implement worker service with .NET Worker Service template\n- [ ] Create message producers in API controllers\n- [ ] Implement message consumers in background service\n- [ ] Set up retry policies and dead letter queues\n- [ ] Configure monitoring and logging for background jobs\n- [ ] Implement job status tracking and reporting\n\n## Acceptance Criteria\n- Long-running operations are processed asynchronously\n- API can queue tasks for background processing\n- Failed jobs are properly handled with retries\n- Background processing status can be queried\n- System is resilient to message broker outages", + "title": "Implement background processing with message queue", + "updatedAt": "2025-09-07T10:25:01Z", + "createdAt": "2025-09-07T09:49:44Z", + "number": 17, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/17", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgedAWs", + "fullDatabaseId": "127730027", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "feature" + }, + { + "name": "architecture" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 3: Quality and Deployment", + "description": "Testing, documentation, and deployment pipeline setup", + "dueOn": "2025-10-18T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Implement background processing with message queue", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 25.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-3", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-10", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KJRC1", + "body": "## Description\nDesign and implement a comprehensive caching strategy for the API to improve performance and reduce database load.\n\n## Tasks\n- [ ] Configure in-memory caching for development environment\n- [ ] Set up distributed caching (Redis, SQL Server, etc.) for production\n- [ ] Implement cache profiles for controller actions\n- [ ] Add cache headers for HTTP caching (ETags, Cache-Control)\n- [ ] Create cache invalidation strategy for data updates\n- [ ] Implement response compression\n- [ ] Document caching behavior for API consumers\n\n## Acceptance Criteria\n- Frequently accessed data is properly cached\n- Cache invalidation occurs when data is modified\n- Response headers include appropriate caching directives\n- Cache-related configuration is environment-specific\n- Performance improvements are measurable under load testing", + "title": "Implement caching strategy", + "updatedAt": "2025-09-07T09:50:34Z", + "createdAt": "2025-09-07T09:50:34Z", + "number": 18, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/18", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgedAwI", + "fullDatabaseId": "127730434", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "feature" + }, + { + "name": "performance" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Implement caching strategy", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "PullRequest", + "id": "PR_kwDOPrRnkc6nndcE", + "body": "", + "title": "PullRequest for development", + "updatedAt": "2025-09-11T14:35:05Z", + "createdAt": "2025-09-09T13:56:05Z", + "number": 25, + "url": "https://github.com/octodemo/rulasg-dev-1/pull/25", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgeioBY", + "fullDatabaseId": "128098326", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rulasg" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 1: Project Setup", + "description": "Initial project setup and basic infrastructure", + "dueOn": "2025-09-20T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "PullRequest for development", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 111.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text1", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-1", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 1", + "startDate": "2025-09-07", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-01", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4BCe3VzgJwmkk", + "body": "", + "title": "DraftIssue for development", + "updatedAt": "2025-09-11T15:14:28Z", + "createdAt": "2025-09-09T13:59:21Z" + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgeiodc", + "fullDatabaseId": "128098775", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "DraftIssue for development", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "In Progress", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 222.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOPrRnkc7KkwSq", + "body": "Body of issue for development", + "title": "Issue for development", + "updatedAt": "2025-09-11T13:06:24Z", + "createdAt": "2025-09-09T14:01:17Z", + "number": 26, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/26", + "state": "OPEN", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgeio4o", + "fullDatabaseId": "128099210", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 3: Quality and Deployment", + "description": "Testing, documentation, and deployment pipeline setup", + "dueOn": "2025-10-18T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Issue for development", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 333.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text3", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-3", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 3", + "startDate": "2025-10-05", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4BCe3VzgJymYM", + "body": "", + "title": "Issue to find", + "updatedAt": "2025-09-22T14:23:38Z", + "createdAt": "2025-09-22T14:23:38Z" + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgfBGX8", + "fullDatabaseId": "130095487", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Issue to find", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4BCe3VzgJymYQ", + "body": "", + "title": "ISSUE to FIND", + "updatedAt": "2025-09-22T14:29:30Z", + "createdAt": "2025-09-22T14:23:41Z" + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgfBGZg", + "fullDatabaseId": "130095512", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "ISSUE to FIND", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + } + ] + } + } + ] + }, + "fields": { + "totalCount": 15, + "nodes": [ + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + }, + { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Y", + "name": "Labels", + "dataType": "LABELS" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-c", + "name": "Linked pull requests", + "dataType": "LINKED_PULL_REQUESTS" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-s", + "name": "Reviewers", + "dataType": "REVIEWERS" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-w", + "name": "Parent issue", + "dataType": "PARENT_ISSUE" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-0", + "name": "Sub-issues progress", + "dataType": "SUB_ISSUES_PROGRESS" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + }, + { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + }, + { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + }, + { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + ], + "pageInfo": { + "endCursor": "MTU", + "hasNextPage": false + } + }, + "owner": { + "__typename": "Organization", + "login": "octodemo" + } + } + } + } +} diff --git a/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-700-skipitems.json b/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-700-skipitems.json index 72b037f..3c3a230 100644 --- a/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-700-skipitems.json +++ b/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-700-skipitems.json @@ -15,7 +15,7 @@ "endCursor": null, "hasNextPage": true }, - "totalCount": 27, + "totalCount": 28, "nodes": [] }, "fields": { diff --git a/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-700.json b/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-700.json index 18ba814..6d9c66e 100644 --- a/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-700.json +++ b/Test/private/mocks/invoke-GitHubOrgProjectWithFields-octodemo-700.json @@ -12,12 +12,18 @@ "readme": null, "items": { "pageInfo": { - "endCursor": "Y3Vyc29yOnYyOpK5MDAwMDAwMDAuMDM1NzE0Mjg1NzE0Mjg1N84HoqOK", + "endCursor": "Y3Vyc29yOnYyOpK5MDAwMDAwMDAuMDMzMzMzMzMzMzMzMzMzM84HwRmY", "hasNextPage": false }, - "totalCount": 27, + "totalCount": 28, "nodes": [ { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJA58", @@ -33,6 +39,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pU", + "fullDatabaseId": "127726229", "fieldValues": { "nodes": [ { @@ -184,6 +191,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJA25", @@ -199,6 +212,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pY", + "fullDatabaseId": "127726230", "fieldValues": { "nodes": [ { @@ -350,6 +364,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJA0S", @@ -365,6 +385,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pc", + "fullDatabaseId": "127726231", "fieldValues": { "nodes": [ { @@ -516,6 +537,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJAyT", @@ -531,6 +558,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pg", + "fullDatabaseId": "127726232", "fieldValues": { "nodes": [ { @@ -698,6 +726,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJAvo", @@ -713,6 +747,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pk", + "fullDatabaseId": "127726233", "fieldValues": { "nodes": [ { @@ -880,6 +915,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJAtI", @@ -895,6 +936,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8po", + "fullDatabaseId": "127726234", "fieldValues": { "nodes": [ { @@ -1065,6 +1107,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJAq7", @@ -1080,6 +1128,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8ps", + "fullDatabaseId": "127726235", "fieldValues": { "nodes": [ { @@ -1237,6 +1286,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJApV", @@ -1252,6 +1307,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8pw", + "fullDatabaseId": "127726236", "fieldValues": { "nodes": [ { @@ -1409,6 +1465,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJAm8", @@ -1424,6 +1486,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8p0", + "fullDatabaseId": "127726237", "fieldValues": { "nodes": [ { @@ -1581,6 +1644,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJAku", @@ -1596,6 +1665,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8p8", + "fullDatabaseId": "127726239", "fieldValues": { "nodes": [ { @@ -1753,6 +1823,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "PullRequest", "id": "PR_kwDOPrRnkc6nPf2s", @@ -1768,6 +1844,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8qA", + "fullDatabaseId": "127726240", "fieldValues": { "nodes": [ { @@ -1922,6 +1999,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "PullRequest", "id": "PR_kwDOPrRnkc6nPgQb", @@ -1937,6 +2020,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8qE", + "fullDatabaseId": "127726241", "fieldValues": { "nodes": [ { @@ -2067,6 +2151,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KI7jH", @@ -2082,6 +2172,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgec8qI", + "fullDatabaseId": "127726242", "fieldValues": { "nodes": [ { @@ -2180,6 +2271,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "DraftIssue", "id": "DI_lADOAlIw4c4BCe3VzgJwMLs", @@ -2189,6 +2286,7 @@ "createdAt": "2025-09-07T10:06:58Z" }, "id": "PVTI_lADOAlIw4c4BCe3VzgedAHE", + "fullDatabaseId": "127729777", "fieldValues": { "nodes": [ { @@ -2259,6 +2357,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "DraftIssue", "id": "DI_lADOAlIw4c4BCe3VzgJwML8", @@ -2268,6 +2372,7 @@ "createdAt": "2025-09-07T10:07:11Z" }, "id": "PVTI_lADOAlIw4c4BCe3VzgedAHc", + "fullDatabaseId": "127729783", "fieldValues": { "nodes": [ { @@ -2338,6 +2443,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "DraftIssue", "id": "DI_lADOAlIw4c4BCe3VzgJwMMA", @@ -2347,6 +2458,7 @@ "createdAt": "2025-09-07T10:07:24Z" }, "id": "PVTI_lADOAlIw4c4BCe3VzgedAIE", + "fullDatabaseId": "127729793", "fieldValues": { "nodes": [ { @@ -2453,6 +2565,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "DraftIssue", "id": "DI_lADOAlIw4c4BCe3VzgJwMME", @@ -2462,6 +2580,7 @@ "createdAt": "2025-09-07T10:07:40Z" }, "id": "PVTI_lADOAlIw4c4BCe3VzgedAIc", + "fullDatabaseId": "127729799", "fieldValues": { "nodes": [ { @@ -2558,6 +2677,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "DraftIssue", "id": "DI_lADOAlIw4c4BCe3VzgJwMMM", @@ -2567,6 +2692,7 @@ "createdAt": "2025-09-07T10:07:56Z" }, "id": "PVTI_lADOAlIw4c4BCe3VzgedAJI", + "fullDatabaseId": "127729810", "fieldValues": { "nodes": [ { @@ -2673,6 +2799,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJQwB", @@ -2688,6 +2820,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3VzgedAUw", + "fullDatabaseId": "127729996", "fieldValues": { "nodes": [ { @@ -2839,6 +2972,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJQyo", @@ -2854,6 +2993,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3VzgedAVU", + "fullDatabaseId": "127730005", "fieldValues": { "nodes": [ { @@ -3005,6 +3145,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJQ0r", @@ -3020,6 +3166,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3VzgedAWU", + "fullDatabaseId": "127730021", "fieldValues": { "nodes": [ { @@ -3171,6 +3318,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJQ3G", @@ -3186,6 +3339,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3VzgedAWs", + "fullDatabaseId": "127730027", "fieldValues": { "nodes": [ { @@ -3337,6 +3491,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KJRC1", @@ -3352,6 +3512,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3VzgedAwI", + "fullDatabaseId": "127730434", "fieldValues": { "nodes": [ { @@ -3399,71 +3560,18 @@ } }, { - "content": { - "__typename": "Issue", - "id": "I_kwDOIEf6YM7KQcCJ", - "body": "- https://github.com/github/sales/issues/10231\n\n---\n\n\n", - "title": "(*) Nice Systems", - "updatedAt": "2025-09-08T09:07:14Z", - "createdAt": "2025-09-08T09:07:14Z", - "number": 392, - "url": "https://github.com/github/rulasg.notes/issues/392", - "state": "OPEN", - "repository": { - "nameWithOwner": "github/rulasg.notes" - } + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" }, - "id": "PVTI_lADOAlIw4c4BCe3Vzgeemmw", - "fieldValues": { - "nodes": [ - { - "__typename": "ProjectV2ItemFieldUserValue", - "users": { - "nodes": [ - { - "login": "rulasg" - } - ] - }, - "field": { - "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", - "name": "Assignees", - "dataType": "ASSIGNEES" - } - }, - { - "__typename": "ProjectV2ItemFieldRepositoryValue", - "repository": { - "url": "https://github.com/github/rulasg.notes" - }, - "field": { - "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", - "name": "Repository", - "dataType": "REPOSITORY" - } - }, - { - "__typename": "ProjectV2ItemFieldTextValue", - "text": "(*) Nice Systems", - "field": { - "__typename": "ProjectV2Field", - "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", - "name": "Title", - "dataType": "TITLE" - } - } - ] - } - }, - { "content": { "__typename": "PullRequest", "id": "PR_kwDOPrRnkc6nndcE", "body": "", "title": "PullRequest for development", - "updatedAt": "2025-09-09T14:04:20Z", + "updatedAt": "2025-09-24T08:56:12Z", "createdAt": "2025-09-09T13:56:05Z", "number": 25, "url": "https://github.com/octodemo/rulasg-dev-1/pull/25", @@ -3473,6 +3581,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3VzgeioBY", + "fullDatabaseId": "128098326", "fieldValues": { "nodes": [ { @@ -3519,7 +3628,7 @@ }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "PR for development", + "text": "PullRequest for development", "field": { "__typename": "ProjectV2Field", "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", @@ -3606,20 +3715,37 @@ "name": "field-iteration", "dataType": "ITERATION" } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-01", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } } ] } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "DraftIssue", "id": "DI_lADOAlIw4c4BCe3VzgJwmkk", "body": "", "title": "DraftIssue for development", - "updatedAt": "2025-09-09T13:59:33Z", + "updatedAt": "2025-09-11T15:14:28Z", "createdAt": "2025-09-09T13:59:21Z" }, "id": "PVTI_lADOAlIw4c4BCe3Vzgeiodc", + "fullDatabaseId": "128098775", "fieldValues": { "nodes": [ { @@ -3716,12 +3842,18 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KkwSq", "body": "Body of issue for development", "title": "Issue for development", - "updatedAt": "2025-09-09T14:01:23Z", + "updatedAt": "2025-09-24T08:29:13Z", "createdAt": "2025-09-09T14:01:17Z", "number": 26, "url": "https://github.com/octodemo/rulasg-dev-1/issues/26", @@ -3731,6 +3863,7 @@ } }, "id": "PVTI_lADOAlIw4c4BCe3Vzgeio4o", + "fullDatabaseId": "128099210", "fieldValues": { "nodes": [ { @@ -3803,6 +3936,18 @@ "dataType": "NUMBER" } }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 3", + "startDate": "2025-10-05", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + }, { "__typename": "ProjectV2ItemFieldTextValue", "text": "text3", @@ -3836,17 +3981,69 @@ } ] } - }, + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4BCe3VzgJymYM", + "body": "", + "title": "Issue to find", + "updatedAt": "2025-09-22T14:23:38Z", + "createdAt": "2025-09-22T14:23:38Z" + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgfBGX8", + "fullDatabaseId": "130095487", + "fieldValues": { + "nodes": [ { - "__typename": "ProjectV2ItemFieldIterationValue", - "title": "field-iteration 3", - "startDate": "2025-10-05", - "duration": 14, + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Issue to find", "field": { - "__typename": "ProjectV2IterationField", - "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", - "name": "field-iteration", - "dataType": "ITERATION" + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + } + ] + } + }, + { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4BCe3VzgJymYQ", + "body": "", + "title": "ISSUE to FIND", + "updatedAt": "2025-09-22T14:29:30Z", + "createdAt": "2025-09-22T14:23:41Z" + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgfBGZg", + "fullDatabaseId": "130095512", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "ISSUE to FIND", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" } } ] diff --git a/Test/private/mocks/invoke-GitHubUpdateItemValue-PVTI_lADOAlIw4c4BCe3VzgeioBY-PVTF_lADOAlIw4c4BCe3Vzg0rhjU.json b/Test/private/mocks/invoke-GitHubUpdateItemValue-PVTI_lADOAlIw4c4BCe3VzgeioBY-PVTF_lADOAlIw4c4BCe3Vzg0rhjU.json new file mode 100644 index 0000000..a34e6b2 --- /dev/null +++ b/Test/private/mocks/invoke-GitHubUpdateItemValue-PVTI_lADOAlIw4c4BCe3VzgeioBY-PVTF_lADOAlIw4c4BCe3Vzg0rhjU.json @@ -0,0 +1,155 @@ +{ + "data": { + "updateProjectV2ItemFieldValue": { + "projectV2Item": { + "content": { + "__typename": "PullRequest", + "body": "", + "title": "PullRequest for development", + "number": 25, + "url": "https://github.com/octodemo/rulasg-dev-1/pull/25", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgeioBY", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rulasg" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 1: Project Setup", + "description": "Initial project setup and basic infrastructure", + "dueOn": "2025-09-20T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "PullRequest for development", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 10.1, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text1", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-1", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 1", + "startDate": "2025-09-07", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + } + } + } +} diff --git a/Test/private/mocks/invoke-GitHubUpdateItemValue-PVTI_lADOAlIw4c4BCe3VzgeioBY-PVTF_lADOAlIw4c4BCe3Vzg0rhlU.json b/Test/private/mocks/invoke-GitHubUpdateItemValue-PVTI_lADOAlIw4c4BCe3VzgeioBY-PVTF_lADOAlIw4c4BCe3Vzg0rhlU.json new file mode 100644 index 0000000..445719a --- /dev/null +++ b/Test/private/mocks/invoke-GitHubUpdateItemValue-PVTI_lADOAlIw4c4BCe3VzgeioBY-PVTF_lADOAlIw4c4BCe3Vzg0rhlU.json @@ -0,0 +1,165 @@ +{ + "data": { + "updateProjectV2ItemFieldValue": { + "projectV2Item": { + "content": { + "__typename": "PullRequest", + "body": "", + "title": "PullRequest for development", + "number": 25, + "url": "https://github.com/octodemo/rulasg-dev-1/pull/25", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgeioBY", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rulasg" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 1: Project Setup", + "description": "Initial project setup and basic infrastructure", + "dueOn": "2025-09-20T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "PullRequest for development", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 111.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text1", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-1", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 1", + "startDate": "2025-09-07", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2021-01-10", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + } + ] + } + } + } + } +} diff --git a/Test/private/mocks/invoke-GitHubUpdateItemValue-PVTI_lADOAlIw4c4BCe3VzgeioBY-PVTSSF_lADOAlIw4c4BCe3Vzg0rhno.json b/Test/private/mocks/invoke-GitHubUpdateItemValue-PVTI_lADOAlIw4c4BCe3VzgeioBY-PVTSSF_lADOAlIw4c4BCe3Vzg0rhno.json new file mode 100644 index 0000000..07ca0ce --- /dev/null +++ b/Test/private/mocks/invoke-GitHubUpdateItemValue-PVTI_lADOAlIw4c4BCe3VzgeioBY-PVTSSF_lADOAlIw4c4BCe3Vzg0rhno.json @@ -0,0 +1,165 @@ +{ + "data": { + "updateProjectV2ItemFieldValue": { + "projectV2Item": { + "content": { + "__typename": "PullRequest", + "body": "", + "title": "PullRequest for development", + "number": 25, + "url": "https://github.com/octodemo/rulasg-dev-1/pull/25", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3VzgeioBY", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "rulasg" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-Q", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 1: Project Setup", + "description": "Initial project setup and basic infrastructure", + "dueOn": "2025-09-20T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "PullRequest for development", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 111.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text1", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 1", + "startDate": "2025-09-07", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-01", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } + } + ] + } + } + } + } +} diff --git a/Test/private/mocks/invoke-addissuecomment-I_kwDOPrRnkc7KkwSq.json b/Test/private/mocks/invoke-addissuecomment-I_kwDOPrRnkc7KkwSq.json new file mode 100644 index 0000000..543d169 --- /dev/null +++ b/Test/private/mocks/invoke-addissuecomment-I_kwDOPrRnkc7KkwSq.json @@ -0,0 +1,11 @@ +{ + "data": { + "addComment": { + "commentEdge": { + "node": { + "url": "https://github.com/octodemo/rulasg-dev-1/issues/26#issuecomment-3324995787" + } + } + } + } +} diff --git a/Test/private/mocks/invoke-addissuecomment-PR_kwDOPrRnkc6nndcE.json b/Test/private/mocks/invoke-addissuecomment-PR_kwDOPrRnkc6nndcE.json new file mode 100644 index 0000000..88b7b70 --- /dev/null +++ b/Test/private/mocks/invoke-addissuecomment-PR_kwDOPrRnkc6nndcE.json @@ -0,0 +1,11 @@ +{ + "data": { + "addComment": { + "commentEdge": { + "node": { + "url": "https://github.com/octodemo/rulasg-dev-1/pull/25#issuecomment-3327293422" + } + } + } + } +} diff --git a/Test/private/mocks/invoke-clearProjectV2ItemFieldValue-PVT_kwDOAlIw4c4BCe3V-PVTI_lADOAlIw4c4BCe3Vzgeio4o-PVTF_lADOAlIw4c4BCe3Vzg0rhko.json b/Test/private/mocks/invoke-clearProjectV2ItemFieldValue-PVT_kwDOAlIw4c4BCe3V-PVTI_lADOAlIw4c4BCe3Vzgeio4o-PVTF_lADOAlIw4c4BCe3Vzg0rhko.json new file mode 100644 index 0000000..f33b57b --- /dev/null +++ b/Test/private/mocks/invoke-clearProjectV2ItemFieldValue-PVT_kwDOAlIw4c4BCe3V-PVTI_lADOAlIw4c4BCe3Vzgeio4o-PVTF_lADOAlIw4c4BCe3Vzg0rhko.json @@ -0,0 +1,129 @@ +{ + "data": { + "clearProjectV2ItemFieldValue": { + "projectV2Item": { + "content": { + "__typename": "Issue", + "body": "Body of issue for development", + "title": "Issue for development", + "number": 26, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/26", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgeio4o", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 3: Quality and Deployment", + "description": "Testing, documentation, and deployment pipeline setup", + "dueOn": "2025-10-18T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Issue for development", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 333.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-3", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 3", + "startDate": "2025-10-05", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + } + } + } +} diff --git a/Test/private/mocks/invoke-clearProjectV2ItemFieldValue-PVT_kwDOAlIw4c4BCe3V-PVTI_lADOAlIw4c4BCe3Vzgeio4o-PVTSSF_lADOAlIw4c4BCe3Vzg0rhno.json b/Test/private/mocks/invoke-clearProjectV2ItemFieldValue-PVT_kwDOAlIw4c4BCe3V-PVTI_lADOAlIw4c4BCe3Vzgeio4o-PVTSSF_lADOAlIw4c4BCe3Vzg0rhno.json new file mode 100644 index 0000000..1ae4d2a --- /dev/null +++ b/Test/private/mocks/invoke-clearProjectV2ItemFieldValue-PVT_kwDOAlIw4c4BCe3V-PVTI_lADOAlIw4c4BCe3Vzgeio4o-PVTSSF_lADOAlIw4c4BCe3Vzg0rhno.json @@ -0,0 +1,105 @@ +{ + "data": { + "clearProjectV2ItemFieldValue": { + "projectV2Item": { + "content": { + "__typename": "Issue", + "body": "Body of issue for development", + "title": "Issue for development", + "number": 26, + "url": "https://github.com/octodemo/rulasg-dev-1/issues/26", + "repository": { + "nameWithOwner": "octodemo/rulasg-dev-1" + } + }, + "id": "PVTI_lADOAlIw4c4BCe3Vzgeio4o", + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/octodemo/rulasg-dev-1" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-k", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldMilestoneValue", + "milestone": { + "title": "Milestone 3: Quality and Deployment", + "description": "Testing, documentation, and deployment pipeline setup", + "dueOn": "2025-10-18T00:00:00Z" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-g", + "name": "Milestone", + "dataType": "MILESTONE" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Issue for development", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Todo", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 333.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 3", + "startDate": "2025-10-05", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + } + } + } +} diff --git a/Test/private/mocks/invoke-getitem-PVTI_lADNJr_OADU3Ys4GAgVO.json b/Test/private/mocks/invoke-getitem-PVTI_lADNJr_OADU3Ys4GAgVO.json new file mode 100644 index 0000000..909afca --- /dev/null +++ b/Test/private/mocks/invoke-getitem-PVTI_lADNJr_OADU3Ys4GAgVO.json @@ -0,0 +1,325 @@ +{ + "data": { + "node": { + "id": "PVTI_lADNJr_OADU3Ys4GAgVO", + "type": "ISSUE", + "fullDatabaseId": "100795726", + "project": { + "id": "PVT_kwDNJr_OADU3Yg", + "url": "https://github.com/orgs/github/projects/9279" + }, + "content": { + "__typename": "Issue", + "id": "I_kwDOAFbrpM6s_fNK", + "body": "Pending customer information", + "title": "CaixaBank", + "updatedAt": "2025-09-08T09:00:32Z", + "createdAt": "2025-03-07T08:16:57Z", + "number": 11742, + "url": "https://github.com/github/sales/issues/11742", + "state": "OPEN", + "repository": { + "nameWithOwner": "github/sales" + } + }, + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldUserValue", + "users": { + "nodes": [ + { + "login": "oanamariadinca" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADNJr_OADU3Ys4CH1oS", + "name": "Assignees", + "dataType": "ASSIGNEES" + } + }, + { + "__typename": "ProjectV2ItemFieldRepositoryValue", + "repository": { + "url": "https://github.com/github/sales" + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADNJr_OADU3Ys4CH1oY", + "name": "Repository", + "dataType": "REPOSITORY" + } + }, + { + "__typename": "ProjectV2ItemFieldLabelValue", + "labels": { + "nodes": [ + { + "name": "Client" + } + ] + }, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADNJr_OADU3Ys4CH1oU", + "name": "Labels", + "dataType": "LABELS" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "CaixaBank", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADNJr_OADU3Ys4CH1oR", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "🔴 Client", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADNJr_OADU3Ys4H9tgv", + "name": "MyType", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "52dd0a94", + "name": "🟣 Event" + }, + { + "id": "07f69b86", + "name": "🔴 Client" + }, + { + "id": "936b8226", + "name": "🔴 Sub-Client" + }, + { + "id": "676aeaed", + "name": "🔵 Project" + }, + { + "id": "1f915e81", + "name": "🔵 Opportunity" + }, + { + "id": "dc2c7605", + "name": "🟠 Session" + }, + { + "id": "b1c7a26b", + "name": "🟡 Support" + }, + { + "id": "a474d0fe", + "name": "🟡 CxQuestion" + }, + { + "id": "b52d5003", + "name": "🟤 Training" + }, + { + "id": "10ea9bf2", + "name": "🟢 Task" + }, + { + "id": "92474147", + "name": "🟢 Dev Task" + }, + { + "id": "feb2cfe2", + "name": "⚫️ Followup" + }, + { + "id": "c4555c8b", + "name": "⚪️ Reference" + }, + { + "id": "85528198", + "name": "🟣 Preview/Feature" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "Spain 🇪🇸", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADNJr_OADU3Ys4Keswg", + "name": "oa_Country", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "☺️ Normal", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADNJr_OADU3Ys4CNK1v", + "name": "Priority", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "ce18a259", + "name": "🔥 Critical" + }, + { + "id": "31843608", + "name": "🥵 High" + }, + { + "id": "a389e3b2", + "name": "☺️ Normal" + }, + { + "id": "1cb3b85a", + "name": "🥶 Low" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "InProgress", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADNJr_OADU3Ys4CH1oT", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "50071b25", + "name": "ActionRequired" + }, + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "5954772e", + "name": "Followup" + }, + { + "id": "47fc9ee4", + "name": "InProgress" + }, + { + "id": "8caaa5f1", + "name": "Waiting" + }, + { + "id": "700d6341", + "name": "Planned" + }, + { + "id": "fa3a5614", + "name": "Answered" + }, + { + "id": "3fec1e37", + "name": "Dormant" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "⚪️", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADNJr_OADU3Ys4H-wa8", + "name": "Topic", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "4109cf64", + "name": "🟡" + }, + { + "id": "7141fadd", + "name": "⚪️" + }, + { + "id": "8f98d44d", + "name": "🟠" + }, + { + "id": "bd39a099", + "name": "🟢" + }, + { + "id": "6c319f30", + "name": "⚫️" + }, + { + "id": "d5222459", + "name": "🔵" + }, + { + "id": "6727fc0d", + "name": "🟣" + }, + { + "id": "2130ccae", + "name": "🟤" + }, + { + "id": "484975c2", + "name": "🔴" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 1.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADNJr_OADU3Ys4LxRAX", + "name": "LastUpdateInWeeks", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "Critical", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADNJr_OADU3Ys4NBnOe", + "name": "Severity", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f4488bd9", + "name": "Critical" + }, + { + "id": "bd05cbe2", + "name": "High" + }, + { + "id": "c17bf654", + "name": "Medium" + }, + { + "id": "50650f37", + "name": "Low" + } + ] + } + } + ] + } + } + } +} diff --git a/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4A0Lf4zgYNTc0.json b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4A0Lf4zgYNTc0.json index 06d0aac..19497f6 100644 --- a/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4A0Lf4zgYNTc0.json +++ b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4A0Lf4zgYNTc0.json @@ -3,6 +3,7 @@ "node": { "id": "PVTI_lADOAlIw4c4A0Lf4zgYNTc0", "type": "DRAFT_ISSUE", + "fullDatabaseId": "101535181", "project": { "id": "PVT_kwDOAlIw4c4A0Lf4", "url": "https://github.com/orgs/octodemo/projects/625" diff --git a/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4A0Lf4zgYNTxI.json b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4A0Lf4zgYNTxI.json index b2ac6fe..69f46fc 100644 --- a/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4A0Lf4zgYNTxI.json +++ b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4A0Lf4zgYNTxI.json @@ -3,6 +3,7 @@ "node": { "id": "PVTI_lADOAlIw4c4A0Lf4zgYNTxI", "type": "DRAFT_ISSUE", + "fullDatabaseId": "101535506", "project": { "id": "PVT_kwDOAlIw4c4A0Lf4", "url": "https://github.com/orgs/octodemo/projects/625" diff --git a/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3Vzgeio4o.json b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3Vzgeio4o.json index 0292005..e1aaf27 100644 --- a/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3Vzgeio4o.json +++ b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3Vzgeio4o.json @@ -3,6 +3,11 @@ "node": { "id": "PVTI_lADOAlIw4c4BCe3Vzgeio4o", "type": "ISSUE", + "fullDatabaseId": "128099210", + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "Issue", "id": "I_kwDOPrRnkc7KkwSq", diff --git a/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3VzgeioBY.json b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3VzgeioBY.json index 8365ff4..bf1f90e 100644 --- a/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3VzgeioBY.json +++ b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3VzgeioBY.json @@ -3,6 +3,11 @@ "node": { "id": "PVTI_lADOAlIw4c4BCe3VzgeioBY", "type": "PULL_REQUEST", + "fullDatabaseId": "128098326", + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "PullRequest", "id": "PR_kwDOPrRnkc6nndcE", @@ -107,7 +112,7 @@ }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "new value of the comment 10", + "text": "text1", "field": { "__typename": "ProjectV2Field", "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", @@ -150,6 +155,16 @@ "name": "field-iteration", "dataType": "ITERATION" } + }, + { + "__typename": "ProjectV2ItemFieldDateValue", + "date": "2025-09-01", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhlU", + "name": "field-date", + "dataType": "DATE" + } } ] } diff --git a/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3Vzgeiodc-updated.json b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3Vzgeiodc-updated.json new file mode 100644 index 0000000..ab1ba0b --- /dev/null +++ b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3Vzgeiodc-updated.json @@ -0,0 +1,115 @@ +{ + "data": { + "node": { + "id": "PVTI_lADOAlIw4c4BCe3Vzgeiodc", + "type": "DRAFT_ISSUE", + "fullDatabaseId": "128098775", + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "url": "https://github.com/orgs/octodemo/projects/700" + }, + "content": { + "__typename": "DraftIssue", + "id": "DI_lADOAlIw4c4BCe3VzgJwmkk", + "body": "", + "title": "DraftIssue for development updated", + "updatedAt": "2025-09-11T15:14:28Z", + "createdAt": "2025-09-09T13:59:21Z" + }, + "fieldValues": { + "nodes": [ + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "DraftIssue for development updated", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rg-M", + "name": "Title", + "dataType": "TITLE" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "In Progress", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rg-U", + "name": "Status", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "f75ad846", + "name": "Todo" + }, + { + "id": "47fc9ee4", + "name": "In Progress" + }, + { + "id": "98236657", + "name": "Done" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldNumberValue", + "number": 222.0, + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhjU", + "name": "field-number", + "dataType": "NUMBER" + } + }, + { + "__typename": "ProjectV2ItemFieldTextValue", + "text": "text2 updated", + "field": { + "__typename": "ProjectV2Field", + "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", + "name": "field-text", + "dataType": "TEXT" + } + }, + { + "__typename": "ProjectV2ItemFieldSingleSelectValue", + "name": "option-2", + "field": { + "__typename": "ProjectV2SingleSelectField", + "id": "PVTSSF_lADOAlIw4c4BCe3Vzg0rhno", + "name": "field-singleselect", + "dataType": "SINGLE_SELECT", + "options": [ + { + "id": "7d96a925", + "name": "option-1" + }, + { + "id": "cd02c585", + "name": "option-2" + }, + { + "id": "6b66c93a", + "name": "option-3" + } + ] + } + }, + { + "__typename": "ProjectV2ItemFieldIterationValue", + "title": "field-iteration 2", + "startDate": "2025-09-21", + "duration": 14, + "field": { + "__typename": "ProjectV2IterationField", + "id": "PVTIF_lADOAlIw4c4BCe3Vzg0rhqQ", + "name": "field-iteration", + "dataType": "ITERATION" + } + } + ] + } + } + } +} diff --git a/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3Vzgeiodc.json b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3Vzgeiodc.json index c286998..c9621c4 100644 --- a/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3Vzgeiodc.json +++ b/Test/private/mocks/invoke-getitem-PVTI_lADOAlIw4c4BCe3Vzgeiodc.json @@ -3,6 +3,11 @@ "node": { "id": "PVTI_lADOAlIw4c4BCe3Vzgeiodc", "type": "DRAFT_ISSUE", + "fullDatabaseId": "128098775", + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "DraftIssue", "id": "DI_lADOAlIw4c4BCe3VzgJwmkk", @@ -59,7 +64,7 @@ }, { "__typename": "ProjectV2ItemFieldTextValue", - "text": "new value of the comment 10", + "text": "text2", "field": { "__typename": "ProjectV2Field", "id": "PVTF_lADOAlIw4c4BCe3Vzg0rhko", diff --git a/Test/private/mocks/invoke-getitem-PVTI_lADOBCrGTM4ActQazgMuXXc.json b/Test/private/mocks/invoke-getitem-PVTI_lADOBCrGTM4ActQazgMuXXc.json index c240403..97122df 100644 --- a/Test/private/mocks/invoke-getitem-PVTI_lADOBCrGTM4ActQazgMuXXc.json +++ b/Test/private/mocks/invoke-getitem-PVTI_lADOBCrGTM4ActQazgMuXXc.json @@ -3,6 +3,7 @@ "node": { "id": "PVTI_lADOBCrGTM4ActQazgMuXXc", "type": "DRAFT_ISSUE", + "fullDatabaseId": "53370231", "project": { "id": "PVT_kwDOBCrGTM4ActQa", "url": "https://github.com/orgs/SolidifyDemo/projects/164" diff --git a/Test/private/mocks/projectV2.json b/Test/private/mocks/projectV2.json index 6fc3abd..792f485 100644 --- a/Test/private/mocks/projectV2.json +++ b/Test/private/mocks/projectV2.json @@ -1901,6 +1901,12 @@ } }, { + "project": { + "id": "PVT_kwDOAlIw4c4BCe3V", + "number": 700, + "title": "rulasg-dev-700", + "url": "https://github.com/orgs/octodemo/projects/700" + }, "content": { "__typename": "DraftIssue", "id": "DI_lADOBCrGTM4ActQazgFYYPQ", diff --git a/Test/public/environmentCache.test.ps1 b/Test/public/environmentCache.test.ps1 index f1f951b..a58da1c 100644 --- a/Test/public/environmentCache.test.ps1 +++ b/Test/public/environmentCache.test.ps1 @@ -7,20 +7,23 @@ function Test_EnvironmentCache{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 ; $itemsCount = 12 ; $fieldsCount = 18 - $fieldComment = "Comment" ; $fieldTitle = "Title" + $p = Get-Mock_Project_700 ; $Owner = $p.owner ; $projectNumber = $p.number + $i = $p.issue + $f = $p.fieldtext + + $fieldComment = $f.name + $fieldTitle = "Title" # Cache the project - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' - $null = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber + MockCall_GetProject_700 -Cache # Reset mock calls Reset-invokeCommandMock Mock_DatabaseRoot -NotReset - $itemId = "PVTI_lADOBCrGTM4ActQazgMuXXc" - $fieldTitleValue = "A draft in the project" - $fieldCommentValue = "This" + $itemId = $i.Id + $fieldTitleValue = $i.title + $fieldCommentValue = $i.fieldtext $result = Get-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -ItemId $itemId diff --git a/Test/public/integrations/sync-ProjectItemsBetweenProjects.test.ps1 b/Test/public/integrations/sync-ProjectItemsBetweenProjects.test.ps1 index d3fcf8c..3ab338f 100644 --- a/Test/public/integrations/sync-ProjectItemsBetweenProjects.test.ps1 +++ b/Test/public/integrations/sync-ProjectItemsBetweenProjects.test.ps1 @@ -2,23 +2,23 @@ function Test_UpdateProjectItemsBetweenProjects{ Reset-InvokeCommandMock Mock_DatabaseRoot - $owner = "octodemo" - $sourceProjectNumber = 625 - $destinationProjectNumber = 626 - - # Mock project calls - $sourceProjectNumber, $destinationProjectNumber | ForEach-Object { - $projectNumber = $_ - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName "invoke-GitHubOrgProjectWithFields-$owner-$projectNumber.syncprj.json" - } + $p625 = Get-Mock_Project_625 ; $owner0 = $p625.owner ; $projectNumber0 = $p625.number + $p626 = Get-Mock_Project_626 ; $owner1 = $p626.owner ; $projectNumber1 = $p626.number + + $sourceOwner = $owner0 ; $sourceProjectNumber = $projectNumber0 + $destinationOwner = $owner1 ; $destinationProjectNumber = $projectNumber1 + + MockCall_GetProject -MockProject $p625 + MockCall_GetProject -MockProject $p626 $fieldlist = @("Int1", "Int2") $params = @{ - SourceOwner = $owner + SourceOwner = $sourceOwner SourceProjectNumber = $sourceProjectNumber - DestinationOwner = $owner + DestinationOwner = $destinationOwner DestinationProjectNumber = $destinationProjectNumber + FieldSlug = "pr1_" } $result = Update-ProjectItemsBetweenProjects -IncludeDoneItems @params @@ -26,39 +26,29 @@ function Test_UpdateProjectItemsBetweenProjects{ $staged = Get-ProjectItemStaged -Owner $owner -ProjectNumber $destinationProjectNumber - Assert-Count -Expected 3 -Presented $staged.Keys - - Assert-Count -Expected 2 -Presented $staged.PVTI_lADOAlIw4c4A0QAozgYQpRY.Keys - Assert-AreEqual -Presented $staged.PVTI_lADOAlIw4c4A0QAozgYQpP0.PVTF_lADOAlIw4c4A0QAozgp6aGw.Value -Expected "Value issue 1" - Assert-AreEqual -Presented $staged.PVTI_lADOAlIw4c4A0QAozgYQpP0.PVTF_lADOAlIw4c4A0QAozgp6aK4.Value -Expected "11" - - Assert-Count -Expected 2 -Presented $staged.PVTI_lADOAlIw4c4A0QAozgYQpSc.Keys - Assert-AreEqual -Presented $staged.PVTI_lADOAlIw4c4A0QAozgYQpSc.PVTF_lADOAlIw4c4A0QAozgp6aGw.Value -Expected "Value issue 3" - Assert-AreEqual -Presented $staged.PVTI_lADOAlIw4c4A0QAozgYQpSc.PVTF_lADOAlIw4c4A0QAozgp6aK4.Value -Expected "33" - - Assert-Count -Expected 2 -Presented $staged.PVTI_lADOAlIw4c4A0QAozgYQpP0.Keys - Assert-AreEqual -Presented $staged.PVTI_lADOAlIw4c4A0QAozgYQpRY.PVTF_lADOAlIw4c4A0QAozgp6aGw.Value -Expected "Value issue 2" - Assert-AreEqual -Presented $staged.PVTI_lADOAlIw4c4A0QAozgYQpRY.PVTF_lADOAlIw4c4A0QAozgp6aK4.Value -Expected "22" - + $p = $p626.syncBtwPrj_625 + + Assert-AreEqual -Expected $p.staged.Count -Presented $staged.Count + foreach ($itemId in $staged.Keys) { + Assert-AreEqual -Expected $p.staged.$itemId.Count -Presented $staged.$itemId.Count + foreach ($fieldId in $p.staged.$itemId.Keys) { + Assert-AreEqual -Expected $p.staged.$itemId.$fieldId -Presented $staged.$itemId.$fieldId.Value + } + } } function Test_UpdateProjectItemsBetweenProjects_NoRefresh_NoRefresh{ Reset-InvokeCommandMock Mock_DatabaseRoot - $owner = "octodemo" - $sourceProjectNumber = 625 - $destinationProjectNumber = 626 + $p625 = Get-Mock_Project_625 ; $owner0 = $p625.owner ; $projectNumber0 = $p625.number + $p626 = Get-Mock_Project_626 ; $owner1 = $p626.owner ; $projectNumber1 = $p626.number - # Mock project calls - $sourceProjectNumber, $destinationProjectNumber | ForEach-Object { - $projectNumber = $_ - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName "invoke-GitHubOrgProjectWithFields-$owner-$projectNumber.syncprj.json" - } + $sourceOwner = $owner0 ; $sourceProjectNumber = $projectNumber0 + $destinationOwner = $owner1 ; $destinationProjectNumber = $projectNumber1 - # Cache projects using mocks calls - $destinationProject = Get-Project -Owner $owner -ProjectNumber $destinationProjectNumber - $sourceProject = Get-Project -Owner $owner -ProjectNumber $sourceProjectNumber + MockCall_GetProject -MockProject $p625 -Cache + MockCall_GetProject -MockProject $p626 -Cache # Reset mocks to fail if mocks are called again Reset-InvokeCommandMock @@ -67,18 +57,20 @@ function Test_UpdateProjectItemsBetweenProjects_NoRefresh_NoRefresh{ # Act $params = @{ - SourceOwner = $owner + SourceOwner = $sourceOwner SourceProjectNumber = $sourceProjectNumber - DestinationOwner = $owner + DestinationOwner = $destinationOwner DestinationProjectNumber = $destinationProjectNumber + FieldSlug = "pr1_" } - $result = Update-ProjectItemsBetweenProjects -IncludeDoneItems -NoRefreshDestination -NoRefreshSource @params + $result = Update-ProjectItemsBetweenProjects -IncludeDoneItems -NoRefreshDestination -NoRefreshSource @params Assert-IsNull -Object $result - $staged = Get-ProjectItemStaged -Owner $owner -ProjectNumber $destinationProjectNumber + $staged = Get-ProjectItemStaged -Owner $destinationOwner -ProjectNumber $destinationProjectNumber - Assert-Count -Expected 3 -Presented $staged.Keys + $p = $p626.syncBtwPrj_625 + Assert-AreEqual -Expected $p.staged.Count -Presented $staged.Count } @@ -86,15 +78,12 @@ function Test_SyncProjectItemsBetweenProjects_SameValues{ Reset-InvokeCommandMock Mock_DatabaseRoot - $owner = "octodemo" - $sourceProjectNumber = 625 - $destinationProjectNumber = 625 + MockCall_GetProject_700 + $p = Get-Mock_Project_700 ; $owner = $p.owner ; $projectNumber = $p.number - # Mock poject calls - $sourceProjectNumber, $destinationProjectNumber | ForEach-Object { - $projectNumber = $_ - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName "invoke-GitHubOrgProjectWithFields-$owner-$projectNumber.syncprj.json" - } + # Setting source and destination project the same + $sourceProjectNumber = $projectNumber + $destinationProjectNumber = $projectNumber $params = @{ SourceOwner = $owner @@ -102,8 +91,11 @@ function Test_SyncProjectItemsBetweenProjects_SameValues{ DestinationOwner = $owner DestinationProjectNumber = $destinationProjectNumber } + + # TODO : this call takes long on every test. Make it quicker $result = Update-ProjectItemsBetweenProjects @params - Assert-IsNull -Object $result + + Assert-IsNull -Object $result -Comment "func always should return null" $staged = Get-ProjectItemStaged -Owner $owner -ProjectNumber $destinationProjectNumber diff --git a/Test/public/integrations/update-ProjectImtesWithInjection.test.ps1 b/Test/public/integrations/update-ProjectImtesWithInjection.test.ps1 index bec543b..c8567e6 100644 --- a/Test/public/integrations/update-ProjectImtesWithInjection.test.ps1 +++ b/Test/public/integrations/update-ProjectImtesWithInjection.test.ps1 @@ -7,13 +7,11 @@ function Test_UpdateProjectWithInjection{ # https://github.com/orgs/octodemo/projects/625/views/1 - $owner = "octodemo" - $projectNumber = "625" - - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName "invoke-GitHubOrgProjectWithFields-$owner-$projectNumber.json" - - New-ModuleV3 -Name IntegrationFunctions + $mp = Get-Mock_Project_625 ; $owner = $mp.owner ; $projectNumber = $mp.number + MockCall_GetProject -MockProject $mp + $p = $mp.updateWithInjection + # Define global integration functions function global:Invoke-ProjectInjection_1 { [CmdletBinding()] param( @@ -24,15 +22,22 @@ function Test_UpdateProjectWithInjection{ "String from integration1" | Write-Host $params = @{ - ItemId = "PVTI_lADOAlIw4c4A0Lf4zgYNTxI" Owner = $Owner ProjectNumber = $ProjectNumber - FieldName = "sf_Text1" + ItemId = $p.item1.id + FieldName = $p.field1.name Value = "Value updated from integration1" } Edit-ProjectItem @params + } + # Expected staged info for invoke-ProjectInjection_1 + $expectedStaged = @{ + $($p.item1.id) = @{ + $($p.field1.id) = "Value updated from integration1" + } } + function global:Invoke-ProjectInjection_2 { [CmdletBinding()] param( @@ -42,21 +47,24 @@ function Test_UpdateProjectWithInjection{ "String from integration1" | Write-Host $params = @{ - ItemId = "PVTI_lADOAlIw4c4A0Lf4zgYNTc0" Owner = $Owner ProjectNumber = $ProjectNumber - FieldName = "sf_Text2" + ItemId = $p.item2.id + FieldName = $p.field2.name Value = "Value updated from integration2" } Edit-ProjectItem @params } - $param = @{ - Owner = $Owner - ProjectNumber = $ProjectNumber + # Expected staged info for invoke-ProjectInjection_2 + $expectedStaged += @{ + $($p.item2.id) = @{ + $($p.field2.id) = "Value updated from integration2" + } } - $result = Update-ProjectItemsWithInjection @param + # Act + $result = Update-ProjectItemsWithInjection -owner $Owner -ProjectNumber $ProjectNumber Assert-AreEqual -Expected 2 -Presented $result.Pass Assert-AreEqual -Expected 2 -Presented $result.Integrations @@ -64,16 +72,15 @@ function Test_UpdateProjectWithInjection{ Assert-Contains -Expected "Invoke-ProjectInjection_2" -Presented $result.IntegrationsName # Confirm that the changes are staged - $result = Get-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber - - Assert-Count -Expected 2 -Presented $result - - # PVTI_lADOAlIw4c4A0Lf4zgYNTxI - Assert-AreEqual -Expected "Value updated from integration1" -Presented $result.PVTI_lADOAlIw4c4A0Lf4zgYNTxI.PVTF_lADOAlIw4c4A0Lf4zgp2lxM.Value - - # PVTI_lADOAlIw4c4A0Lf4zgYNTc0 - Assert-AreEqual -Expected "Value updated from integration2" -Presented $result.PVTI_lADOAlIw4c4A0Lf4zgYNTc0.PVTF_lADOAlIw4c4A0Lf4zgp2l3o.Value + $staged = Get-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber + # Items edited + Assert-AreEqual -Expected $expectedStaged.Count -Presented $staged.Count -Comment "Items staged" + foreach($id in $expectedStaged.Keys){ + foreach($field in $expectedStaged.$id.Keys){ + Assert-AreEqual -Expected $expectedStaged.$id.$field -Presented $staged.$id.$field.Value -Comment "Item $id Field $field" + } + } } function Test_UpdateProjectWithInjection_Failed_1{ @@ -83,13 +90,11 @@ function Test_UpdateProjectWithInjection_Failed_1{ # https://github.com/orgs/octodemo/projects/625/views/1 - $owner = "octodemo" - $projectNumber = "625" - - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName "invoke-GitHubOrgProjectWithFields-$owner-$projectNumber.json" - - New-ModuleV3 -Name IntegrationFunctions + $mp = Get-Mock_Project_625 ; $owner = $mp.owner ; $projectNumber = $mp.number + MockCall_GetProject -MockProject $mp + $p = $mp.updateWithInjection + # Define global integration functions function global:Invoke-ProjectInjection_1 { [CmdletBinding()] param( @@ -100,15 +105,22 @@ function Test_UpdateProjectWithInjection_Failed_1{ "String from integration1" | Write-Host $params = @{ - ItemId = "PVTI_lADOAlIw4c4A0Lf4zgYNTxI" Owner = $Owner ProjectNumber = $ProjectNumber - FieldName = "sf_Text1" + ItemId = $p.item1.id + FieldName = $p.field1.name Value = "Value updated from integration1" } Edit-ProjectItem @params + } + # Expected staged info for invoke-ProjectInjection_1 + $expectedStaged = @{ + $($p.item1.id) = @{ + $($p.field1.id) = "Value updated from integration1" + } } + function global:Invoke-ProjectInjection_2 { [CmdletBinding()] param( @@ -120,12 +132,8 @@ function Test_UpdateProjectWithInjection_Failed_1{ throw "Integration 2 failed" } - $param = @{ - Owner = $Owner - ProjectNumber = $ProjectNumber - } - - $result = Update-ProjectItemsWithInjection @param + # Act + $result = Update-ProjectItemsWithInjection -Owner $Owner -ProjectNumber $ProjectNumber Assert-AreEqual -Expected 2 -Presented $result.Integrations Assert-Contains -Expected "Invoke-ProjectInjection_1" -Presented $result.IntegrationsName @@ -138,17 +146,17 @@ function Test_UpdateProjectWithInjection_Failed_1{ Assert-AreEqual -Expected "Integration 2 failed" -Presented $result.FailedIntegrationErrors."Invoke-ProjectInjection_2".Exception.Message Assert-AreEqual -Expected "Integration 2 failed" -Presented $global:FailedIntegrationErrors."Invoke-ProjectInjection_2".Exception.Message - # Confirm that the changes are staged - $result = Get-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber + $staged = Get-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber - Assert-Count -Expected 1 -Presented $result - - # PVTI_lADOAlIw4c4A0Lf4zgYNTxI - Assert-AreEqual -Expected "Value updated from integration1" -Presented $result.PVTI_lADOAlIw4c4A0Lf4zgYNTxI.PVTF_lADOAlIw4c4A0Lf4zgp2lxM.Value - - # # PVTI_lADOAlIw4c4A0Lf4zgYNTc0 - # Assert-AreEqual -Expected "Value updated from integration2" -Presented $result.PVTI_lADOAlIw4c4A0Lf4zgYNTc0.PVTF_lADOAlIw4c4A0Lf4zgp2l3o.Value + # Items edited + Assert-AreEqual -Expected $expectedStaged.Count -Presented $staged.Count -Comment "Items staged" + foreach($id in $expectedStaged.Keys){ + foreach($field in $expectedStaged.$id.Keys){ + Assert-AreEqual -Expected $expectedStaged.$id.$field -Presented $staged.$id.$field.Value -Comment "Item $id Field $field" + } + } + } function Test_InvokeProjectInjection{ diff --git a/Test/public/integrations/update-ProjectItemsStatusOnDueDate.test.ps1 b/Test/public/integrations/update-ProjectItemsStatusOnDueDate.test.ps1 index f9b3143..619d51d 100644 --- a/Test/public/integrations/update-ProjectItemsStatusOnDueDate.test.ps1 +++ b/Test/public/integrations/update-ProjectItemsStatusOnDueDate.test.ps1 @@ -1,65 +1,73 @@ -function Test_UpdateProjectItemStatusOnDueDate{ +function Test_UpdateProjectItemStatusOnDueDate { Reset-InvokeCommandMock Mock_DatabaseRoot + + # Mock calling Toda MockCallToString -Command "Get-Date -Format yyyy-MM-dd" -OutString "2025-03-15" - MockCall_GitHubOrgProjectWithFields -Owner octodemo -ProjectNumber 625 -FileName "invoke-GitHubOrgProjectWithFields-octodemo-625.updateStatus.json" - - # > prg = Get-Project -Owner octodemo -ProjectNumber 625 - # > $prj.items.values | Select id,NCC,Status | Sort-Object NCC - # - # id NCC Status - # -- --- ------ - # PVTI_lADOAlIw4c4A0Lf4zgYQpP4 - # PVTI_lADOAlIw4c4A0Lf4zgYVsJc 2025-03-09 Done - # PVTI_lADOAlIw4c4A0Lf4zgYNTwo 2025-03-11 - # PVTI_lADOAlIw4c4A0Lf4zgYQpRc 2025-03-14 - # PVTI_lADOAlIw4c4A0Lf4zgYNTc0 2025-03-15 - # PVTI_lADOAlIw4c4A0Lf4zgYUeW4 2025-03-15 - # PVTI_lADOAlIw4c4A0Lf4zgYNTxI 2025-03-15 - # PVTI_lADOAlIw4c4A0Lf4zgYQpSY 2025-03-16 - # PVTI_lADOAlIw4c4A0Lf4zgYNTyM 2025-03-22 - # PVTI_lADOAlIw4c4A0Lf4zgYUecs 2025-03-26 - # - # 10 total - # 9 with NCC - # 6 with NCC overdued - # 1 with Status Done - - - $params = @{ - Owner = "octodemo" - ProjectNumber = 625 - Status = "Todo" - DueDateFieldName = "NCC" - } - $result = Update-ProjectItemsStatusOnDueDate @params + $mp = Get-Mock_Project_625 ; $owner = $mp.owner ; $projectNumber = $mp.number + MockCall_GetProject -MockProject $mp -Cache + + $p = $mp.updateStatusOnDueDate + $statusFieldId = $p.fields.status.id + $otherDone = $p.statusDoneOther + + # Act and Assert + function Assert-DueDateStaged{ + param( + [Parameter()][object]$Expected, + [Parameter(Position=1)][bool]$AnyStatus, + [Parameter(Position=2)][bool]$IncludeDoneItems, + [Parameter(Position=3)][string]$StatusDone + ) + + Reset-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber + $params = @{ + Owner = $owner + ProjectNumber = $projectNumber + StatusFieldName = $p.fields.status.name + DateFieldName = $p.fields.dueDate.name + StatusAction = $p.statusAction + StatusPlanned = $p.statusPlanned + IncludeDoneItems = $IncludeDoneItems + StatusDone = $StatusDone + AnyStatus = $AnyStatus + } - Assert-IsNull -Object $result + # Act + $result = Update-ProjectItemsStatusOnDueDate @params + + # Result is null + Assert-IsNull -Object $result -Comment "Result is null" + + # Assert + $staged = Get-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber + + # Items edited to ActionRequired or Planned + $total = $Expected.Count + Assert-AreEqual -Expected $total -Presented $staged.Count + foreach($id in $Expected.Keys){ + foreach($field in $Expected.$id.Keys){ + Assert-AreEqual -Expected $Expected.$id.$field -Presented $staged.$id.$field.Value -Comment "Item $id Field $field" + } + } + } - $staged = Get-ProjectItemStaged -Owner octodemo -ProjectNumber 625 + # Assert of the combination of the three parameters - Assert-Count -Expected 5 -Presented $staged + # AnyStatus | IncludeDoneItems | DoneOther | Expected + Assert-DueDateStaged $true $true "" -Expected ($p.staged + $p.anyStatus + $p.includeDone ) + Assert-DueDateStaged $false $true "" -Expected ($p.staged + $p.includeDone ) + Assert-DueDateStaged $true $false "" -Expected ($p.staged + $p.anyStatus ) + Assert-DueDateStaged $false $false "" -Expected ($p.staged ) + Assert-DueDateStaged $true $true $otherDone -Expected ($p.staged + $p.anyStatus_and_includeDoneOther + $p.includeDone ) + Assert-DueDateStaged $false $true $otherDone -Expected ($p.staged + $p.includeDoneOther + $p.includeDone ) + Assert-DueDateStaged $true $false $otherDone -Expected ($p.staged + $p.anyStatus_and_includeDoneOther ) + Assert-DueDateStaged $false $false $otherDone -Expected ($p.staged + $p.includeDoneOther ) - Assert-Contains -Expected PVTI_lADOAlIw4c4A0Lf4zgYNTc0 -Presented $staged.Keys - Assert-Contains -Expected PVTI_lADOAlIw4c4A0Lf4zgYNTwo -Presented $staged.Keys - Assert-Contains -Expected PVTI_lADOAlIw4c4A0Lf4zgYNTxI -Presented $staged.Keys - Assert-Contains -Expected PVTI_lADOAlIw4c4A0Lf4zgYQpRc -Presented $staged.Keys - Assert-Contains -Expected PVTI_lADOAlIw4c4A0Lf4zgYUeW4 -Presented $staged.Keys - Assert-NotContains -Expected PVTI_lADOAlIw4c4A0Lf4zgYVsJc -Presented $staged.Keys -Comment "Done item" - Assert-NotContains -Expected PVTI_lADOAlIw4c4A0Lf4zgYQpP4 -Presented $staged.Keys -Comment "Item without NCC" +} - # Act with NotDone - Reset-ProjectItemStaged -Owner octodemo -ProjectNumber 625 - # Act - $result = Update-ProjectItemsStatusOnDueDate -IncludeDoneItems @params - # Assert - Assert-IsNull -Object $result - $staged = Get-ProjectItemStaged -Owner octodemo -ProjectNumber 625 - Assert-Count -Expected 6 -Presented $staged - Assert-Contains -Expected PVTI_lADOAlIw4c4A0Lf4zgYVsJc -Presented $staged.Keys -Comment "Done item " -} \ No newline at end of file diff --git a/Test/public/integrations/update-ProjectItemsWithIntegration.test.ps1 b/Test/public/integrations/update-ProjectItemsWithIntegration.test.ps1 index 1ab7be0..0eee1ad 100644 --- a/Test/public/integrations/update-ProjectItemsWithIntegration.test.ps1 +++ b/Test/public/integrations/update-ProjectItemsWithIntegration.test.ps1 @@ -3,57 +3,42 @@ function Test_UpdateProjectWithIntegration{ Reset-InvokeCommandMock Mock_DatabaseRoot - # https://github.com/orgs/octodemo/projects/625/views/1 - - $owner = "octodemo" - $projectNumber = "625" - # $itemId = "PVTI_lADOAlIw4c4A0Lf4zgYNTc0" - $fieldSlug = "sf_" - $IntegrationField = "sfUrl" - $IntegrationCommand = "Get-SfAccount" - - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName "invoke-GitHubOrgProjectWithFields-$owner-$projectNumber.2.json" + $mp = Get-Mock_Project_625 ; $owner = $mp.owner ; $projectNumber = $mp.number + Mockcall_GetProject -MockProject $mp + $p = $mp.updateWithIntegration - $data1 = @{ - "Text1" = "value11" - "Text2" = "value12" - "Text3" = "value13" - "Number1" = 11 - } + # https://github.com/orgs/octodemo/projects/625/views/1 - $data2 = @{ - "Text1" = "value21" - "Text2" = "value22" - "Text3" = "value23" - "Number1" = 22 - } + $fieldSlug = $p.fieldSlug + $integrationField = $p.integrationField + $integrationCommand = $p.integrationCommand - MockCallToObject -Command 'Get-SfAccount "https://some.com/1234/viuew"' -OutObject $data1 - MockCallToObject -Command 'Get-SfAccount "https://some.com/4321/viuew"' -OutObject $data2 + # Mock integration calles + MockCallToObject -Command $p.mockdata1.command -OutObject $p.mockdata1.data + MockCallToObject -Command $p.mockdata2.command -OutObject $p.mockdata2.data $param = @{ Owner = $owner ProjectNumber = $projectNumber - IntegrationField = $IntegrationField - IntegrationCommand = $IntegrationCommand + IntegrationField = $integrationField + IntegrationCommand = $integrationCommand Slug = $fieldSlug } + # Act $result = Update-ProjectItemsWithIntegration @param + # Result is null + Assert-IsNull -Object $result -Comment "Result is null" # Confirm that the changes are staged - $result = Get-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber - - Assert-Count -Expected 2 -Presented $result - - # PVTI_lADOAlIw4c4A0Lf4zgYNTc0 - Assert-AreEqual -Expected $($data1.Text1) -Presented $result.PVTI_lADOAlIw4c4A0Lf4zgYNTc0.PVTF_lADOAlIw4c4A0Lf4zgp2lxM.Value - Assert-AreEqual -Expected $($data1.Text2) -Presented $result.PVTI_lADOAlIw4c4A0Lf4zgYNTc0.PVTF_lADOAlIw4c4A0Lf4zgp2l3o.Value - Assert-AreEqual -Expected $($data1.Number1) -Presented $result.PVTI_lADOAlIw4c4A0Lf4zgYNTc0.PVTF_lADOAlIw4c4A0Lf4zgp2mBs.Value - - # PVTI_lADOAlIw4c4A0Lf4zgYNTxI - Assert-AreEqual -Expected $($data2.Text1) -Presented $result.PVTI_lADOAlIw4c4A0Lf4zgYNTxI.PVTF_lADOAlIw4c4A0Lf4zgp2lxM.Value - Assert-AreEqual -Expected $($data2.Text2) -Presented $result.PVTI_lADOAlIw4c4A0Lf4zgYNTxI.PVTF_lADOAlIw4c4A0Lf4zgp2l3o.Value - Assert-AreEqual -Expected $($data2.Number1) -Presented $result.PVTI_lADOAlIw4c4A0Lf4zgYNTxI.PVTF_lADOAlIw4c4A0Lf4zgp2mBs.Value + $staged = Get-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber + + # Items edited + Assert-AreEqual -Expected $p.staged.Count -Presented $staged.Count -Comment "Items staged" + foreach($id in $p.staged.Keys){ + foreach($field in $p.staged.$id.Keys){ + Assert-AreEqual -Expected $p.staged.$id.$field -Presented $staged.$id.$field.Value -Comment "Item $id Field $field" + } + } } \ No newline at end of file diff --git a/Test/public/interactive_test/getproject.test.ps1 b/Test/public/interactive_test/getproject.test.ps1 index 8e80675..d4ce89e 100644 --- a/Test/public/interactive_test/getproject.test.ps1 +++ b/Test/public/interactive_test/getproject.test.ps1 @@ -1,50 +1,39 @@ -function Test_Get_Project_ItemId_Equal_Case_Sensitive{ +function Test_Get_Project_ItemId_Equal_Case_Sensitive { Reset-InvokeCommandMock Mock_DatabaseRoot - # Project -Owner github -ProjectNumber 20521 has two items with the same Id case sensitive - # Mock the scenario for testing in 'projectV2.json' - $item1 ="PVTI_lADNJr_OALnx2s4Fqq8F" - $item2 ="PVTI_lADNJr_OALnx2s4Fqq8f" - $item3 = "PVTI_lADNJr_OALnx2s4Fqq8p" - $item4 = "PVTI_lADNJr_OALnx2s4Fqq8P" - # Testing that we can load this - - $Owner = "SomeOrg" ; $ProjectNumber = 164 + $p = Get-Mock_Project_700 ; $owner = $p.owner ; $projectNumber = $p.number - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + MockCall_GetProject_700_CaseSensitive + + # Project -Owner github -ProjectNumber 20521 has two items with the same Id case sensitive + # Forces project 700n with two item ids with case difference last leter of their id + $item1 = "PVTI_lADOAlIw4c4BCe3Vzgec8pU" + $item2 = "PVTI_lADOAlIw4c4BCe3Vzgec8pu" + # Act $result = Get-Project -owner $owner -ProjectNumber $ProjectNumber - Assert-Count -Expected 12 -Presented $result.items.keys + Assert-Count -Expected $p.items.totalCount -Presented $result.items.keys - $result1 = Get-ProjectItem -ItemId $item1 + $result1 = Get-ProjectItem -ItemId $item1 Assert-IsNotNull -Object $result1 Assert-AreEqual -Expected $result1.id -Presented $result.items.$item1.id - $result2 = Get-ProjectItem -ItemId $item2 + $result2 = Get-ProjectItem -ItemId $item2 Assert-IsNotNull -Object $result2 Assert-AreEqual -Expected $result2.id -Presented $result.items.$item2.id - $result3 = Get-ProjectItem -ItemId $item3 - Assert-IsNotNull -Object $result3 - Assert-AreEqual -Expected $result3.id -Presented $result.items.$item3.id - - $result4 = Get-ProjectItem -ItemId $item4 - Assert-IsNotNull -Object $result4 - Assert-AreEqual -Expected $result4.id -Presented $result.items.$item4.id - - } -function Test_Get_Project_ItemId_Equal_Case_Sensitive_2{ +function Test_Get_Project_ItemId_Equal_Case_Sensitive_2 { # Allthough this test pass this is not the case when # adding Items to a @{} - $item1 ="PVTI_lADNJr_OALnx2s4Fqq8F" - $item2 ="PVTI_lADNJr_OALnx2s4Fqq8f" - $item3 = "PVTI_lADNJr_OALnx2s4Fqq8p" - $item4 = "PVTI_lADNJr_OALnx2s4Fqq8P" + $item1 = "PVTI_lADNJr_OALnx2s4Fqq8F" # Ending Capital F + $item2 = "PVTI_lADNJr_OALnx2s4Fqq8f" # Ending Small f + $item3 = "PVTI_lADNJr_OALnx2s4Fqq8p" # Ending Small p + $item4 = "PVTI_lADNJr_OALnx2s4Fqq8P" # Ending Capital P # Testing that we can load this # By default hashTables are case insensitive in the name of the keys @@ -53,6 +42,8 @@ function Test_Get_Project_ItemId_Equal_Case_Sensitive_2{ $ht11 = @{} # Case insensitive by default + Assert-AreEqual -Expected "System.Collections.Hashtable"-Presented $($ht11.GetType().FullName) + $ht11[$item1] = "item1" $ht11[$item2] = "item2" $ht11[$item3] = "item3" @@ -65,6 +56,8 @@ function Test_Get_Project_ItemId_Equal_Case_Sensitive_2{ $ht21 = New-Object System.Collections.Hashtable # Case sensitive + Assert-AreEqual -Expected "System.Collections.Hashtable"-Presented $($ht21.GetType().FullName) + $ht21[$item1] = "item1" $ht21[$item2] = "item2" $ht21[$item3] = "item3" @@ -72,4 +65,117 @@ function Test_Get_Project_ItemId_Equal_Case_Sensitive_2{ $ht22 = @{} $ht22 += $ht22 + + +} + +function Test_Get_Project_ItemId_Equal_Case_Sensitive_4 { + # This should work using the private funciton + + Invoke-PrivateContext { + + $item1 = "PVTI_lADNJr_OALnx2s4Fqq8F" # Ending Capital F + $item2 = "PVTI_lADNJr_OALnx2s4Fqq8f" # Ending Small f + $item3 = "PVTI_lADNJr_OALnx2s4Fqq8p" # Ending Small p + $item4 = "PVTI_lADNJr_OALnx2s4Fqq8P" # Ending Capital P + + # Act + + $source = New-HashTable + $destination = New-HashTable + + $source[$item1] = "item1" + $source[$item2] = "item2" + $source[$item3] = "item3" + $source[$item4] = "item4" + + Assert-Count -Expected 4 -Presented $source.keys + + # This will throw. + # Wrong way to merge hashtables + $hasthrow = $false + try{ + $destination += $source + } catch { + $hasthrow = $true + } + Assert-IsTrue -Condition $hasthrow + + # Correct way to merge hashtables + + foreach($key in $source.keys) { + $destination.$key = $source[$key] + } + + Assert-Count -Expected 4 -Presented $destination.keys + } +} + + +function Test_Get_Project_ItemId_Equal_Case_Sensitive_3{ + + # Test 1 - Wrong. We lose items + + $source = @{} + $destination = @{} + + $source.kk = "value" + $source.Kk = "value" + + Assert-Count -Expected 1 -Presented $source.keys + + $destination += $source + + Assert-Count -Expected 1 -Presented $destination.keys + + # Test 2 - Wrong we throw on merges + + $source = New-Object System.Collections.Hashtable + $destination = New-Object System.Collections.Hashtable + + $source.kk = "value" + $source.Kk = "value" + + Assert-Count -Expected 2 -Presented $source.keys + + $hasthrow = $false + try{ + $destination += $source + } catch { + $hasthrow = $true + } + Assert-IsTrue -Condition $hasthrow + + + # Test 3 - Wrong we throw on merges + + $source = New-Object System.Collections.Hashtable + $destination = @{} + + $source.kk = "value" + $source.Kk = "value" + Assert-Count -Expected 2 -Presented $source.keys + + $hasthrow = $false + try{ + $destination += $source + } catch { + $hasthrow = $true + } + Assert-IsTrue -Condition $hasthrow + + # Test 4 - Wrong we lose items + + $source = @{} + $destination = New-Object System.Collections.Hashtable + + $source.kk = "value" + $source.Kk = "value" + + Assert-Count -Expected 1 -Presented $source.keys + + $destination += $source + + Assert-Count -Expected 1 -Presented $destination.keys + } \ No newline at end of file diff --git a/Test/public/issues/Add-IssueComment.test.ps1 b/Test/public/issues/Add-IssueComment.test.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Test/public/issues/Add-IssuePullRequestComment.test.ps1 b/Test/public/issues/Add-IssuePullRequestComment.test.ps1 new file mode 100644 index 0000000..d6a4d62 --- /dev/null +++ b/Test/public/issues/Add-IssuePullRequestComment.test.ps1 @@ -0,0 +1,77 @@ + +function Test_AddIssueComment_SUCCESS_Using_Cache{ + Reset-InvokeCommandMock + Mock_DatabaseRoot + + $p = Get-Mock_Project_700 ; $owner = $p.Owner ; $projectNumber = $p.Number + $i = $p.Issue + $contentId = $i.contentId + $comment = "sample comment 1" + + MockCall_GetProject_700 + + Set-ProjectHelperEnvironment -Owner $owner -ProjectNumber $projectNumber -DisplayFields @("Status","FieldText") + + MockCallJson -Command "Invoke-AddIssueComment -SubjectId $contentId -Comment ""$comment""" -FileName "invoke-addissuecomment-$contentId.json" + + #Act + $result = Add-IssuePullRequestCommentDirect -ItemId $i.id -Comment $comment + + # $result is a valid url + Assert-IsUrl -Presented $result +} + +function Test_AddIssueComment_SUCCESS_Using_Direct{ + Reset-InvokeCommandMock + Mock_DatabaseRoot + + $p = Get-Mock_Project_700 + $i = $p.Issue + $id = $i.id + $contentId = $i.contentId + $comment = "sample comment 1" + + MockCallJson -Command "Invoke-GetItem -ItemId $id" -FileName "invoke-getitem-$id.json" + + MockCallJson -Command "Invoke-AddIssueComment -SubjectId $contentId -Comment ""$comment""" -FileName "invoke-addissuecomment-$contentId.json" + + #Act + $result = Add-IssuePullRequestCommentDirect -ItemId $i.id -Comment $comment + + # $result is a valid url + Assert-IsUrl -Presented $result +} + +function Test_AddIssueComment_SUCCESS_Using_Direct_PR{ + Reset-InvokeCommandMock + Mock_DatabaseRoot + + $p = Get-Mock_Project_700 + $i = $p.PullRequest + $id = $i.id + $contentId = $i.contentId + $comment = "sample comment 1" + + MockCallJson -Command "Invoke-GetItem -ItemId $id" -FileName "invoke-getitem-$id.json" + + MockCallJson -Command "Invoke-AddIssueComment -SubjectId $contentId -Comment ""$comment""" -FileName "invoke-addissuecomment-$contentId.json" + + #Act + $result = Add-IssuePullRequestCommentDirect -ItemId $i.id -Comment $comment + + # $result is a valid url + Assert-IsUrl -Presented $result +} + + +function Assert-IsUrl{ + param( + [string]$Presented + ) + + # Determine if the presented string is a valid absolute http/https URL + $uri = $null + $isUrl = [System.Uri]::TryCreate($Presented, [System.UriKind]::Absolute, [ref]$uri) -and $null -ne $uri -and $uri.Scheme -in @('http','https') + + Assert-IsTrue -Condition $isUrl +} \ No newline at end of file diff --git a/Test/public/projectDatabase/project_database.test.ps1 b/Test/public/projectDatabase/project_database.test.ps1 index f9052f7..63cd51f 100644 --- a/Test/public/projectDatabase/project_database.test.ps1 +++ b/Test/public/projectDatabase/project_database.test.ps1 @@ -4,15 +4,11 @@ function Test_SaveProjectDatabase_SafeId_Flag_PrivateCall{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' - - # Call on private mode - $modulePath = $MODULE_PATH | Split-Path -Parent - $module = Import-Module -Name $modulePath -PassThru + $p = Get-Mock_Project_700 ; $owner = $p.owner ; $projectNumber = $p.number + MockCall_GetProject_700 - & $module { - $Owner = "SomeOrg" ; $ProjectNumber = 164 + Invoke-PrivateContext { + $Owner = "octodemo" ; $ProjectNumber = 700 # Cache the project $prj = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber @@ -36,16 +32,12 @@ function Test_SaveProjectDatabase_Safe_PrivateCall{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' - - # Call on private mode - $modulePath = $MODULE_PATH | Split-Path -Parent - $module = Import-Module -Name $modulePath -PassThru + $p = Get-Mock_Project_700 ; $owner = $p.owner ; $projectNumber = $p.number + MockCall_GetProject_700 - & $module { + Invoke-PrivateContext { - $Owner = "SomeOrg" ; $ProjectNumber = 164 + $Owner = "octodemo" ; $ProjectNumber = 700 # Cache the project $prj1 = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber diff --git a/Test/public/projectDatabase/project_database_fields.test.ps1 b/Test/public/projectDatabase/project_database_fields.test.ps1 new file mode 100644 index 0000000..b577381 --- /dev/null +++ b/Test/public/projectDatabase/project_database_fields.test.ps1 @@ -0,0 +1,37 @@ +function Test_ConvertTo_Number_Valid { + + Invoke-PrivateContext { + + # Equal to 10.1 + "10.1", "10,1" | ForEach-Object { + Assert-AreEqual -Expected 10.1 -Presented $(ConvertTo-Number $_) + } + + # Equal to 1000.1 + "1,000.1", "1.000,1" | ForEach-Object { + Assert-AreEqual -Expected 1000.1 -Presented $(ConvertTo-Number $_) + } + } +} + +function Test_ConvertTo_Number_NotValid{ + + Invoke-PrivateContext { + + # Not a number + "abc", "10..1", "10,1.5", "10,1,5","1.000.1","1,000,1" | ForEach-Object { + $hasThrow = $false + try { + ConvertTo-Number $_ | Out-Null + } catch { + $hasThrow = $true + } finally{ + if(-not $hasThrow){ + Write-Host "Value '$_' was incorrectly parsed as a number" + } + } + Assert-IsTrue -Condition $hasThrow -Comment "Should throw as the value is not a number" + } + } + +} \ No newline at end of file diff --git a/Test/public/project_item.test.ps1 b/Test/public/project_item.test.ps1 index d914185..7666537 100644 --- a/Test/public/project_item.test.ps1 +++ b/Test/public/project_item.test.ps1 @@ -3,22 +3,23 @@ function Test_GetProjectItem_SUCCESS{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 ; - # $itemsCount = 12 ; $fieldsCount = 18 - $fieldComment = "Comment" ; $fieldTitle = "Title" + $p = Get-Mock_Project_700 ; $Owner = $p.owner ; $projectNumber = $p.number + $i = $p.draftissue + $f = $p.fieldtext + $fieldComment = $f.name ; $fieldTitle = "Title" - $itemId = "PVTI_lADOBCrGTM4ActQazgMuXXc" - $projectFieldTitleValue = "A draft in the project" - $projectFieldCommentValue = "This" - $itemFieldTitleValue = "kk text" - $itemFieldCommentValue = "comment for draft 1" + $itemId = $i.id + $projectFieldTitleValue = $i.title + $projectFieldCommentValue = $i.fieldtext + $itemFieldTitleValue = $projectFieldTitleValue + " updated" + $itemFieldCommentValue = $projectFieldCommentValue + " updated" # allow get project - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + MockCall_GetProject_700 # Even if id is in project we make a direct call when with Force - MockCallJson -Command "Invoke-GetItem -itemid $itemId" -FileName "invoke-getitem-$itemId.json" + MockCallJson -Command "Invoke-GetItem -itemid $itemId" -FileName "invoke-getitem-$itemId-updated.json" # Act get value from project $result = Get-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -ItemId $itemId @@ -53,22 +54,23 @@ function Test_EditProjetItems_SUCCESS{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 ; + $Owner = "octodemo" ; $ProjectNumber = 700 ; #$itemsCount = 12 ; $fieldsCount = 18 - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + # MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'invoke-GitHubOrgProjectWithFields-octodemo-700.json' $before = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber # Item id 10 # $title = "A draft in the project" - $itemId = "PVTI_lADOBCrGTM4ActQazgMuXXc" + $itemId = "PVTI_lADOAlIw4c4BCe3Vzgeio4o" # $title_fieldid= "PVTF_lADOBCrGTM4ActQazgSkYm8" $title_fieldid= "title" - $comment_fieldid = "PVTF_lADOBCrGTM4ActQazgSl5GU" + $comment_fieldid = "PVTF_lADOAlIw4c4BCe3Vzg0rhko" - $fieldComment = "Comment" ; $fieldCommentValue = "new value of the comment 10.1" ; $fieldCommentValue_Before = $before.items.$itemId.$fieldComment + $fieldComment = "field-text" ; $fieldCommentValue = "new value of the comment 10.1" ; $fieldCommentValue_Before = $before.items.$itemId.$fieldComment $fieldTitle = "Title" ; $fieldTitleValue = "new value of the title 10.1" ; $fieldTitleValue_Before = $before.items.$itemId.$fieldTitle # Act @@ -91,19 +93,19 @@ function Test_EditProjetItems_SUCCESS{ Assert-Contains -Expected $itemId -Presented $result.Keys Assert-Count -Expected 2 -Presented $result.$itemId - Assert-AreEqual -Expected "Comment" -Presented $result.$itemId.$comment_fieldid.Field.name - Assert-AreEqual -Expected "Title" -Presented $result.$itemId.$title_fieldid.Field.name + Assert-AreEqual -Expected $fieldComment -Presented $result.$itemId.$comment_fieldid.Field.name + Assert-AreEqual -Expected $fieldTitle -Presented $result.$itemId.$title_fieldid.Field.name Assert-AreEqual -Expected $fieldCommentValue -Presented $result.$itemId.$comment_fieldid.Value Assert-AreEqual -Expected $fieldTitleValue -Presented $result.$itemId.$title_fieldid.Value } -function Test_EditProejctItems_SameValue{ +function Test_EditProjectItems_SameValue{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + $Owner = "octodemo" ; $ProjectNumber = 700 + MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'invoke-GitHubOrgProjectWithFields-octodemo-700.json' $prj = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber #$actualtitle = $prj.items.$itemId."Title" @@ -111,11 +113,11 @@ function Test_EditProejctItems_SameValue{ # Item id 10 # $title = "A draft in the project" - $itemId = "PVTI_lADOBCrGTM4ActQazgMuXXc" + $itemId = "PVTI_lADOAlIw4c4BCe3Vzgeio4o" #$title_fieldid= "PVTF_lADOBCrGTM4ActQazgSkYm8" #$comment_fieldid = "PVTF_lADOBCrGTM4ActQazgSl5GU" - $fieldComment = "Comment" ; $fieldCommentValue = $prj.items.$itemId."Comment" + $fieldComment = "field-text" ; $fieldCommentValue = $prj.items.$itemId."field-text" $fieldTitle = "Title" ; $fieldTitleValue = $prj.items.$itemId."Title" Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId $fieldComment $fieldCommentValue @@ -126,71 +128,18 @@ function Test_EditProejctItems_SameValue{ Assert-Count -Expected 0 -Presented $result.Keys } -function Test_EditProejctItems_NumberDecimals{ - Reset-InvokeCommandMock - Mock_DatabaseRoot - - $Owner = "SomeOrg" ; $ProjectNumber = 164 - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2-skipitems.json' -SkipItems - - # $prj = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber - - $itemId = "PVTI_lADOBCrGTM4ActQazgMuXXc" - - # Mock the direct call for item - MockCallJson -Command "Invoke-GetItem -itemid $itemId" -FileName "invoke-getitem-$itemId.json" - - # [DBG]:> $prj.fields.PVTF_lADOBCrGTM4ActQazgSkglc - # Name Value - # ---- ----- - # dataType NUMBER - # id PVTF_lADOBCrGTM4ActQazgSkglc - # type ProjectV2Field - # name TimeTracker - - $fieldNumber = "TimeTracker" - - "NotANumber","1.000.1","1,000,1" | ForEach-Object{ - - # Not a valid value - $hasThrow= $false - try { - Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId $fieldNumber "NotNumber" - } - catch { - $hasThrow = $true - } - Assert-IsTrue -Condition $hasThrow -Comment "Should throw as the value is not a number" - } - - "10.1","10,1" | ForEach-Object { - Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId $fieldNumber $_ - $result = Get-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber - Assert-Count -Expected 1 -Presented $result.Keys - Assert-AreEqual -Expected 10.1 -Presented $result.$itemId.PVTF_lADOBCrGTM4ActQazgSkglc.Value - Reset-ProjectItemStaged - } - - "1,000.1","1.000,1" | ForEach-Object { - Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId $fieldNumber $_ - $result = Get-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber - Assert-Count -Expected 1 -Presented $result.Keys - Assert-AreEqual -Expected 1000.1 -Presented $result.$itemId.PVTF_lADOBCrGTM4ActQazgSkglc.Value - Reset-ProjectItemStaged - } -} - -function Test_EditProejctItems_Direct{ +function Test_EditProjectItems_Direct{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 + $Owner = "octodemo" ; $ProjectNumber = 700 # No sync of project with items allowed just with skipitems - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2-skipitems.json' -skipitems + MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'invoke-GitHubOrgProjectWithFields-octodemo-700-skipitems.json' -skipitems - $itemId = "PVTI_lADOBCrGTM4ActQazgMuXXc" - $fieldComment = "Comment" ; $fieldCommentValue = "new value of the comment 10.1" + $itemId = "PVTI_lADOAlIw4c4BCe3Vzgeio4o" + $fieldComment = "field-text" ; $fieldCommentValue = "new value of the comment 10.1" + $fieldId = "PVTF_lADOAlIw4c4BCe3Vzg0rhko" # Mock the direct call for item MockCallJson -Command "Invoke-GetItem -itemid $itemId" -FileName "invoke-getitem-$itemId.json" @@ -203,8 +152,8 @@ function Test_EditProejctItems_Direct{ Assert-Count -Expected 1 -Presented $result.Keys Assert-AreEqual -Expected $itemId -Presented $result.Keys[0] - Assert-AreEqual -Expected "Comment" -Presented $result.$itemId.PVTF_lADOBCrGTM4ActQazgSl5GU.Field.name - Assert-AreEqual -Expected $fieldCommentValue -Presented $result.$itemId.PVTF_lADOBCrGTM4ActQazgSl5GU.Value + Assert-AreEqual -Expected $fieldComment -Presented $result.$itemId.$fieldId.Field.name + Assert-AreEqual -Expected $fieldCommentValue -Presented $result.$itemId.$fieldId.Value } @@ -215,20 +164,22 @@ function Test_UpdateProjectDatabase_Fail_With_Staged{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 ; $itemsCount = 12 ; - $itemId = "PVTI_lADOBCrGTM4ActQazgMueM4" - $fieldComment = "comment" ; $fieldCommentValue = "new value of the comment 10.1" - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + MockCall_GetProject_700 + $p = Get-Mock_Project_700 ; $Owner = $p.Owner ; $ProjectNumber = $p.Number + $itemsCount = $p.items.totalCount + $itemId = $p.issue.Id + $fieldComment = "field-text" ; $fieldCommentValue = "new value of the comment 10.1" + MockCall_GetItem -ItemId $itemId - # Calling Get-ProjectItemList with Force to trigger update-projectdatabase that should fail as their are - # staged changes not yet synced to remote. + # Act empty as their is nothing staged yet $result = Get-ProjectItemList -Owner $Owner -ProjectNumber $ProjectNumber -Force Assert-Count -Expected $itemsCount -Presented $result - $result = Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId $fieldComment $fieldCommentValue - Assert-IsNull -Object $result + # arrange modifyt to add staged + Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId $fieldComment $fieldCommentValue + # Act get staged. 1 modified $result = Get-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber Assert-Count -Expected 1 -Presented $result.Keys Assert-AreEqual -Expected $itemId -Presented $result.Keys @@ -251,94 +202,27 @@ function Test_UpdateProjectDatabase_Fail_With_Staged{ } -function Test_FindProjectItem_SUCCESS{ - Reset-InvokeCommandMock - Mock_DatabaseRoot - - $Owner = "SomeOrg" ; $ProjectNumber = 164 ; - #$itemsCount = 12 ; $fieldsCount = 18 - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' - - $title = "Issue 455d29e3" - $itemId1 = "PVTI_lADNJr_OALnx2s4Fqq8f" - $itemId2 = "PVTI_lADNJr_OALnx2s4Fqq8p" - $subtitle = $title.Substring(4,4) - $subtitle - - # Item not found - $result = Find-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -Title "No item with this title" - Assert-IsNull -Object $result - - # Several items with similar title - $result = Find-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -Title "*$subtitle*" -IncludeDone - Assert-Count -Expected 2 -Presented $result - Assert-Contains -Expected $itemId1 -Presented $result.id - Assert-Contains -Expected $itemId2 -Presented $result.id - - # Not Match - $result = Find-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -Title $title -Match - Assert-IsNull -Object $result - - # Match - $result = Find-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -Title "$title 1" -Match - Assert-Count -Expected 1 -Presented $result - Assert-Contains -Expected $itemId1 -Presented $result.id -} - -function Test_FindProjectItem_FAIL{ - - Reset-InvokeCommandMock - Mock_DatabaseRoot - - $Owner = "SomeOrg" ; $ProjectNumber = 164 - $erroMessage= "Error: Project not found. Check owner and projectnumber" - - Mock_DatabaseRoot - - MockCall_GitHubOrgProjectWithFields_Null -Owner $owner -ProjectNumber $projectNumber - - # Run the command - Start-MyTranscript - $result = Find-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -Title "no title" - $tt = Stop-MyTranscript - - Assert-IsNull -Object $result - Assert-Contains -Expected $erroMessage -Presented $tt -} function Test_SearchProjectItem_SUCCESS{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 - - # title refrence with differnt case and spaces - $filter = "epic" - - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + MockCall_GetProject_700 - # Act 1 - $result = Search-ProjectItem -Owner $owner -ProjectNumber $projectNumber -Filter $filter -IncludeDone + $p = Get-Mock_Project_700; $owner = $p.owner ; $projectNumber = $p.number - Assert-Count -Expected 2 -Presented $result - - Assert-Contains -Expected "EPIC 1 " -Presented $result.Title - Assert-Contains -Expected "PVTI_lADOBCrGTM4ActQazgMtRO0" -Presented $result.id - Assert-Contains -Expected "EPIC 2" -Presented $result.Title - Assert-Contains -Expected "PVTI_lADOBCrGTM4ActQazgMtRPg" -Presented $result.id - # Act 2 - $result = Search-ProjectItem 68 -IncludeDone # TimeTracker value 684 + "development", "96", "rulasg-dev-1" | ForEach-Object{ - Assert-Count -Expected 1 -Presented $result - Assert-AreEqual -Expected "Issue 455d29e3 2" -Presented $result[0].Title - Assert-AreEqual -Expected "PVTI_lADNJr_OALnx2s4Fqq8p" -Presented $result[0].id - - # Act 3 - $result = Search-ProjectItem "ProjectDemoTest-repo-front" - Assert-Count -Expected 5 -Presented $result + $result = Search-ProjectItem -Owner $owner -ProjectNumber $projectNumber -Filter $_ -IncludeDone + Assert-Count -Expected $p.searchInAnyField.$_.totalCount -Presented $result + + foreach ($r in $result) { + Assert-Contains -Expected $r.Title -Presented $p.searchInAnyField.$_.Titles + } + } } function Test_GetItemDirect_SUCCESS{ @@ -349,7 +233,7 @@ function Test_GetItemDirect_SUCCESS{ $itemUrl = "https://github.com/github/sales/issues/11742" $contentId ="I_kwDOAFbrpM6s_fNK" - MockCallJson -Command "Invoke-GetItem -itemid $itemId" -FileName 'getitemdirect_1.json' + MockCallJson -Command "Invoke-GetItem -itemid $itemId" -FileName "invoke-getitem-$itemId.json" $result = Get-ProjectItemDirect -ItemId $itemId @@ -364,16 +248,18 @@ function Test_ShowProjectItem_SUCCESS{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 - $id = "PVTI_lADOBCrGTM4ActQazgMtRO0" - $title = "EPIC 1 " - $status = "Todo" + MockCall_GetProject_700 + + $p = Get-Mock_Project_700; $Owner = "octodemo" ; $ProjectNumber = 700 + + $i = $p.issue + $id = $i.Id + $title = $i.title + $status = $i.status # title refrence with differnt case and spaces - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' - - $item = Find-ProjectItem -Owner $owner -ProjectNumber $projectNumber -Title $title -Match + $item = Get-ProjectItem -Owner $owner -ProjectNumber $projectNumber -ItemId $id $result = $item | Show-ProjectItem -AdditionalFields "Status" @@ -382,7 +268,6 @@ function Test_ShowProjectItem_SUCCESS{ Assert-AreEqual -Expected $id -Presented $result[0].id Assert-AreEqual -Expected $title -Presented $result[0].Title Assert-AreEqual -Expected $status -Presented $result[0].Status - } function Test_ShowProjectItem_SUCCESS_Multiple{ @@ -390,19 +275,17 @@ function Test_ShowProjectItem_SUCCESS_Multiple{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 + MockCall_GetProject_700 + $p = Get-Mock_Project_700; $Owner = $p.owner; $ProjectNumber = $p.number - - # title refrence with differnt case and spaces - - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' - - $items = Search-ProjectItem -Owner $owner -ProjectNumber $projectNumber -Filter "Issue*" + # Arrange - get a few items using search-projectitem + $items = Search-ProjectItemByTitle -Owner $owner -ProjectNumber $projectNumber -Title $p.searchInTitle.titleFilter + $itemsCount = $items.Count + Assert-Count -Expected $p.searchInTitle.totalCount -Presented $items $result = $items | Show-ProjectItem -AdditionalFields "Status" - - Assert-Count -Expected 8 -Presented $Items - Assert-Count -Expected 8 -Presented $result + + Assert-Count -Expected $itemsCount -Presented $result # Get properties of the first item to verify $expectedProperties = @("id", "Title", "Status") diff --git a/Test/public/project_item_list.test.ps1 b/Test/public/project_item_list.test.ps1 index 748c32a..763c489 100644 --- a/Test/public/project_item_list.test.ps1 +++ b/Test/public/project_item_list.test.ps1 @@ -3,29 +3,41 @@ function Test_GetProjetItemList_SUCCESS{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 ; $itemsCount = 12 + $p = Get-Mock_Project_700 ; $owner = $p.owner ; $projectNumber = $p.number + $i = $p.issue + $itemsCount = $p.items.totalCount - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + MockCall_GetProject_700 + # Act $result = Get-ProjectItemList -Owner $Owner -ProjectNumber $ProjectNumber Assert-Count -Expected $itemsCount -Presented $result - $randomItem = $result.PVTI_lADOBCrGTM4ActQazgMuXXc - - # Item 10 - Chose one at random - Assert-AreEqual -Presented $randomItem.UserStories -Expected "8" - Assert-AreEqual -Presented $randomItem.body -Expected "some content in body" - Assert-AreEqual -Presented $randomItem.Comment -Expected "This" - Assert-AreEqual -Presented $randomItem.Title -Expected "A draft in the project" - Assert-AreEqual -Presented $randomItem.state -Expected "CLOSED" - Assert-AreEqual -Presented $randomItem.id -Expected "PVTI_lADOBCrGTM4ActQazgMuXXc" - Assert-AreEqual -Presented $randomItem.type -Expected "DraftIssue" - Assert-AreEqual -Presented $randomItem.TimeTracker -Expected "890" - Assert-AreEqual -Presented $randomItem.Severity -Expected "Nice⭐️" + $randomItem = $result.$($i.Id) + + Assert-AreEqual -Presented $randomItem.Title -Expected "Issue for development" + Assert-AreEqual -Presented $randomItem.body -Expected "Body of issue for development" + Assert-AreEqual -Presented $randomItem.state -Expected "OPEN" + Assert-AreEqual -Presented $randomItem.id -Expected "PVTI_lADOAlIw4c4BCe3Vzgeio4o" + Assert-AreEqual -Presented $randomItem.type -Expected "Issue" Assert-AreEqual -Presented $randomItem.Status -Expected "Todo" - Assert-AreEqual -Presented $randomItem.Priority -Expected "🥵High" - Assert-AreEqual -Presented $randomItem.Assignees -Expected "rulasg" + Assert-AreEqual -Presented $randomItem.Milestone -Expected "Milestone 3: Quality and Deployment" + Assert-AreEqual -Presented $randomItem.Repository -Expected "https://github.com/octodemo/rulasg-dev-1" + Assert-AreEqual -Presented $randomItem."field-text" -Expected "text3" + Assert-AreEqual -Presented $randomItem."field-number" -Expected "333" + Assert-AreEqual -Presented $randomItem."field-singleselect" -Expected "option-3" + Assert-AreEqual -Presented $randomItem.databaseId -Expected "128099210" + Assert-AreEqual -Presented $randomItem.projectUrl -Expected "https://github.com/orgs/octodemo/projects/700" + # Assert-AreEqual -Presented $randomItem.updatedAt -Expected "9/11/2025 1:06:24 PM" + Assert-AreEqual -Presented $randomItem.number -Expected "26" + Assert-AreEqual -Presented $randomItem.contentId -Expected "I_kwDOPrRnkc7KkwSq" + Assert-AreEqual -Presented $randomItem.projectId -Expected "PVT_kwDOAlIw4c4BCe3V" + Assert-AreEqual -Presented $randomItem.urlContent -Expected "https://github.com/octodemo/rulasg-dev-1/issues/26" + Assert-AreEqual -Presented $randomItem.urlPanel -Expected "https://github.com/orgs/octodemo/projects/700/views/1?pane=issue&itemId=128099210" + Assert-AreEqual -Presented $randomItem.url -Expected "https://github.com/octodemo/rulasg-dev-1/issues/26" + # Assert-AreEqual -Presented $randomItem.createdAt -Expected "9/9/2025 2:01:17 PM" + # Assert-AreEqual -Presented $randomItem."field-iteration" -Expected "" # Reset all mock invokes Reset-InvokeCommandMock @@ -67,8 +79,12 @@ function Test_ProjectItemList_ExcludeDone{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 ; $itemsCount = 12 ; $itemsDone = 3 - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + $p = Get-Mock_Project_700 ; $owner = $p.owner ; $projectNumber = $p.number + $i = $p.issue + $itemsCount = $p.items.totalCount + $itemsDone = $p.items.doneCount + + MockCall_GetProject_700 $result = Get-ProjectItemList -Owner $Owner -ProjectNumber $ProjectNumber @@ -79,91 +95,26 @@ function Test_ProjectItemList_ExcludeDone{ Assert-AreEqual -Expected ($itemsCount - $itemsDone) -Presented $result.Keys.Count } -function Test_FindProjectItemByTitle_SUCCESS{ - - Reset-InvokeCommandMock - Mock_DatabaseRoot - - $Owner = "SomeOrg" ; $ProjectNumber = 164 ; $id = "PVTI_lADOBCrGTM4ActQazgMtRO0" - - # title refrence with differnt case and spaces - $title = "epic 1" - $actual = "EPIC 1 " - - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' - - $result = Find-ProjectItemByTitle -Owner $owner -ProjectNumber $projectNumber -Title $title - - Assert-AreEqual -Expected $id -Presented $result.id - Assert-AreEqual -Expected $actual -Presented $result.Title -} - -function Test_FindProjectItemByTitle_SUCCESS_MultipleResults{ - - Reset-InvokeCommandMock - Mock_DatabaseRoot - - $Owner = "SomeOrg" ; $ProjectNumber = 164 ; - $id1 = "PVTI_lADNJr_OALnx2s4Fqq8P" - $id2 = "PVTI_lADOBCrGTM4ActQazgMtRPA" - - # title refrence with differnt case and spaces - $title = "issue name 1" - $title1 = "Issue Name 1" - $title2 = "ISSUE NAME 1" - - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' - - $result = Find-ProjectItemByTitle -Owner $owner -ProjectNumber $projectNumber -Title $title -Force - - Assert-Count -Expected 2 -Presented $result - Assert-Contains -Expected $id1 -Presented $result.id - Assert-Contains -Expected $id2 -Presented $result.id - Assert-AreEqual -Expected $title1 -Presented $result[0].Title - Assert-AreEqual -Expected $title2 -Presented $result[1].Title -} - -function Test_FindProjectItemByTitle_FAIL{ - - Reset-InvokeCommandMock - Mock_DatabaseRoot - - $Owner = "SomeOrg" ; $ProjectNumber = 164 - - MockCall_GitHubOrgProjectWithFields_Null -Owner $owner -ProjectNumber $projectNumber - - # Run the command - Start-MyTranscript - $result = Find-ProjectItemByTitle -Owner $Owner -ProjectNumber $ProjectNumber - $tt = Stop-MyTranscript - - # Capture the standard output - $erroMessage1= "Error: Project not found. Check owner and projectnumber" - - Assert-IsNull -Object $result - Assert-Contains -Expected $erroMessage1 -Presented $tt -} - function Test_SearchProjectItemByTitle_SUCCESS{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 ; $id = "PVTI_lADOBCrGTM4ActQazgMtRO0" + MockCall_GetProject_700 - # title refrence with differnt case and spaces - $title = "epic" + $p = Get-Mock_Project_700 ; $owner = $p.owner ; $projectNumber = $p.number - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + # title refrence with differnt case and spaces + $title = $p.searchInTitle.titleFilter + # Act $result = Search-ProjectItemByTitle -Owner $owner -ProjectNumber $projectNumber -Title $title - Assert-Count -Expected 2 -Presented $result + Assert-Count -Expected $p.searchInTitle.totalCount -Presented $result - Assert-Contains -Expected "EPIC 1 " -Presented $result.Title - Assert-Contains -Expected "PVTI_lADOBCrGTM4ActQazgMtRO0" -Presented $result.id - Assert-Contains -Expected "EPIC 2" -Presented $result.Title - Assert-Contains -Expected "PVTI_lADOBCrGTM4ActQazgMtRPg" -Presented $result.id + $p.searchInTitle.Titles | ForEach-Object { + Assert-Contains -Expected $_ -Presented $result.Title + } } diff --git a/Test/public/project_items_staged.test.ps1 b/Test/public/project_items_staged.test.ps1 index 793ddbd..61601ac 100644 --- a/Test/public/project_items_staged.test.ps1 +++ b/Test/public/project_items_staged.test.ps1 @@ -15,8 +15,223 @@ function Test_SyncProjectItemsStaged_NoStaged { Assert-IsNull -Object $result } +function Test_SyncProjectItemsStaged_SUCCESS_Number{ + Reset-InvokeCommandMock + Mock_DatabaseRoot + + $Owner = "octodemo" ; $ProjectNumber = 700 + $projectId = "PVT_kwDOAlIw4c4BCe3V" + + # project item issue + $itemId1 = "PVTI_lADOAlIw4c4BCe3VzgeioBY" + $contentId1 = "I_kwDOPrRnkc7KkwSq" + + $fieldName = "field-number" + $fieldBeforeValueNumber = 111.0 + $fieldValue = "10,1" + $fieldValueToUpdate = "10.1" + $fieldId = "PVTF_lADOAlIw4c4BCe3Vzg0rhjU" + $type = "number" + + $mockItems = @( + @{ + ItemId = $itemId1 + FieldId = $fieldId + Value = $fieldValueToUpdate + ResultFile = "invoke-GitHubUpdateItemValue-$itemId1-$fieldId.json" + } + ) + + # Loop through the array and set the mock commands + foreach ($item in $mockItems) { + $command = 'Invoke-GitHubUpdateItemValues -ProjectId {ProjectId} -ItemId {ItemId} -FieldId {FieldId} -Value "{Value}" -Type {Type}' + $command = $command -replace '{ProjectId}', $projectId + $command = $command -replace '{ItemId}', $item.ItemId + $command = $command -replace '{FieldId}', $item.FieldId + $command = $command -replace '{Value}', $item.Value + $command = $command -replace '{Type}', $type + + Set-InvokeCommandMock -Command "Get-MockFileContentJson -filename $($item.ResultFile)" -Alias $command + } + + # Mock get-project + # MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'invoke-GitHubOrgProjectWithFields-octodemo-700-skipitems.json' -skipItems + MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'invoke-GitHubOrgProjectWithFields-octodemo-700.json' + $null = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber -Force + + Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId1 $fieldName $fieldValue + + # Act- Staged + # Check that show shows the display value + $staged = Get-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber + Assert-AreEqual -Expected $fieldValue -Presented $staged.$itemId1.$fieldId.Value + + $showStaged = Show-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber | Show-ProjectItemStaged + Assert-AreEqual -Expected $fieldValue -Presented $showStaged.$fieldName.Value + Assert-AreEqual -Expected $fieldBeforeValueNumber -Presented $showStaged.$fieldName.Before + + # Act - Sync + $result = Sync-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber + + # Assert + Assert-IsTrue -Condition $result + + $staged = Get-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber + Assert-Count -Expected 0 -Presented $staged + + $item1 = Get-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -ItemId $itemId1 + Assert-AreEqual -Expected $fieldValue -Presented $item1.$fieldName +} + +function Test_SyncProjectItemsStaged_SUCCESS_Date{ + Reset-InvokeCommandMock + Mock_DatabaseRoot + + $Owner = "octodemo" ; $ProjectNumber = 700 + $projectId = "PVT_kwDOAlIw4c4BCe3V" + + # project item issue + $itemId1 = "PVTI_lADOAlIw4c4BCe3VzgeioBY" + $contentId1 = "I_kwDOPrRnkc7KkwSq" + + $fieldName = "field-date" + $fieldBeforeValueDate = "2025-09-01" + $fieldValue = "2021-01-10" + $fieldValueToUpdate = "2021-01-10" + $fieldId = "PVTF_lADOAlIw4c4BCe3Vzg0rhlU" + $type = "date" + + $mockItems = @( + @{ + ItemId = $itemId1 + FieldId = $fieldId + Value = $fieldValueToUpdate + ResultFile = "invoke-GitHubUpdateItemValue-$itemId1-$fieldId.json" + } + ) + + # Loop through the array and set the mock commands + foreach ($item in $mockItems) { + $command = 'Invoke-GitHubUpdateItemValues -ProjectId {ProjectId} -ItemId {ItemId} -FieldId {FieldId} -Value "{Value}" -Type {Type}' + $command = $command -replace '{ProjectId}', $projectId + $command = $command -replace '{ItemId}', $item.ItemId + $command = $command -replace '{FieldId}', $item.FieldId + $command = $command -replace '{Value}', $item.Value + $command = $command -replace '{Type}', $type + + Set-InvokeCommandMock -Command "Get-MockFileContentJson -filename $($item.ResultFile)" -Alias $command + } + + # Mock get-project + MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'invoke-GitHubOrgProjectWithFields-octodemo-700.json' + $null = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber + + Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId1 $fieldName $fieldValue + + # Act- Staged + # Check that show shows the display value + $staged = Get-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber + Assert-AreEqual -Expected $fieldValue -Presented $staged.$itemId1.$fieldId.Value + + $showStaged = Show-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber | Show-ProjectItemStaged + Assert-AreEqual -Expected $fieldValue -Presented $showStaged.$fieldName.Value + Assert-AreEqual -Expected $fieldBeforeValueDate -Presented $showStaged.$fieldName.Before + + # Act - Sync + $result = Sync-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber + + # Assert + Assert-IsTrue -Condition $result + $staged = Get-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber + Assert-Count -Expected 0 -Presented $staged + $item1 = Get-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -ItemId $itemId1 + Assert-AreEqual -Expected $fieldValueToUpdate -Presented $item1.$fieldName +} + +function Test_SyncProjectItemsStaged_SUCCESS_SingleSelect{ + Reset-InvokeCommandMock + Mock_DatabaseRoot + + $p = Get-Mock_Project_700 ; $owner = $p.owner ; $projectNumber = $p.number + + $projectId = $p.id + + # project item issuue + $item = $p.pullrequest + $field = $p.fieldsingleselect + + $itemId1 = $item.id + + $fieldName = $field.name + $fieldId = $field.id + $type = "singleSelectOptionId" + + $fieldBeforeValueSingleSelect = $item.fieldsingleselect.name + $fieldNewValue = $field.options[1].name + $fieldNewValueId = $field.options[1].id + + $mockItems = @( + @{ + ItemId = $itemId1 + FieldId = $fieldId + Value = $fieldNewValue + ResultFile = "invoke-GitHubUpdateItemValue-$itemId1-$fieldId.json" + } + ) + + # Loop through the array and set the mock commands + foreach ($item in $mockItems) { + $command = 'Invoke-GitHubUpdateItemValues -ProjectId {ProjectId} -ItemId {ItemId} -FieldId {FieldId} -Value "{Value}" -Type {Type}' + $command = $command -replace '{ProjectId}', $projectId + $command = $command -replace '{ItemId}', $item.ItemId + $command = $command -replace '{FieldId}', $item.FieldId + $command = $command -replace '{Value}', $fieldNewValueId + $command = $command -replace '{Type}', $type + + Set-InvokeCommandMock -Command "Get-MockFileContentJson -filename $($item.ResultFile)" -Alias $command + } + + # Mock get-project + MockCall_GetProject -MockProject $p -Cache + + + Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId1 $fieldName $fieldNewValue + + $expectedStaged = @{ + $($itemId1) = @{ + $($fieldId) = $fieldNewValue + } + } + + # Act- Staged + # Confirm that the changes are staged + $staged = Get-ProjectItemStaged -Owner $owner -ProjectNumber $projectNumber + Assert-AreEqual -Expected $expectedStaged.Count -Presented $staged.Count -Comment "Items staged" + foreach($id in $expectedStaged.Keys){ + foreach($field in $expectedStaged.$id.Keys){ + Assert-AreEqual -Expected $expectedStaged.$id.$field -Presented $staged.$id.$field.Value -Comment "Item $id Field $field" + } + } + + $showStaged = Show-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber | Show-ProjectItemStaged + Assert-AreEqual -Expected $fieldNewValue -Presented $showStaged.$fieldName.Value + Assert-AreEqual -Expected $fieldBeforeValueSingleSelect -Presented $showStaged.$fieldName.Before + + # Act - Sync + $result = Sync-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber + + # Assert + Assert-IsTrue -Condition $result + + $staged = Get-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber + Assert-Count -Expected 0 -Presented $staged + + $item1 = Get-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -ItemId $itemId1 + Assert-AreEqual -Expected $fieldNewValue -Presented $item1.$fieldName +} + function Test_SyncProjectItemsStaged_SUCCESS_Content_Issue_NotCached { - Reset-InvokeCommandMock + Reset-InvokeCommandMock Mock_DatabaseRoot $Owner = "octodemo" ; $ProjectNumber = 700 @@ -576,8 +791,13 @@ function Test_ShowProjectItemsStaged { Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + MockCall_GetProject_700 + $p = Get-Mock_Project_700 ; $owner = $p.owner ; $projectNumber = $p.number + $i = $p.pullrequest + $pr = $p.issue + $fieldText = $p.fieldtext + $fieldSingleSelect = $p.fieldsingleselect + $fielddate = $p.fielddate $result = Show-ProjectItemStaged -Owner $owner -ProjectNumber $ProjectNumber Assert-IsNull -Object $result @@ -585,18 +805,18 @@ function Test_ShowProjectItemsStaged { $projectBefore = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber # Item 1 - $itemId1 = "PVTI_lADOBCrGTM4ActQazgMuXXc" + $itemId1 = $i.id - $fieldComment1 = "Comment" ; $fieldCommentValue1 = "new value of the comment 10" + $fieldComment1 = $fieldText.name ; $fieldCommentValue1 = "new value of the comment 10" $fieldCommentValue1_Before = $projectBefore.items.$itemId1.$fieldComment1 $fieldTitle1 = "Title" ; $fieldTitleValue1 = "new value of the title" $fieldTitleValue1_Before = $projectBefore.items.$itemId1.$fieldTitle1 - $fieldStatus = "Status" ; $fieldStatusValue1 = "Done" + $fieldStatus = $fieldSingleSelect.name ; $fieldStatusValue1 = $fieldSingleSelect.options[1].name $fieldStatusValue1_Before = $projectBefore.items.$itemId1.$fieldStatus - $fieldDate = "Next Action Date" ; $fieldDateValue1 = "2024-03-31" + $fieldDate = $fielddate.name ; $fieldDateValue1 = "2024-03-31" $fieldDateValue1_Before = $projectBefore.items.$itemId1.$fieldDate Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId1 $fieldComment1 $fieldCommentValue1 @@ -605,12 +825,12 @@ function Test_ShowProjectItemsStaged { Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId1 $fieldDate $fieldDateValue1 # Item 2 - $itemId2 = "PVTI_lADOBCrGTM4ActQazgMueM4" - $fieldComment2 = "Comment" ; $fileCommentValue2 = "new value of the comment 11" - $fieldTitle2 = "Title" ; $fileTitleValue2 = "new value of the title 11" + $itemId2 = $pr.id + $fieldComment2 = $fieldText.name ; $fieldCommentValue2 = "new value of the comment 11" + $fieldTitle2 = "Title" ; $fieldTitleValue2 = "new value of the title 11" - Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId2 $fieldComment2 $fileCommentValue2 - Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId2 $fieldTitle2 $fileTitleValue2 + Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId2 $fieldComment2 $fieldCommentValue2 + Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId2 $fieldTitle2 $fieldTitleValue2 # Act all staged items $result = Show-ProjectItemStaged -Owner $owner -ProjectNumber $ProjectNumber @@ -652,22 +872,36 @@ function Test_TestProjectItemStaged { Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 + $p = Get-Mock_Project_700 ; $Owner = $p.Owner ; $ProjectNumber = $p.Number + $i = $p.issue + $f = $p.fieldtext + $fieldName = "field-text" + $fieldValue = "some value" + # no project information available $result = Test-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber Assert-IsFalse -Condition $result # Project is cached - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' + MockCall_GetProject_700 -Cache $null = Get-ProjectItemList -Owner $Owner -ProjectNumber $ProjectNumber $result = Test-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber Assert-IsFalse -Condition $result # Edit some thing - MockCallJson -FileName 'updateProjectV2ItemFieldValue.json' -Command 'Invoke-GitHubUpdateItemValues -ProjectId PVT_kwDOBCrGTM4ActQa -ItemId PVTI_lADOBCrGTM4ActQazgMuXXc -FieldId PVTF_lADOBCrGTM4ActQazgSl5GU -Value "new value of the comment 10" -Type text' - Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber PVTI_lADOBCrGTM4ActQazgMuXXc "Comment" "new value of the comment 10" + $params = @{ + ProjectId = $p.Id + ItemId = $i.Id + FieldId = $f.Id + Type = "text" + Value = $fieldValue + } + MockCall_GitHubUpdateItemValues @params + MockCall_GetItem -ItemId $i.Id + Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $i.Id $fieldName $fieldValue + # Act $result = Test-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber Assert-IsTrue -Condition $result @@ -703,20 +937,22 @@ function Test_Sync_ProjectDatabaseAsync_ClearValues{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 - $moduleRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Convert-Path - $projectId = "PVT_kwDOBCrGTM4ActQa" - $itemId1 = "PVTI_lADOBCrGTM4ActQazgMuXXc" - $fieldComment1 = "Comment" ; $fieldComment1Id = "PVTF_lADOBCrGTM4ActQazgSl5GU" - $fieldPriority1 = "Priority" ; $fieldPriority1Id ="PVTSSF_lADOBCrGTM4ActQazgSl5LY" + $p = Get-Mock_Project_700 ; $Owner = $p.Owner ; $ProjectNumber = $p.Number + $i = $p.issue + $f = $p.fieldtext + $fss = $p.fieldsingleselect + + $projectId = $p.Id + $itemId1 = $i.Id + $fieldComment1 = $f.name ; $fieldComment1Id = $f.Id ; $fieldCommentName = "field-text" + $fieldPriority1 = $fss.name ; $fieldPriority1Id = $fss.Id ; $fieldPriorityName = "field-singleselect" # Edit-ProjectItem will call Get-Project with SkipItems # This test is to confirm the sync works with the project and items # Cache the project with items - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' - $null = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber + MockCall_GetProject_700 -Cache # Mock clear command for empty comment field (using async alias) $clearCommand = 'Import-Module {projecthelper} ; Invoke-GitHubClearItemValues -ProjectId {ProjectId} -ItemId {ItemId} -FieldId {FieldId}' @@ -724,7 +960,9 @@ function Test_Sync_ProjectDatabaseAsync_ClearValues{ $clearCommand = $clearCommand -replace '{ProjectId}', $projectId $clearCommand = $clearCommand -replace '{ItemId}', $itemId1 $clearCommand = $clearCommand -replace '{FieldId}', $fieldComment1Id - MockCallJsonAsync -Command $clearCommand -FileName "clearProjectV2ItemFieldValue.json" + # MockCallJsonAsync -Command $clearCommand -FileName "clearProjectV2ItemFieldValue.json" + MockCallJsonAsync -Command $clearCommand -FileName "invoke-clearProjectV2ItemFieldValue-$projectId-$itemId1-$fieldComment1Id.json" + # Mock clear command for empty priority field (using async alias) $clearCommand = 'Import-Module {projecthelper} ; Invoke-GitHubClearItemValues -ProjectId {ProjectId} -ItemId {ItemId} -FieldId {FieldId}' @@ -732,14 +970,15 @@ function Test_Sync_ProjectDatabaseAsync_ClearValues{ $clearCommand = $clearCommand -replace '{ProjectId}', $projectId $clearCommand = $clearCommand -replace '{ItemId}', $itemId1 $clearCommand = $clearCommand -replace '{FieldId}', $fieldPriority1Id - MockCallJsonAsync -Command $clearCommand -FileName "clearProjectV2ItemFieldValue.json" + # MockCallJsonAsync -Command $clearCommand -FileName "clearProjectV2ItemFieldValue.json" + MockCallJsonAsync -Command $clearCommand -FileName "invoke-clearProjectV2ItemFieldValue-$projectId-$itemId1-$fieldPriority1Id.json" # Stage the values - clear comment (empty string) and update title Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId1 $fieldComment1 "" Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId1 $fieldPriority1 "" Start-MyTranscript - $result = Sync-ProjectItemStagedAsync -Owner $Owner -ProjectNumber $ProjectNumber -SyncBatchSize 2 + $result = Sync-ProjectItemStagedAsync -Owner $Owner -ProjectNumber $ProjectNumber $transcript = Stop-MyTranscript # Return true @@ -747,11 +986,15 @@ function Test_Sync_ProjectDatabaseAsync_ClearValues{ # Verify clear and update commands were called by checking mock invocations # The transcript should show both operations on async: Calling and Saving - Assert-Contains -Presented $transcript -Expected 'Calling to update ItemField Async[True][PVT_kwDOBCrGTM4ActQa/PVTI_lADOBCrGTM4ActQazgMuXXc/PVTF_lADOBCrGTM4ActQazgSl5GU (text) = "" ]' - Assert-Contains -Presented $transcript -Expected 'Saving to database [PVT_kwDOBCrGTM4ActQa/PVTI_lADOBCrGTM4ActQazgMuXXc/Comment () = "" ]' - - Assert-Contains -Presented $transcript -Expected 'Calling to update ItemField Async[True][PVT_kwDOBCrGTM4ActQa/PVTI_lADOBCrGTM4ActQazgMuXXc/PVTSSF_lADOBCrGTM4ActQazgSl5LY (singleSelectOptionId) = "" ]' - Assert-Contains -Presented $transcript -Expected 'Saving to database [PVT_kwDOBCrGTM4ActQa/PVTI_lADOBCrGTM4ActQazgMuXXc/Priority () = "" ]' + @( + "Saving [$projectId/$itemId1/$fieldComment1Id ($fieldCommentName) = """" ] ..." + "Calling to update ItemField Async[True][$projectId/$itemId1/$fieldComment1Id (text) = """" ]" + "Done" + "Saving [$projectId/$itemId1/$fieldPriority1Id ($fieldPriorityName) = """" ] ..." + "Calling to update ItemField Async[True][$projectId/$itemId1/$fieldPriority1Id (singleSelectOptionId) = """" ]" + ) | ForEach-Object { + Assert-Contains -Presented $transcript -Expected $_ -Comment "Not found in transcript: '$_'" + } # Staged list should be empty after successful sync $staged = Get-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber @@ -767,36 +1010,34 @@ function Test_Sync_ProjectDatabase_ClearValues{ Reset-InvokeCommandMock Mock_DatabaseRoot - $Owner = "SomeOrg" ; $ProjectNumber = 164 + $p = Get-Mock_Project_700 ; $Owner = $p.Owner ; $ProjectNumber = $p.Number + $i = $p.issue + $f = $p.fieldtext + $fss = $p.fieldsingleselect - $moduleRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Convert-Path - - $projectId = "PVT_kwDOBCrGTM4ActQa" - $itemId1 = "PVTI_lADOBCrGTM4ActQazgMuXXc" - $fieldComment1 = "Comment" ; $fieldComment1Id = "PVTF_lADOBCrGTM4ActQazgSl5GU" - $fieldPriority1 = "Priority" ; $fieldPriority1Id ="PVTSSF_lADOBCrGTM4ActQazgSl5LY" + $projectId = $p.Id + $itemId1 = $i.Id + $fieldComment1 = $f.name ; $fieldComment1Id = $f.Id ; $fieldCommentName = "field-text" + $fieldPriority1 = $fss.name ; $fieldPriority1Id = $fss.Id ; $fieldPriorityName = "field-singleselect" # Edit-ProjectItem will call Get-Project with SkipItems # This test is to confirm the sync works with the project and items # Cache the project with items - MockCall_GitHubOrgProjectWithFields -Owner $owner -ProjectNumber $projectNumber -FileName 'projectV2.json' - $null = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber + MockCall_GetProject_700 -Cache # Mock clear command for empty comment field (using async alias) $clearCommand = 'Invoke-GitHubClearItemValues -ProjectId {ProjectId} -ItemId {ItemId} -FieldId {FieldId}' - $clearCommand = $clearCommand -replace '{projecthelper}', $moduleRootPath $clearCommand = $clearCommand -replace '{ProjectId}', $projectId $clearCommand = $clearCommand -replace '{ItemId}', $itemId1 $clearCommand = $clearCommand -replace '{FieldId}', $fieldComment1Id - MockCallJson -Command $clearCommand -FileName "clearProjectV2ItemFieldValue.json" + MockCallJson -Command $clearCommand -FileName "invoke-clearProjectV2ItemFieldValue-$projectId-$itemId1-$fieldComment1Id.json" # Mock clear command for empty priority field (using async alias) $clearCommand = 'Invoke-GitHubClearItemValues -ProjectId {ProjectId} -ItemId {ItemId} -FieldId {FieldId}' - $clearCommand = $clearCommand -replace '{projecthelper}', $moduleRootPath $clearCommand = $clearCommand -replace '{ProjectId}', $projectId $clearCommand = $clearCommand -replace '{ItemId}', $itemId1 $clearCommand = $clearCommand -replace '{FieldId}', $fieldPriority1Id - MockCallJsonAsync -Command $clearCommand -FileName "clearProjectV2ItemFieldValue.json" + MockCallJson -Command $clearCommand -FileName "invoke-clearProjectV2ItemFieldValue-$projectId-$itemId1-$fieldPriority1Id.json" # Stage the values - clear comment (empty string) and update title Edit-ProjectItem -Owner $owner -ProjectNumber $projectNumber $itemId1 $fieldComment1 "" @@ -812,12 +1053,14 @@ function Test_Sync_ProjectDatabase_ClearValues{ # Verify clear and update commands were called by checking mock invocations # The transcript should show both operations on async: Calling and Saving @( - 'Saving [PVT_kwDOBCrGTM4ActQa/PVTI_lADOBCrGTM4ActQazgMuXXc/PVTF_lADOBCrGTM4ActQazgSl5GU (Comment) = ] ...' - 'Calling to update ItemField Async[False][PVT_kwDOBCrGTM4ActQa/PVTI_lADOBCrGTM4ActQazgMuXXc/PVTF_lADOBCrGTM4ActQazgSl5GU (text) = "" ]' - 'Done' - 'Saving [PVT_kwDOBCrGTM4ActQa/PVTI_lADOBCrGTM4ActQazgMuXXc/PVTSSF_lADOBCrGTM4ActQazgSl5LY (Priority) = ] ...' - 'Calling to update ItemField Async[False][PVT_kwDOBCrGTM4ActQa/PVTI_lADOBCrGTM4ActQazgMuXXc/PVTSSF_lADOBCrGTM4ActQazgSl5LY (singleSelectOptionId) = "" ]' - ) | ForEach-Object { Assert-Contains -Presented $transcript -Expected $_ } + "Saving [$projectId/$itemId1/$fieldComment1Id ($fieldCommentName) = """" ] ..." + "Calling to update ItemField Async[False][$projectId/$itemId1/$fieldComment1Id (text) = """" ]" + "Done" + "Saving [$projectId/$itemId1/$fieldPriority1Id ($fieldPriorityName) = """" ] ..." + "Calling to update ItemField Async[False][$projectId/$itemId1/$fieldPriority1Id (singleSelectOptionId) = """" ]" + ) | ForEach-Object { + Assert-Contains -Presented $transcript -Expected $_ -Comment "Not found in transcript: '$_'" + } # Staged list should be empty after successful sync $staged = Get-ProjectItemStaged -Owner $Owner -ProjectNumber $ProjectNumber diff --git a/helper/invokeCommand.helper.ps1 b/helper/invokeCommand.helper.ps1 index 1fe37dc..1e83a86 100644 --- a/helper/invokeCommand.helper.ps1 +++ b/helper/invokeCommand.helper.ps1 @@ -26,4 +26,16 @@ function Set-MyInvokeCommandAlias{ if ($PSCmdlet.ShouldProcess("InvokeCommandAliasList", ("Add Command Alias [{0}] = [{1}]" -f $Alias, $Command))) { InvokeHelper\Set-InvokeCommandAlias -Alias $Alias -Command $Command -Tag $MODULE_INVOKATION_TAG } +} + +function Invoke-MyCommand{ + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline,Position=0)][string]$Command, + [Parameter(Position=1)][hashtable]$Parameters + ) + + Write-Debug "[invoke] $Command" $Parameters + + return InvokeHelper\Invoke-MyCommand -Command $Command -Parameters $Parameters } \ No newline at end of file diff --git a/include/MyWrite.ps1 b/include/MyWrite.ps1 index 0b722bb..2aa9004 100644 --- a/include/MyWrite.ps1 +++ b/include/MyWrite.ps1 @@ -2,31 +2,32 @@ $ERROR_COLOR = "Red" $WARNING_COLOR = "Yellow" $OUTPUT_COLOR = "DarkCyan" +$DEBUG_COLOR = "DarkGray" -function Write-MyError{ +function Write-MyError { param( - [Parameter(Mandatory,ValueFromPipeline)][string]$Message + [Parameter(Mandatory, ValueFromPipeline)][string]$Message ) # Write-Host "Error: $message" -ForegroundColor $ERROR_COLOR Write-ToConsole "Error: $message" -Color $ERROR_COLOR } -function Write-MyWarning{ +function Write-MyWarning { param( - [Parameter(Mandatory,ValueFromPipeline)][string]$Message + [Parameter(Mandatory, ValueFromPipeline)][string]$Message ) # Write-Host "Error: $message" -ForegroundColor $WARNING_COLOR Write-ToConsole $message -Color $WARNING_COLOR } -function Write-MyVerbose{ +function Write-MyVerbose { param( [Parameter(ValueFromPipeline)][string]$Message ) Write-Verbose -Message $message } -function Write-MyHost{ +function Write-MyHost { param( [Parameter(ValueFromPipeline)][string]$Message, #NoNewLine @@ -36,12 +37,41 @@ function Write-MyHost{ Write-ToConsole $message -Color $OUTPUT_COLOR -NoNewLine:$NoNewLine } -function Write-ToConsole{ +function Write-Debug { + param( + [Parameter(Position = 0)][string]$section, + [Parameter(Position = 1, ValueFromPipeline)][string]$Message, + [Parameter(Position = 2)][object]$Object + ) + + $flag = $env:ProjectHelper_DEBUG + + # Enable debug + if ([string]::IsNullOrWhiteSpace( $flag )) { + return + } + + $trace = ($flag -like '*all*') -or ( $section -like "*$flag*") + + # Write-Host $message -ForegroundColor $DEBUG_COLOR + if ($trace) { + + if ($Object) { + $objJson = $Object | ConvertTo-Json -Depth 10 -ErrorAction SilentlyContinue + $message = $message + " - " + $objJson + } + + $message = "[DEBUG][$section] " + $message + Write-ToConsole $message -Color $DEBUG_COLOR + } +} + +function Write-ToConsole { param( [Parameter(ValueFromPipeline)][string]$Color, - [Parameter(ValueFromPipeline, Position=0)][string]$Message, + [Parameter(ValueFromPipeline, Position = 0)][string]$Message, [Parameter()][switch]$NoNewLine ) - Write-Host $message -ForegroundColor $Color -NoNewLine:$NoNewLine + Microsoft.PowerShell.Utility\Write-Host $message -ForegroundColor $Color -NoNewLine:$NoNewLine } \ No newline at end of file diff --git a/include/openFilesUrls.ps1 b/include/openFilesUrls.ps1 index 62606dc..d6e4c2d 100644 --- a/include/openFilesUrls.ps1 +++ b/include/openFilesUrls.ps1 @@ -48,7 +48,7 @@ function Open-Url { Write-Error "Failed to open URL: $_" } } -} Export-ModuleMember -Function 'Open-Url' +} function Open-File { [CmdletBinding()] diff --git a/private/hashTable.ps1 b/private/hashTable.ps1 index af3a7a1..289633f 100644 --- a/private/hashTable.ps1 +++ b/private/hashTable.ps1 @@ -8,5 +8,6 @@ #> Function New-HashTable(){ # Return a case-sensitive hashtable - return new-object System.Collections.Hashtable + $ret = New-Object System.Collections.Hashtable + return $ret } \ No newline at end of file diff --git a/private/item/convertItemToHash.ps1 b/private/item/convertItemToHash.ps1 index f8cd487..18c9184 100644 --- a/private/item/convertItemToHash.ps1 +++ b/private/item/convertItemToHash.ps1 @@ -9,6 +9,11 @@ function Convert-NodeItemToHash { $item = New-Object System.Collections.Hashtable $item.id = $NodeItem.id + $item.databaseId = $NodeItem.fullDatabaseId + + # Project + $item.projectId = $NodeItem.project.id + $item.projectUrl = $NodeItem.project.url # Content $item.type = $NodeItem.content.__typename @@ -17,14 +22,14 @@ function Convert-NodeItemToHash { # Title is stored in two places. in the content and as a field. # We will use the field value $item.number = $NodeItem.content.number - $item.url = $NodeItem.content.url $item.state = $NodeItem.content.state - - $item.projectId = $NodeItem.project.id - $item.projectUrl = $NodeItem.project.url - + $item.urlContent = $NodeItem.content.url $item.createdAt = GetDateTime -DateTimeString $NodeItem.content.createdAt $item.updatedAt = GetDateTime -DateTimeString $NodeItem.content.updatedAt + + # Url + $item.urlPanel = Build-ItemPanelUrl -Item $item + $item.url = [string]::IsNullOrWhiteSpace($item.urlContent) ? $item.urlPanel : $item.urlContent #Fields foreach ($nodefield in $NodeItem.fieldValues.nodes) { @@ -38,7 +43,8 @@ function Convert-NodeItemToHash { $value = $nodefield.name } "ProjectV2ItemFieldNumberValue" { - $value = $nodefield.number + # $value = $nodefield.number + $value = ConvertFrom-FieldValue -Field $nodefield.field -Value $nodefield.number } "ProjectV2ItemFieldDateValue" { $value = $nodefield.date @@ -67,4 +73,31 @@ function Convert-NodeItemToHash { return $item } -} \ No newline at end of file +} + +function Build-ItemPanelUrl{ + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter(Position = 1)][object]$Item + ) + + $baseUrl = $item.projectUrl + $uriBuilder = [System.UriBuilder]::new($baseUrl) + $uriBuilder.Path += "/views/1" + + # Remove the port to avoid :443 in HTTPS URLs + $uriBuilder.Port = -1 + + # Add query parameters + $query = [System.Web.HttpUtility]::ParseQueryString([string]::Empty) + $query.Add("pane", "issue") + $query.Add("itemId", $Item.databaseId) + + $uriBuilder.Query = $query.ToString() + + # Get the final URL + $finalUrl = $uriBuilder.ToString() + + return $finalUrl +} \ No newline at end of file diff --git a/private/projectDatabase/project_database_Async.ps1 b/private/projectDatabase/project_database_Async.ps1 index fa28c4a..246db2e 100644 --- a/private/projectDatabase/project_database_Async.ps1 +++ b/private/projectDatabase/project_database_Async.ps1 @@ -94,12 +94,14 @@ function Sync-ProjectAsync { continue } - "Saving to database [$($call.projectId)/$($call.itemId)/$($call.fieldName) ($($call.FieldType)) = ""$($call.Value)"" ]" | Write-MyHost + "Saving [$($call.projectId)/$($call.itemId)/$($call.FieldId) ($($call.FieldName)) = ""$($call.Value)"" ] ..." | Write-MyHost Set-ItemValue -Database $db -ItemId $call.itemId -FieldName $call.fieldName -Value $call.Value Remove-ItemStaged -Database $db -ItemId $call.itemId -FieldId $call.FieldId } + "Done" | Write-MyHost + return $db } diff --git a/private/projectDatabase/project_database_Item.ps1 b/private/projectDatabase/project_database_Item.ps1 index 1075034..b631934 100644 --- a/private/projectDatabase/project_database_Item.ps1 +++ b/private/projectDatabase/project_database_Item.ps1 @@ -18,7 +18,7 @@ function Get-Item{ foreach($fieldKey in $database.Staged.$ItemId.keys){ $fieldname = $database.Staged.$ItemId.$fieldKey.Field.name # Make type conversions to string - $ret.$fieldname = ConvertFrom-FieldValue -Value $database.Staged.$ItemId.$fieldKey.Value -Field $database.fields.$fieldKey + $ret.$fieldname = $database.Staged.$ItemId.$fieldKey.Value } } @@ -136,12 +136,12 @@ function Save-ItemFieldValue{ throw "Failed testing value [$Value] for field $FieldName [$($field.dataType)]" } - #Transform value if needed. Sample SingleSelect will change form String to option - $value = ConvertTo-FieldValue $field $Value - if($null -eq $value){ - "Failed convertig value [$Value] for field $FieldName [$($field.dataType)]" | Write-MyError - return - } + # #Transform value if needed. Sample SingleSelect will change form String to option + # $value = ConvertTo-FieldValue $field $Value + # if($null -eq $value){ + # "Failed convertig value [$Value] for field $FieldName [$($field.dataType)]" | Write-MyError + # return + # } $node = $Database | AddHashLink Staged | AddHashLink $ItemId $node.$fieldId = [PSCustomObject]@{ diff --git a/private/projectDatabase/project_database_Sync.ps1 b/private/projectDatabase/project_database_Sync.ps1 index d6b0284..8c2cdef 100644 --- a/private/projectDatabase/project_database_Sync.ps1 +++ b/private/projectDatabase/project_database_Sync.ps1 @@ -47,7 +47,10 @@ function Sync-Project{ $FieldStagedId = $itemStaged.Keys | Copy-MyStringArray foreach($fieldId in $FieldStagedId){ + # Staged has the display value. + # Convert to the update value $value = $itemStaged.$fieldId.Value + $valueToSend = ConvertTo-FieldValue -Field $itemStaged.$fieldId.Field -Value $value $field = $itemStaged.$fieldId.Field $params = @{ @@ -57,15 +60,16 @@ function Sync-Project{ FieldName = $field.name FieldType = $field.type FieldDataType = $field.dataType - Value = $value + Value = $valueToSend } - "Saving [$($params.Database.ProjectId)/$($params.ItemId)/$($params.FieldId) ($($params.FieldName)) = $($params.Value) ] ..." | Write-MyHost -NoNewLine + "Saving [$($params.Database.ProjectId)/$($params.ItemId)/$($params.FieldId) ($($params.FieldName)) = ""$($params.Value)"" ] ..." | Write-MyHost $call = Update-ProjectItem @params if ( ! (Test-UpdateProjectItemCall $call) ) { "FAILED !!" | Write-MyHost + Write-Debug -section "Sync-Project" -Message "Update-ProjectItem call failed" -Object $call continue } diff --git a/private/projectDatabase/project_database_call.ps1 b/private/projectDatabase/project_database_call.ps1 index 337b7b8..c7c3566 100644 --- a/private/projectDatabase/project_database_call.ps1 +++ b/private/projectDatabase/project_database_call.ps1 @@ -256,22 +256,26 @@ function Update-ItemField { if ($Async) { if ([string]::IsNullOrWhiteSpace($value)) { $job = Start-MyJob -Command GitHub_ClearProjectV2ItemFieldValueAsync -Parameters $params + $resultDataType = "clearProjectV2ItemFieldValue" } else { $job = Start-MyJob -Command GitHub_UpdateProjectV2ItemFieldValueAsync -Parameters $params + $resultDataType = "updateProjectV2ItemFieldValue" } } else { - + if ([string]::IsNullOrWhiteSpace($value)) { $result = Invoke-MyCommand -Command GitHub_ClearProjectV2ItemFieldValue -Parameters $params + $resultDataType = "clearProjectV2ItemFieldValue" } else { $result = Invoke-MyCommand -Command GitHub_UpdateProjectV2ItemFieldValue -Parameters $params + $resultDataType = "updateProjectV2ItemFieldValue" } } - return $result, $job , "updateProjectV2ItemFieldValue" + return $result, $job , $resultDataType } function Test-UpdateProjectItemAsyncCall { @@ -299,5 +303,9 @@ function Test-UpdateProjectItemCall { $ret = $null -ne $call.Result.data.$($call.ResultDataType) + if (! $ret) { + "Update Project Item call Failed: $($call.Result.errors.message -join ', ')" | Write-Debug + } + return $ret } \ No newline at end of file diff --git a/private/projectDatabase/project_database_fields.ps1 b/private/projectDatabase/project_database_fields.ps1 index a4851f5..ba5c445 100644 --- a/private/projectDatabase/project_database_fields.ps1 +++ b/private/projectDatabase/project_database_fields.ps1 @@ -42,6 +42,10 @@ function Test-FieldValue{ return $ret } +<# +.SYNOPSIS + Convert from UpdateValue to DisplayValue +#> function ConvertTo-FieldValue{ [CmdletBinding()] [OutputType([string])] @@ -63,6 +67,11 @@ function ConvertTo-FieldValue{ return $ret } +<# +.SYNOPSIS + Convert from Id to Name + Convert from Internal to Display +#> function ConvertFrom-FieldValue{ [CmdletBinding()] [OutputType([string])] @@ -83,7 +92,8 @@ function ConvertFrom-FieldValue{ return $ret } -<#.SYNOPSIS +<# +.SYNOPSIS Convert from OptionId to OptionName #> function ConvertFrom-SingleSelect{ @@ -109,8 +119,12 @@ function ConvertFrom-SingleSelect{ } } -<#.SYNOPSIS +<# +.SYNOPSIS Convert from OptionName to OptionId +.DESCRIPTION + DisplayValue to UpdateValue + Display to Internal #> function ConvertTo-SingleSelect{ [CmdletBinding()] @@ -213,6 +227,11 @@ function ConvertTo-Number { # Get the numeric part from the string $match = $regex.Match($Value) + + if (-not $match.Success) { + throw "Value '$Value' is not a valid number format" + } + $numberPart = $match.Groups[1].Value # Try to guess which character is used for decimals and which is used for thousands diff --git a/private/projectDatabase/project_database_update.ps1 b/private/projectDatabase/project_database_update.ps1 index ad5d5da..dbeb570 100644 --- a/private/projectDatabase/project_database_update.ps1 +++ b/private/projectDatabase/project_database_update.ps1 @@ -134,72 +134,8 @@ function Convert-ItemsFromResponse{ $item = Convert-NodeItemToHash -NodeItem $nodeItem - # "Processing Item $($nodeItem.id) - $($nodeItem.content.title)" | Write-Verbose - - # $itemId = $nodeItem.id - - # # TODO !! - Refactor to call Convert-ItemFromResponse for each node utem - - # $item = New-Object System.Collections.Hashtable - # $item.id = $itemId - - # # Content - # $item.type = $nodeItem.content.__typename - # $item.body = $nodeItem.content.body - # $item.contentId = $nodeItem.content.id - # # Title is stored in two places. in the content and as a field. - # # We will use the field value - # # $item.title = $nodeItem.content.title - # $item.number = $nodeItem.content.number - # $item.url = $nodeItem.content.url - # $item.state = $nodeItem.content.state - - # $item.createdAt = GetDateTime -DateTimeString $nodeItem.content.createdAt - # $item.updatedAt = GetDateTime -DateTimeString $nodeItem.content.updatedAt - - # #Fields - # foreach ($nodefield in $nodeItem.fieldValues.nodes) { - - # " Procesing $($nodefield.field.name)" | Write-Verbose - - # switch ($nodefield.__typename) { - # "ProjectV2ItemFieldTextValue" { - # $value = $nodefield.text - # } - # "ProjectV2ItemFieldSingleSelectValue" { - # $value = $nodefield.name - # } - # "ProjectV2ItemFieldNumberValue" { - # $value = $nodefield.number - # } - # "ProjectV2ItemFieldDateValue" { - # $value = $nodefield.date - # } - # "ProjectV2ItemFieldUserValue" { - # $value = GetUsers -FieldNode $nodefield - # } - # "ProjectV2ItemFieldRepositoryValue" { - # $value = $nodefield.repository.url - # } - # "ProjectV2ItemFieldLabelValue" { - # $value = GetLabels -FieldNode $nodefield - # } - # "ProjectV2ItemFieldMilestoneValue" { - # $value = $nodefield.milestone.title - # } - # "ProjectV2ItemFieldPullRequestValue" { - # $value = GetPullRequests -FieldNode $nodefield - # } - # Default { - # $value = $nodefield.text - # } - # } - # $item.$($nodefield.field.name) = $value - - # } - try { - $items.$itemId += $item + $items.$itemId = $item } catch { "Failed to add item $itemId to items collection" | Write-Error } diff --git a/public/driver/issue/Invoke-AddIssueComment.ps1 b/public/driver/issue/Invoke-AddIssueComment.ps1 new file mode 100644 index 0000000..e43eeef --- /dev/null +++ b/public/driver/issue/Invoke-AddIssueComment.ps1 @@ -0,0 +1,56 @@ +function Invoke-AddIssueComment { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)][string]$SubjectId, + [Parameter(Mandatory = $true)][string]$Comment + ) + + # Use the environmentraviable + $token = Get-GithubToken + if(-not $token){ + throw "GH Cli Auth Token not available. Run 'gh auth login' in your terminal." + } + + # Define the GraphQL query with variables + $moduleroot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $qlPath = $moduleroot | Join-Path -ChildPath "graphql" -AdditionalChildPath "commentCreate.mutant" + $query = get-content -path $qlPath | Out-String + + # Define the headers for the request + $headers = @{ + "Authorization" = "Bearer $token" + "Content-Type" = "application/json" + } + + # Define the variables for the request + $variables = @{ + input = @{ + subjectId = $SubjectId + body = $Comment + } + } + + # Define the body for the request + $body = @{ + query= $query + variables = $variables + } | ConvertTo-Json -Depth 10 + + # Send the request + $response = Invoke-RestMethod -Uri 'https://api.github.com/graphql' -Method Post -Body $body -Headers $headers + + # Check if here are errors + if($response.errors){ + $response.errors | ForEach-Object { + "RESPONSE Type[$($_.type)] $($_.message)" | Write-MyError + } + return $null + } + + # Return the field names + return $response +} Export-ModuleMember -Function Invoke-AddIssueComment + +# GraphQL query: +# mutation CommentCreate($input:AddCommentInput!){addComment(input: $input){commentEdge{node{url}}}} +# GraphQL variables: {"input":{"subjectId":"I_kwDOIEf6YM7NZPEl","body":"sample comment 1"}} \ No newline at end of file diff --git a/public/environmentCache.ps1 b/public/environmentCache.ps1 index cd61092..f546f64 100644 --- a/public/environmentCache.ps1 +++ b/public/environmentCache.ps1 @@ -38,9 +38,16 @@ function Set-ProjectHelperEnvironment{ [Parameter()][string[]]$DisplayFields ) - Set-EnvItem -Name "EnvironmentCache_Owner" -Value $owner - Set-EnvItem -Name "EnvironmentCache_ProjectNumber" -Value $projectNumber - Set-EnvItem -Name "EnvironmentCache_Display_Fields" -Value $displayFields + if(! [string]::IsNullOrWhiteSpace($Owner)) { + Set-EnvItem -Name "EnvironmentCache_Owner" -Value $Owner + } + + if(! [string]::IsNullOrWhiteSpace($ProjectNumber)) { + Set-EnvItem -Name "EnvironmentCache_ProjectNumber" -Value $ProjectNumber + } + if($DisplayFields) { + Set-EnvItem -Name "EnvironmentCache_Display_Fields" -Value $DisplayFields + } } Export-ModuleMember -Function Set-ProjectHelperEnvironment @@ -56,19 +63,21 @@ function Get-OwnerAndProjectNumber{ } $ownerCache = Get-EnvItem -Name "EnvironmentCache_Owner" - if($owner -ne $ownerCache){ - Set-EnvItem -Name "EnvironmentCache_Owner" -Value $Owner - } if([string]::IsNullOrWhiteSpace($Owner)){ $owner = $ownerCache + } else { + if($owner -ne $ownerCache){ + Set-EnvItem -Name "EnvironmentCache_Owner" -Value $Owner + } } - + $projectNumberCache = Get-EnvItem -Name "EnvironmentCache_ProjectNumber" - if($ProjectNumber -ne $projectNumberCache){ - Set-EnvItem -Name "EnvironmentCache_ProjectNumber" -Value $ProjectNumber - } if([string]::IsNullOrWhiteSpace($ProjectNumber)){ $ProjectNumber = $projectNumberCache + } else { + if($ProjectNumber -ne $projectNumberCache){ + Set-EnvItem -Name "EnvironmentCache_ProjectNumber" -Value $ProjectNumber + } } return ($owner, $ProjectNumber) diff --git a/public/getPrompt.ps1 b/public/getPrompt.ps1 index 1bf264a..cd8adc6 100644 --- a/public/getPrompt.ps1 +++ b/public/getPrompt.ps1 @@ -138,6 +138,7 @@ function Write-HostPrompt { function Set-ProjecthelperPrompt { [CmdletBinding()] + [Alias("sphp")] param( [switch]$WithNewLine ) @@ -179,7 +180,7 @@ function Set-ProjecthelperPrompt { } -} Export-ModuleMember -Function Set-ProjecthelperPrompt +} Export-ModuleMember -Function Set-ProjecthelperPrompt -Alias sphp function Reset-ProjecthelperPrompt { [CmdletBinding()] diff --git a/public/graphql/commentCreate.mutant b/public/graphql/commentCreate.mutant new file mode 100644 index 0000000..154f001 --- /dev/null +++ b/public/graphql/commentCreate.mutant @@ -0,0 +1,9 @@ +mutation CommentCreate($input:AddCommentInput!){ + addComment(input: $input){ + commentEdge{ + node{ + url + } + } + } +} \ No newline at end of file diff --git a/public/graphql/getProjectV2Item.query b/public/graphql/getProjectV2Item.query index d9a99c2..b8b98c2 100644 --- a/public/graphql/getProjectV2Item.query +++ b/public/graphql/getProjectV2Item.query @@ -1,7 +1,7 @@ query($itemId: ID!) { node(id: $itemId) { ... on ProjectV2Item { - id, type, + id, type, fullDatabaseId, project{ id, url}, content{__typename, ... on DraftIssue {id,body,title,updatedAt,createdAt}, diff --git a/public/graphql/orgprojectwithfieldsAndItems.query b/public/graphql/orgprojectwithfieldsAndItems.query index 07b67d4..49d358e 100644 --- a/public/graphql/orgprojectwithfieldsAndItems.query +++ b/public/graphql/orgprojectwithfieldsAndItems.query @@ -7,7 +7,7 @@ query OrgProjectWithFields($afterFields:String$afterItems:String$firstFields:Int },... on PullRequest{id,body,title,updatedAt,createdAt,number,url,state,repository{nameWithOwner} },... on Issue{id,body,title,updatedAt,createdAt,number,url,state,repository{nameWithOwner} } - },id, + },id,fullDatabaseId, fieldValues(first: 100){nodes{__typename,... on ProjectV2ItemFieldDateValue{date,field{__typename,... on ProjectV2Field{id,name,dataType },... on ProjectV2IterationField{id,name,dataType },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name diff --git a/public/integrations/sync-ProjectItemsBetweenProjects.ps1 b/public/integrations/sync-ProjectItemsBetweenProjects.ps1 index b50ec78..c07e377 100644 --- a/public/integrations/sync-ProjectItemsBetweenProjects.ps1 +++ b/public/integrations/sync-ProjectItemsBetweenProjects.ps1 @@ -87,7 +87,7 @@ function Update-ProjectItemsBetweenProjects { # Create hashtable with the values of the fields defined in $fieldlist $values = @{} foreach($field in $FieldsList){ - $values[$field] = $sourceItem.$field + $values.$field = $sourceItem.$field } # Check if values is empty or null diff --git a/public/integrations/update-ProjectItemsStatusOnDueDate.ps1 b/public/integrations/update-ProjectItemsStatusOnDueDate.ps1 index b754737..7b99a46 100644 --- a/public/integrations/update-ProjectItemsStatusOnDueDate.ps1 +++ b/public/integrations/update-ProjectItemsStatusOnDueDate.ps1 @@ -1,34 +1,97 @@ <# .SYNOPSIS - Update the status of project items if they are overdue. + Updates (stages) project item statuses based on a due date field. + .DESCRIPTION - This function updates the status of project items if they are overdue based on a specific field. + This cmdlet helps you maintain the status of project items based on a planning date field (e.g. 'DueDate', 'Target', etc.). + The goal is set items to Action Required when due date is passed or reached, or set a Planned status if the due date is in the future. + If items are closed (aka status "Done"), their due date is cleared. + + To achieve this objective here the loggig implemented: + For each project item that has the specified due date field: + - If the due date is today or in the past: + * Change its status to the value in -StatusAction when its current status equals -StatusPlanned. + * If -AnyStatus is specified, change any (non-done) item with a past/today due date to -StatusAction (except items filtered out or cleared as done). + - If the due date is in the future and the current status equals -StatusAction, change it back to -StatusPlanned. + - If -IncludeDoneItems is set, items whose status is exactly 'Done' have the due date field cleared (status is not modified). + - If -StatusDone is provided, items whose status equals that value have the due date field cleared. + It is an error to specify both -AnyStatus and -StatusDone. + All edits are staged locally (not immediately pushed). Use Show-ProjectItemStaged, Save-ProjectItemStaged, or Reset-ProjectItemStaged as needed. + .PARAMETER Owner - The owner of the project. + Owner of the project. Optional if a default context is configured. + .PARAMETER ProjectNumber - The project number. -.PARAMETER DueDateFieldName - The name of the field that contains the due date. -.PARAMETER Status - The status to set for the project items that have overdued. + Numeric project number. Optional if a default context is configured. + +.PARAMETER StatusFieldName + Name of the project field that holds the status value (e.g. 'Status'). + +.PARAMETER DateFieldName + Name of the project field that stores the due / target date (must contain date values). + +.PARAMETER StatusAction + Status value that represents an actionable / ready state (e.g. 'ActionRequired'). + +.PARAMETER StatusPlanned + Status value that represents a planned / future / scheduled state (e.g. 'Planned'). + +.PARAMETER StatusDone + Optional alternate done-like status whose items should have their due date cleared (e.g. 'Cancelled', 'Won't Do'). + Cannot be used together with -AnyStatus. + +.PARAMETER AnyStatus + If specified, any item (except system Done or -StatusDone items cleared earlier) with a due date of today/past is moved to -StatusAction. + Mutually exclusive with -StatusDone. + +.PARAMETER IncludeDoneItems + Include items whose status is exactly 'Done'. Their due date is cleared (status unchanged). + .PARAMETER Force - Force to read the actual status of the project. + Force refresh/sync of the project items content before processing. If not set will used project cached information if available. + +.OUTPUTS + None. Writes no output. All modifications are staged. + +.EXAMPLE + Update-ProjectItemsStatusOnDueDate -Owner octodemo -ProjectNumber 625 ` + -StatusFieldName Status -DateFieldName DueDate ` + -StatusAction ActionRequired -StatusPlanned Planned + Moves overdue Planned items to ActionRequired; moves future ActionRequired items back to Planned. + +.EXAMPLE + Update-ProjectItemsStatusOnDueDate -StatusFieldName Status -DateFieldName Target ` + -StatusAction "In Progress" -StatusPlanned Planned -AnyStatus + Forces every overdue item (except 'Done') into In Progress regardless of prior status. + +.EXAMPLE + Update-ProjectItemsStatusOnDueDate -StatusFieldName Status -DateFieldName Due ` + -StatusAction ActionRequired -StatusPlanned Planned -IncludeDoneItems + Also clears Due on items already marked Done. + .EXAMPLE - Update-ProjectItemsStatusOnDueDate -Owner "octodemo" -ProjectNumber 625 -DueDateFieldName "NCC" -Status "ActionRequired" - This will update the status of project items for the owner "octodemo" and project number 625, setting the status to "ActionRequired" for items that are overdue based on the "NCC" field. + Update-ProjectItemsStatusOnDueDate -StatusFieldName Status -DateFieldName Due ` + -StatusAction ActionRequired -StatusPlanned Planned -StatusDone Cancelled + Clears Due on items whose status is Cancelled (treating them as done-like). + .NOTES - Items changed are staged on the local database. Use Show-ProjectItemStaged to see the items staged. Use Save-ProjectItemStaged to save the changes to the project. + Throws if both -AnyStatus and -StatusDone are supplied. + Avoid conflict when item is in the past with the Specified Done Status. reopening closed items when Status Done and has Due field in the past. + In this cases we could set to Action status or clear DueDate. #> function Update-ProjectItemsStatusOnDueDate{ [CmdletBinding()] param( [Parameter()][string]$Owner, [Parameter()][int]$ProjectNumber, - [Parameter()][string]$DueDateFieldName, - [Parameter()][string]$Status, + [Parameter()][string]$StatusFieldName = "Status", + [Parameter(Mandatory)][string]$DateFieldName, + [Parameter(Mandatory)][string]$StatusAction, + [Parameter(Mandatory)][string]$StatusPlanned, + [Parameter()][string]$StatusDone, + [Parameter()][switch]$AnyStatus, [Parameter()][switch]$IncludeDoneItems, - [Parameter()] [switch]$Force - + [Parameter()][switch]$Force ) "Updating project items status with due date for project $owner/$ProjectNumber" | Write-MyHost @@ -44,20 +107,36 @@ function Update-ProjectItemsStatusOnDueDate{ # Sync project if needed $null = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber -Force:$Force + $params = @{ + Owner = $Owner + ProjectNumber = $ProjectNumber + DateFieldName = $DateFieldName + StatusFieldName = $StatusFieldName + StatusAction = $StatusAction + StatusPlanned = $StatusPlanned + StatusDone = $StatusDone + AnyStatus = $AnyStatus + IncludeDoneItems = $IncludeDoneItems + } + # Call the injection type function - $ret = Invoke-ProjectInjectionOnDueDate -Owner $Owner -ProjectNumber $ProjectNumber -DueDateFieldName $DueDateFieldName -Status $Status -IncludeDoneItems:$IncludeDoneItems + $ret = Invoke-ProjectInjectionOnDueDate @params return $ret } Export-ModuleMember -Function Update-ProjectItemsStatusOnDueDate function Invoke-ProjectInjectionOnDueDate { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter()][string]$Owner, [Parameter()][int]$ProjectNumber, - [Parameter()][string]$DueDateFieldName, - [Parameter()][string]$Status, + [Parameter(Mandatory)][string]$StatusFieldName, + [Parameter(Mandatory)][string]$DateFieldName, + [Parameter(Mandatory)][string]$StatusAction, + [Parameter(Mandatory)][string]$StatusPlanned, + [Parameter()][string]$StatusDone, + [Parameter()][switch]$AnyStatus, [Parameter()][switch]$IncludeDoneItems ) @@ -68,27 +147,62 @@ function Invoke-ProjectInjectionOnDueDate { foreach($item in $items.Values){ + function EditItem($FieldName,$Value){ + $params = @{ + Owner = $Owner + ProjectNumber = $ProjectNumber + ItemId = $item.id + FieldName = $FieldName + Value = $Value + } + + if ($PSCmdlet.ShouldProcess($item.Title, "edit to $FieldName [$Value]")) { + Edit-ProjectItem @params + } + } + # Skip if the item does not have the due date field - if(-not $item.$DueDateFieldName){ + if(-not $item.$DateFieldName){ continue } # Skip if the item is not overdue $today = Get-DateToday - if($today -lt $item.$DueDateFieldName){ + $actualDate = $item.$DateFieldName + $actualStatus = $item.Status + + # IncludeDoneItems --> Clear date on items that are system done + if (($IncludeDoneItems) -and ($actualStatus -eq "Done")) { + # Clear DueDate to done items + EditItem $DateFieldName "" continue } - # Change item + $isPastOrToday = $actualDate -le $today + $isFuture = ! $isPastOrToday + $isActionRequired = $actualStatus -eq $StatusAction + $isSetOtherDone = ! [string]::IsNullOrWhiteSpace($StatusDone) + $isOtherDone = ($actualStatus -eq $StatusDone) - # Update status of the items - $params = @{ - Owner = $Owner - ProjectNumber = $ProjectNumber - ItemId = $item.id - FieldName = "Status" - Value = $Status + # Clear date on items where status is $StatusDone + if ($isSetOtherDone -and $isOtherDone ) { + # Clear DueDate to done items + EditItem $DateFieldName "" + continue + } + + # Move to StatusAction if today or due, and status is Planned or AnyStatus is set + if ( $isPastOrToday -and (($actualStatus -eq $StatusPlanned) -or ($AnyStatus))){ + EditItem $StatusFieldName $StatusAction + continue + } + + # Move to Planned if ActionRequred but in the future + if( $isFuture -and $isActionRequired){ + EditItem $StatusFieldName $StatusPlanned + continue } - Edit-ProjectItem @params } -} # Do not export this function to avoid conflicts with Update-ProjectItemsWithIntegration \ No newline at end of file +} # Do not export this function to avoid conflicts with Update-ProjectItemsWithIntegration + + diff --git a/public/issues/Add-IssuePullRequestComment.ps1 b/public/issues/Add-IssuePullRequestComment.ps1 new file mode 100644 index 0000000..3a11dd0 --- /dev/null +++ b/public/issues/Add-IssuePullRequestComment.ps1 @@ -0,0 +1,50 @@ +Set-MyInvokeCommandAlias -Alias AddIssueComment -Command 'Invoke-AddIssueComment -SubjectId {subjectid} -Comment "{comment}"' + +function Add-IssuePullRequestCommentDirect { + [CmdletBinding()] + param( + [Parameter(Mandatory, ValueFromPipeline, Position = 0)][string]$ItemId, + [Parameter(Mandatory, Position = 1)][string]$Comment + ) + + # Try to find item on context project + ($Owner, $ProjectNumber) = Get-OwnerAndProjectNumber -Owner $Owner -ProjectNumber $ProjectNumber + if (! [string]::IsNullOrWhiteSpace($owner) -and ! [string]::IsNullOrWhiteSpace($ProjectNumber)) { + $item = Get-ProjectItem -ItemId $ItemId -Owner $Owner -ProjectNumber $ProjectNumber + } + + # If not found get the item directly + if (-not $item) { + $item = Get-ProjectItemDirect -ItemId $ItemId + } + + if (-not $item) { + throw "ItemId [$ItemId] not found" + } + + if ( ! ($item.type -in ('Issue', 'PullRequest')) ) { + "ItemId [$ItemId] is not an Issue. Type is [$($item.type)]" | Write-Error + return $null + } + + $response = Invoke-MyCommand -Command 'AddIssueComment' -Parameters @{ + subjectid = $item.contentId + comment = $Comment + } + + # check if the response is null + if ($response.errors) { + "[$($response.errors[0].type)] $($response.errors[0].message)" | Write-MyError + return $null + } + + $url = $response.data.addComment.commentEdge.node.url + + if ($null -eq $url) { + "Failed to add comment to issue [$ItemId]" | Write-MyError + return $null + } + + return $url + +} Export-ModuleMember -Function Add-IssuePullRequestCommentDirect \ No newline at end of file diff --git a/public/project_fields_list.ps1 b/public/project_fields_list.ps1 index d2f11af..56e892f 100644 --- a/public/project_fields_list.ps1 +++ b/public/project_fields_list.ps1 @@ -34,9 +34,9 @@ function Get-ProjectFields{ [CmdletBinding()] [OutputType([string[]])] param( - [Parameter(Position = 0)] [string]$Owner, - [Parameter(Position = 1)] [string]$ProjectNumber, - [Parameter()][string]$Name, + [Parameter()] [string]$Owner, + [Parameter()] [string]$ProjectNumber, + [Parameter(Position = 0)][string]$Name, [Parameter()][switch]$Force ) diff --git a/public/project_item.ps1 b/public/project_item.ps1 index 97e99fe..24ca406 100644 --- a/public/project_item.ps1 +++ b/public/project_item.ps1 @@ -12,6 +12,7 @@ Set-MyInvokeCommandAlias -Alias GetItem -Command 'Invoke-GetItem -ItemId {itemid #> function Get-ProjectItem { [CmdletBinding()] + [Alias ("gpi")] param( [Parameter(Mandatory, ValueFromPipeline, Position = 0)][string]$ItemId, [Parameter()][string]$Owner, @@ -29,8 +30,8 @@ function Get-ProjectItem { if(! $db){ "Project not found for Owner [$Owner] and ProjectNumber [$ProjectNumber]" | Write-MyError; return $null} - # Durty flag - $durty = $false + # Dirty flag + $dirty = $false } process { @@ -46,7 +47,7 @@ function Get-ProjectItem { } } -} Export-ModuleMember -Function Get-ProjectItem +} Export-ModuleMember -Function Get-ProjectItem -Alias "gpi" # function Set-ProjectItem { # [CmdletBinding()] @@ -90,67 +91,73 @@ function Remove-ProjectItem { } -function Find-ProjectItem { +function Search-ProjectItem { [CmdletBinding()] + [Alias ("spi")] param( + [Parameter(Mandatory, Position = 0)] [string]$Filter, + [Parameter(Position = 1)][string[]]$Attributes, [Parameter()][string]$Owner, [Parameter()][string]$ProjectNumber, - [Parameter(Mandatory, Position = 0)][string]$Title, [Parameter()][switch]$IncludeDone, - [Parameter()][switch]$Match, - [Parameter()][switch]$Force + [Parameter()][switch]$Force, + [Parameter()][switch]$PassThru ) + if([string]::IsNullOrWhiteSpace($Attributes)){ + $Attributes = @("id", "Title") + } + ($Owner, $ProjectNumber) = Get-OwnerAndProjectNumber -Owner $Owner -ProjectNumber $ProjectNumber if ([string]::IsNullOrWhiteSpace($owner) -or [string]::IsNullOrWhiteSpace($ProjectNumber)) { "Owner and ProjectNumber are required" | Write-MyError; return $null } - + $items = Get-ProjectItemList -Owner $Owner -ProjectNumber $ProjectNumber -Force:$Force -ExcludeDone:$(-not $IncludeDone) # return if #items is null if ($null -eq $items) { return $null } - # Find item in the database - if ($Match) { - $found = $items.Values | Where-Object { $_.Title -eq $Title } - } - else { - $found = $items.Values | Where-Object { $_.Title -like "$Title" } - } + $found = $items.Values | Where-Object { Test-ProjectItemIsLikeAnyField -Item $_ -Value $Filter } - $ret = $found | ForEach-Object { - [PSCustomObject]$_ - } + if($PassThru){ + $ret = $found + } else { + + $ret = @($found | ForEach-Object { + $i = [pscustomobject]::new() + # return the issue with additional attributes + Add-Attributes -SourceObject $_ -DestinationObject $i -Attributes $Attributes + }) + + } return $ret -} Export-ModuleMember -Function Find-ProjectItem -function Search-ProjectItem { +} Export-ModuleMember -Function Search-ProjectItem -Alias "spi" + +function Add-Attributes{ [CmdletBinding()] param( - [Parameter(Mandatory, Position = 0)] [string]$Filter, - [Parameter()][string]$Owner, - [Parameter()][string]$ProjectNumber, - [Parameter()][switch]$IncludeDone, - [Parameter()][switch]$Force + [Parameter(Position = 0)][object]$SourceObject, + [Parameter(Position = 1)][object]$DestinationObject, + [Parameter(Position = 2)][string[]]$Attributes ) - ($Owner, $ProjectNumber) = Get-OwnerAndProjectNumber -Owner $Owner -ProjectNumber $ProjectNumber - if ([string]::IsNullOrWhiteSpace($owner) -or [string]::IsNullOrWhiteSpace($ProjectNumber)) { "Owner and ProjectNumber are required" | Write-MyError; return $null } - - $items = Get-ProjectItemList -Owner $Owner -ProjectNumber $ProjectNumber -Force:$Force -ExcludeDone:$(-not $IncludeDone) - - # return if #items is null - if ($null -eq $items) { return $null } + # return if empty attributes + if([string]::IsNullOrWhiteSpace($Attributes)){ + return $DestinationObject + } - $found = $items.Values | Where-Object { Test-ProjectItemIsLikeAnyField -Item $_ -Value $Filter } + foreach($a in $Attributes){ + if( ! $SourceObject.$a){ + continue + } - $ret = @($found | ForEach-Object { - [PSCustomObject]$_ - }) + $DestinationObject | Add-Member -MemberType NoteProperty -Name $a -Value $SourceObject.$a -force + } - return $ret + return $DestinationObject +} -} Export-ModuleMember -Function Search-ProjectItem function Get-ProjectItems { [CmdletBinding()] @@ -179,40 +186,54 @@ function Get-ProjectItems { function Open-ProjectItem { [CmdletBinding()] + [Alias ("opi")] param( [Parameter()][string]$Owner, [Parameter()][int]$ProjectNumber, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline, Position = 0)] - [string]$ItemId + [string]$Id, + [Parameter()][switch]$InProject ) begin { - "Project set to [$owner/$ProjectNumber]" | Write-Verbose - + ($Owner, $ProjectNumber) = Get-OwnerAndProjectNumber -Owner $Owner -ProjectNumber $ProjectNumber if ([string]::IsNullOrWhiteSpace($owner) -or [string]::IsNullOrWhiteSpace($ProjectNumber)) { throw "Owner and ProjectNumber are required on Open-ProjectItem" } + + "Project set to [$owner/$ProjectNumber]" | Write-Verbose + } process { + $itemId = $Id + "Opening item [$ItemId] in project [$Owner/$ProjectNumber]" | Write-Verbose $item = Get-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -ItemId $ItemId if (-not $item) { throw "Item not found for Owner [$Owner], ProjectNumber [$ProjectNumber] and ItemId [$ItemId]" } + + if($InProject){ + $url = $item.urlPanel + } else { + # fall back to url if urlcontent is empty + $url = $item.urlContent ?? $item.url + } - if (-not $item.url) { - "No URL found for Item [$ItemId] type $($item.type)" | Write-Error + if ([string]::IsNullOrWhiteSpace($url)) { + # We should never reach this point as all items has a urlpanel set in Convert-NodeItemToHash + "No URL found for Item [$ItemId] type [ $($item.type) ]" | Write-Error return } - - Open-Url -Url $item.url + + Open-Url -Url $url } -} Export-ModuleMember -Function Open-ProjectItem +} Export-ModuleMember -Function Open-ProjectItem -Alias "opi" <# .SYNOPSIS @@ -418,6 +439,8 @@ function Show-ProjectItem { begin { $fields = Get-EnvironmentDisplayFields -Fields $AdditionalFields + + $fields | Write-Verbose } process { @@ -435,6 +458,7 @@ function Test-ProjectItemIsLikeAnyField { ) foreach ($key in $item.Keys) { if ($item.$key -Like "*$Value*") { + "Found [$Value] in field [$key] in [$($item.$key)]" | Write-Verbose return $true } } diff --git a/public/project_item_List.ps1 b/public/project_item_List.ps1 index 0f304c9..d1192b5 100644 --- a/public/project_item_List.ps1 +++ b/public/project_item_List.ps1 @@ -97,64 +97,12 @@ function Test-ItemIsDone($Item){ return $ret } -<# -.SYNOPSIS -Finds project items by exact title match. - -.DESCRIPTION -Searches for project items that have an exact title match (case-insensitive). -Returns all items that match the specified title. - -.PARAMETER Title -The exact title to search for. The comparison is case-insensitive and trims whitespace. - -.PARAMETER Owner -The owner of the GitHub repository containing the project. - -.PARAMETER ProjectNumber -The project number in the repository. - -.PARAMETER Force -Forces a refresh of the project data from GitHub. - -.OUTPUTS -System.Object[] -Returns an array of project item objects that match the title. - -.EXAMPLE -Find-ProjectItemByTitle -Title "Bug Fix" -Owner "octocat" -ProjectNumber "1" -Finds all items in project 1 with the exact title "Bug Fix". -#> -function Find-ProjectItemByTitle{ - [CmdletBinding()] - param( - [Parameter(Position = 0)] [string]$Title, - [Parameter()] [string]$Owner, - [Parameter()] [string]$ProjectNumber, - [Parameter()] [switch]$Force - ) - - ($Owner,$ProjectNumber) = Get-OwnerAndProjectNumber -Owner $Owner -ProjectNumber $ProjectNumber - if([string]::IsNullOrWhiteSpace($owner) -or [string]::IsNullOrWhiteSpace($ProjectNumber)){ "Owner and ProjectNumber are required" | Write-MyError; return $null} - - $items = Get-ProjectItemList -Owner $Owner -ProjectNumber $ProjectNumber -Force:$Force - - # return if #db is null - if($null -eq $items){ return $null } - - $ret = $items.Values | Where-Object { $_.Title.Trim().ToLower() -eq $($Title.Trim().ToLower()) } - - return $ret - -} Export-ModuleMember -Function Find-ProjectItemByTitle - <# .SYNOPSIS Searches for project items by title using wildcard matching. .DESCRIPTION Searches for project items where the title contains the specified text using wildcard matching. -This is different from Find-ProjectItemByTitle which requires an exact match. .PARAMETER Owner The owner of the GitHub repository containing the project. @@ -237,7 +185,7 @@ Searches for items containing "urgent" and displays only Title and Status fields function Search-ProjectItem{ [CmdletBinding()] param( - [Parameter(Position = 0)] [string]$filter, + [Parameter(Position = 0)] [string]$Filter, [Parameter()][string]$Owner, [Parameter()][string]$ProjectNumber, [Parameter()][string[]]$Fields, @@ -253,12 +201,12 @@ function Search-ProjectItem{ if($null -eq $itemList){ return $null} - $itemListValues = $itemList.Values | FilterItems -Filter $filter + $itemListValues = $itemList.Values | FilterItems -Filter $Filter $items = $itemListValues | ConvertToItemDisplay -Fields $Fields Write-MyHost - "Filter: $filter" | Write-MyHost + "Filter: $Filter" | Write-MyHost Write-MyHost return $items diff --git a/public/project_item_staged.ps1 b/public/project_item_staged.ps1 index 5f3e93f..5472cf2 100644 --- a/public/project_item_staged.ps1 +++ b/public/project_item_staged.ps1 @@ -178,10 +178,8 @@ function Show-ProjectItemStaged{ $ret = @{} foreach($key in $staged.Keys){ - $field = Get-Field $db $key - $stagedFieldValue = ConvertFrom-FieldValue -Field $field -Value $staged.$key $ret.$key = [PSCustomObject]@{ - Value = $stagedFieldValue + Value = $staged.$key Before = $item.$key } }