From 4093f63bf7587930c49d56c65e46411ebc99108b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20=28Dibildos=29=20Gonz=C3=A1lez?= Date: Mon, 15 Sep 2025 06:50:13 +0200 Subject: [PATCH 01/10] feat(graphql): add project fields to queries and convert function --- private/item/convertItemToHash.ps1 | 3 +++ public/graphql/getProjectV2Item.query | 1 + public/graphql/orgprojectwithfieldsAndItems.query | 8 ++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/private/item/convertItemToHash.ps1 b/private/item/convertItemToHash.ps1 index cc0e756..f8cd487 100644 --- a/private/item/convertItemToHash.ps1 +++ b/private/item/convertItemToHash.ps1 @@ -20,6 +20,9 @@ function Convert-NodeItemToHash { $item.url = $NodeItem.content.url $item.state = $NodeItem.content.state + $item.projectId = $NodeItem.project.id + $item.projectUrl = $NodeItem.project.url + $item.createdAt = GetDateTime -DateTimeString $NodeItem.content.createdAt $item.updatedAt = GetDateTime -DateTimeString $NodeItem.content.updatedAt diff --git a/public/graphql/getProjectV2Item.query b/public/graphql/getProjectV2Item.query index f9a3542..d9a99c2 100644 --- a/public/graphql/getProjectV2Item.query +++ b/public/graphql/getProjectV2Item.query @@ -2,6 +2,7 @@ query($itemId: ID!) { node(id: $itemId) { ... on ProjectV2Item { id, type, + project{ id, url}, content{__typename, ... on DraftIssue {id,body,title,updatedAt,createdAt}, ... on PullRequest{id,body,title,updatedAt,createdAt,number,url,state,repository{nameWithOwner}}, diff --git a/public/graphql/orgprojectwithfieldsAndItems.query b/public/graphql/orgprojectwithfieldsAndItems.query index 0ad9e64..07b67d4 100644 --- a/public/graphql/orgprojectwithfieldsAndItems.query +++ b/public/graphql/orgprojectwithfieldsAndItems.query @@ -1,10 +1,14 @@ query OrgProjectWithFields($afterFields:String$afterItems:String$firstFields:Int!$firstItems:Int!$login:String!$number:Int!){organization(login: $login){projectV2(number: $number){number,url,shortDescription,public,closed,title,id,readme,items(first: $firstItems, after: $afterItems){pageInfo{endCursor,hasNextPage - },totalCount,nodes{content{__typename + },totalCount, + nodes{ + project{ id, number, title, url}, + content{__typename ,... on DraftIssue {id,body,title,updatedAt,createdAt },... 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,fieldValues(first: 100){nodes{__typename,... on ProjectV2ItemFieldDateValue{date,field{__typename,... on ProjectV2Field{id,name,dataType + },id, + 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 } From 986ee6cbc91c6ef9e11675b47e2e0847f759803e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20=28Dibildos=29=20Gonz=C3=A1lez?= Date: Mon, 15 Sep 2025 07:27:42 +0200 Subject: [PATCH 02/10] fix(database): specify module path for Remove-Item command --- include/databaseV2.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/databaseV2.ps1 b/include/databaseV2.ps1 index 2f917ad..1333712 100644 --- a/include/databaseV2.ps1 +++ b/include/databaseV2.ps1 @@ -16,7 +16,7 @@ function Reset-DatabaseStore{ $databaseRoot = Invoke-MyCommand -Command GetDatabaseStorePath - Remove-Item -Path $databaseRoot -Recurse -Force -ErrorAction SilentlyContinue + Microsoft.PowerShell.Management\Remove-Item -Path $databaseRoot -Recurse -Force -ErrorAction SilentlyContinue New-Item -Path $databaseRoot -ItemType Directory @@ -55,7 +55,7 @@ function Reset-Database{ [Parameter(Position = 0)][string]$Key ) $path = Get-DatabaseFile -Key $Key - Remove-Item -Path $path -Force -ErrorAction SilentlyContinue + Microsoft.PowerShell.Management\Remove-Item -Path $path -Force -ErrorAction SilentlyContinue return } From 2411f9017b9f3e6f7d426c78d0fed8981ea73c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20=28Dibildos=29=20Gonz=C3=A1lez?= Date: Mon, 15 Sep 2025 07:37:39 +0200 Subject: [PATCH 03/10] refactor(graphql): reorganize AddItem mutation structure for clarity --- public/graphql/addItemToProject.mutant | 149 +++++++++++++------------ 1 file changed, 78 insertions(+), 71 deletions(-) diff --git a/public/graphql/addItemToProject.mutant b/public/graphql/addItemToProject.mutant index e03875c..efcb8f8 100644 --- a/public/graphql/addItemToProject.mutant +++ b/public/graphql/addItemToProject.mutant @@ -1,91 +1,98 @@ -mutation AddItem($input:AddProjectV2ItemByIdInput!){addProjectV2ItemById(input:$input){item{content{__typename,... on DraftIssue{id,body,title - },... on PullRequest{body,title,number,url,repository{nameWithOwner +mutation AddItem($input:AddProjectV2ItemByIdInput!){addProjectV2ItemById(input:$input){ + item{ +id, type, + project{ id, url}, + content{__typename, + ... on DraftIssue {id,body,title,updatedAt,createdAt}, + ... on PullRequest{id,body,title,updatedAt,createdAt,number,url,state,repository{nameWithOwner}}, + ... on Issue{id,body,title,updatedAt,createdAt,number,url,state,repository{nameWithOwner} + }, + } + 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 + } } - },... on Issue{body,title,number,url,repository{nameWithOwner + } + },... on ProjectV2ItemFieldIterationValue{title,startDate,duration,field{__typename,... on ProjectV2Field{id,name,dataType + },... on ProjectV2IterationField{id,name,dataType + },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name + } } } - },id,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 - } - } + },... on ProjectV2ItemFieldLabelValue{labels(first: 10){nodes{name + } + },field{__typename,... on ProjectV2Field{id,name,dataType + },... on ProjectV2IterationField{id,name,dataType + },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name } - },... on ProjectV2ItemFieldIterationValue{title,startDate,duration,field{__typename,... on ProjectV2Field{id,name,dataType - },... on ProjectV2IterationField{id,name,dataType - },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name - } - } - },iterationId - },... on ProjectV2ItemFieldLabelValue{labels(first: 10){nodes{name - } - },field{__typename,... on ProjectV2Field{id,name,dataType - },... on ProjectV2IterationField{id,name,dataType - },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name - } - } + } + } + },... on ProjectV2ItemFieldNumberValue{number,field{__typename,... on ProjectV2Field{id,name,dataType + },... on ProjectV2IterationField{id,name,dataType + },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name } - },... on ProjectV2ItemFieldNumberValue{number,field{__typename,... on ProjectV2Field{id,name,dataType - },... on ProjectV2IterationField{id,name,dataType - },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name - } - } + } + } + },... on ProjectV2ItemFieldSingleSelectValue{name,field{__typename,... on ProjectV2Field{id,name,dataType + },... on ProjectV2IterationField{id,name,dataType + },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name } - },... on ProjectV2ItemFieldSingleSelectValue{name,field{__typename,... on ProjectV2Field{id,name,dataType - },... on ProjectV2IterationField{id,name,dataType - },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name - } - } + } + } + },... on ProjectV2ItemFieldTextValue{text,field{__typename,... on ProjectV2Field{id,name,dataType + },... on ProjectV2IterationField{id,name,dataType + },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name } - },... on ProjectV2ItemFieldTextValue{text,field{__typename,... on ProjectV2Field{id,name,dataType - },... on ProjectV2IterationField{id,name,dataType - },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name - } - } + } + } + },... on ProjectV2ItemFieldMilestoneValue{milestone{title,description,dueOn + },field{__typename,... on ProjectV2Field{id,name,dataType + },... on ProjectV2IterationField{id,name,dataType + },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name } - },... on ProjectV2ItemFieldMilestoneValue{milestone{title,description,dueOn - },field{__typename,... on ProjectV2Field{id,name,dataType - },... on ProjectV2IterationField{id,name,dataType - },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name - } - } + } + } + },... on ProjectV2ItemFieldPullRequestValue{pullRequests(first: 10){nodes{url + } + },field{__typename,... on ProjectV2Field{id,name,dataType + },... on ProjectV2IterationField{id,name,dataType + },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name } - },... on ProjectV2ItemFieldPullRequestValue{pullRequests(first: 10){nodes{url - } - },field{__typename,... on ProjectV2Field{id,name,dataType - },... on ProjectV2IterationField{id,name,dataType - },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name - } - } + } + } + },... on ProjectV2ItemFieldRepositoryValue{repository{url + },field{__typename,... on ProjectV2Field{id,name,dataType + },... on ProjectV2IterationField{id,name,dataType + },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name } - },... on ProjectV2ItemFieldRepositoryValue{repository{url - },field{__typename,... on ProjectV2Field{id,name,dataType - },... on ProjectV2IterationField{id,name,dataType - },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name - } - } + } + } + },... on ProjectV2ItemFieldUserValue{users(first: 10){nodes{login + } + },field{__typename,... on ProjectV2Field{id,name,dataType + },... on ProjectV2IterationField{id,name,dataType + },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name } - },... on ProjectV2ItemFieldUserValue{users(first: 10){nodes{login - } - },field{__typename,... on ProjectV2Field{id,name,dataType - },... on ProjectV2IterationField{id,name,dataType - },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name - } - } + } + } + },... on ProjectV2ItemFieldReviewerValue{reviewers(first: 10){nodes{__typename,... on Team{name + },... on User{login } - },... on ProjectV2ItemFieldReviewerValue{reviewers(first: 10){nodes{__typename,... on Team{name - },... on User{login - } - } - },field{__typename,... on ProjectV2Field{id,name,dataType - },... on ProjectV2IterationField{id,name,dataType - },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name - } - } + } + },field{__typename,... on ProjectV2Field{id,name,dataType + },... on ProjectV2IterationField{id,name,dataType + },... on ProjectV2SingleSelectField{id,name,dataType,options{id,name } } } } } + } + + } } } \ No newline at end of file From 40cfaee3564a8df3c0861d4e08cb4a956dc3e73b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20=28Dibildos=29=20Gonz=C3=A1lez?= Date: Mon, 15 Sep 2025 07:38:57 +0200 Subject: [PATCH 04/10] fix(database): validate Database parameter and ensure owner and project number match --- private/projectDatabase/project_database.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/private/projectDatabase/project_database.ps1 b/private/projectDatabase/project_database.ps1 index 9e87dbd..3a76b6d 100644 --- a/private/projectDatabase/project_database.ps1 +++ b/private/projectDatabase/project_database.ps1 @@ -106,6 +106,18 @@ function Save-ProjectDatabase{ [Parameter(Position = 2)][hashtable]$Database ) + if($null -eq $Database){ + throw "Database parameter is required" + } + + if($Owner -ne $Database.owner){ + throw "Owner parameter [$Owner] does not match database owner [$($Database.owner)]" + } + + if($ProjectNumber -ne $Database.number){ + throw "ProjectNumber parameter [$ProjectNumber] does not match database project number [$($Database.number)]" + } + $dbkey = Get-DatabaseKey -Owner $owner -ProjectNumber $projectnumber Save-Database -Key $dbkey -Database $Database } From c16ee6d8b62510608a66a0125722fdbedf7918b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20=28Dibildos=29=20Gonz=C3=A1lez?= Date: Mon, 15 Sep 2025 07:39:58 +0200 Subject: [PATCH 05/10] refactor(database): remove OutputType attribute from Set-Item and add Remove-Item function --- private/projectDatabase/project_database_Item.ps1 | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/private/projectDatabase/project_database_Item.ps1 b/private/projectDatabase/project_database_Item.ps1 index 2651c62..d490202 100644 --- a/private/projectDatabase/project_database_Item.ps1 +++ b/private/projectDatabase/project_database_Item.ps1 @@ -36,7 +36,6 @@ function Get-Item{ function Set-Item{ [CmdletBinding()] - [OutputType([string])] param( [Parameter(Position = 0)][object[]]$Database, [Parameter(ValueFromPipeline, Position = 1)][PSCustomObject]$Item @@ -52,6 +51,17 @@ function Set-Item{ } +function Remove-Item{ + [CmdletBinding()] + param( + [Parameter(Position = 0)][object[]]$Database, + [Parameter(ValueFromPipeline, Position = 1)][string]$ItemId + ) + + $db.Items.Remove($ItemId) | Out-Null + +} + function Set-ItemValue{ [CmdletBinding()] [OutputType([string])] From f863bfcf2f79161c40cd75d61493ba4fd3bfdd0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20=28Dibildos=29=20Gonz=C3=A1lez?= Date: Mon, 15 Sep 2025 08:08:57 +0200 Subject: [PATCH 06/10] refactor(database): simplify Save-ProjectDatabase parameters by removing Owner and ProjectNumber --- private/projectDatabase/project_database.ps1 | 18 +++++++++--------- .../projectDatabase/project_database_Async.ps1 | 4 ++-- .../projectDatabase/project_database_Sync.ps1 | 2 +- public/project_item.ps1 | 4 ++-- public/project_item_staged.ps1 | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/private/projectDatabase/project_database.ps1 b/private/projectDatabase/project_database.ps1 index 3a76b6d..19f88a8 100644 --- a/private/projectDatabase/project_database.ps1 +++ b/private/projectDatabase/project_database.ps1 @@ -95,27 +95,27 @@ function Set-ProjectDatabaseV2{ $db.items = $items $db.fields = $fields - Save-ProjectDatabase -Database $db -Owner $owner -ProjectNumber $projectnumber + Save-ProjectDatabase -Database $db } function Save-ProjectDatabase{ [CmdletBinding()] param( - [Parameter(Position = 0)][string]$Owner, - [Parameter(Position = 1)][int]$ProjectNumber, - [Parameter(Position = 2)][hashtable]$Database + [Parameter(Position = 0)][hashtable]$Database ) + $owner = $Database.owner + $projectnumber = $Database.number + if($null -eq $Database){ throw "Database parameter is required" } - if($Owner -ne $Database.owner){ - throw "Owner parameter [$Owner] does not match database owner [$($Database.owner)]" + if([string]::IsNullOrWhiteSpace($owner)){ + throw "Database.owner is null or empty" } - - if($ProjectNumber -ne $Database.number){ - throw "ProjectNumber parameter [$ProjectNumber] does not match database project number [$($Database.number)]" + if($projectnumber -le 0){ + throw "Database.number is null or not a positive integer" } $dbkey = Get-DatabaseKey -Owner $owner -ProjectNumber $projectnumber diff --git a/private/projectDatabase/project_database_Async.ps1 b/private/projectDatabase/project_database_Async.ps1 index 76599c6..4ef34f6 100644 --- a/private/projectDatabase/project_database_Async.ps1 +++ b/private/projectDatabase/project_database_Async.ps1 @@ -22,7 +22,7 @@ function Sync-ProjectDatabaseAsync { $db = Sync-ProjectAsync -Database $db -SyncBatchSize $SyncBatchSize # Saved changes to database - Save-ProjectDatabase -Database $db -Owner $Owner -ProjectNumber $ProjectNumber + Save-ProjectDatabase -Database $db if (Test-ProjectDatabaseStaged -Owner $Owner -ProjectNumber $ProjectNumber) { "Still pending staged values" | Write-MyError @@ -100,7 +100,7 @@ function Sync-ProjectAsync { Remove-ItemStaged -Database $db -ItemId $call.itemId -FieldId $call.FieldId } - Save-ProjectDatabase -Database $db -Owner $Owner -ProjectNumber $ProjectNumber + Save-ProjectDatabase -Database $db return $db } diff --git a/private/projectDatabase/project_database_Sync.ps1 b/private/projectDatabase/project_database_Sync.ps1 index b7659a8..c9e2878 100644 --- a/private/projectDatabase/project_database_Sync.ps1 +++ b/private/projectDatabase/project_database_Sync.ps1 @@ -20,7 +20,7 @@ function Sync-ProjectDatabase{ # Send update to project $db = Sync-Project -Database $db - Save-ProjectDatabase -Database $db -Owner $Owner -ProjectNumber $ProjectNumber + Save-ProjectDatabase -Database $db if (Test-ProjectDatabaseStaged -Owner $Owner -ProjectNumber $ProjectNumber) { "Still pending staged values" | Write-MyError diff --git a/public/project_item.ps1 b/public/project_item.ps1 index 9b3c1e5..b7ca197 100644 --- a/public/project_item.ps1 +++ b/public/project_item.ps1 @@ -54,7 +54,7 @@ function Set-ProjectItem{ Set-Item $db $item - Save-ProjectDatabase -Owner $Owner -ProjectNumber $ProjectNumber -Database $db + Save-ProjectDatabase -Database $db } function Find-ProjectItem{ @@ -163,7 +163,7 @@ function Edit-ProjectItem{ Save-ItemFieldValue $db $itemId $FieldName $Value # Commit changes to the database - Save-ProjectDatabase -Owner $Owner -ProjectNumber $ProjectNumber -Database $db + Save-ProjectDatabase -Database $db } Export-ModuleMember -Function Edit-ProjectItem diff --git a/public/project_item_staged.ps1 b/public/project_item_staged.ps1 index 42710d6..0aba28b 100644 --- a/public/project_item_staged.ps1 +++ b/public/project_item_staged.ps1 @@ -107,7 +107,7 @@ function Reset-ProjectItemStaged{ $db = Get-Project $Owner $ProjectNumber $db.Staged = $null - Save-ProjectDatabase -Database $db -Owner $Owner -ProjectNumber $ProjectNumber + Save-ProjectDatabase -Database $db } Export-ModuleMember -Function Reset-ProjectItemStaged From d363f0062badd7c8b3222c21d0717cc02a9dbae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20=28Dibildos=29=20Gonz=C3=A1lez?= Date: Mon, 15 Sep 2025 08:28:52 +0200 Subject: [PATCH 07/10] refactor(database): update Get-ProjectItemDirect and Set-ProjectItem to support NoCache option --- Test/public/project_item.test.ps1 | 5 +- .../projectDatabase/project_database_call.ps1 | 3 +- public/project_item.ps1 | 80 ++++++++++++++++--- 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/Test/public/project_item.test.ps1 b/Test/public/project_item.test.ps1 index 40cc102..db9b630 100644 --- a/Test/public/project_item.test.ps1 +++ b/Test/public/project_item.test.ps1 @@ -359,13 +359,16 @@ function Test_GetItemDirect_SUCCESS{ MockCallJson -Command "Invoke-GetItem -itemid $itemId" -FileName 'getitemdirect_1.json' - $result = Get-ProjectItemDirect -ItemId $itemId + $result = Get-ProjectItemDirect -ItemId $itemId -NoCache Assert-AreEqual -Expected $itemId -Presented $result.id Assert-AreEqual -Expected $itemUrl -Presented $result.url Assert-AreEqual -Expected $contentId -Presented $result.contentId } +function Test_GetItemDirect_SUCCESS_WithCache{ + Assert-NotImplemented +} function Test_ShowProjectItem_SUCCESS{ diff --git a/private/projectDatabase/project_database_call.ps1 b/private/projectDatabase/project_database_call.ps1 index c136a11..8474563 100644 --- a/private/projectDatabase/project_database_call.ps1 +++ b/private/projectDatabase/project_database_call.ps1 @@ -94,7 +94,8 @@ function GetItemInfo { # TODO: Situation where we are updating an item that is not cached and therefore type is not known if (-not $item.type) { # Single fetch item from api - $item = Get-ProjectItemDirect -ItemId $ItemId + # Avoid caching as we are beeing called from a Database modification context + $item = Get-ProjectItemDirect -ItemId $ItemId -NoCache } if ( $null -eq $item) { diff --git a/public/project_item.ps1 b/public/project_item.ps1 index b7ca197..3d34f22 100644 --- a/public/project_item.ps1 +++ b/public/project_item.ps1 @@ -32,9 +32,11 @@ function Get-ProjectItem{ # If item not found on cache get it directly if($null -eq $item){ - $item = Get-ProjectItemDirect -ItemId $ItemId + $item = Get-ProjectItemDirect -ItemId $ItemId -NoCache - Set-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -Item $item + Set-Item $db $item + + Save-ProjectDatabase -Database $db } return $item @@ -57,6 +59,24 @@ function Set-ProjectItem{ Save-ProjectDatabase -Database $db } +function Remove-ProjectItem{ + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline,Position = 0)][string]$ItemId, + [Parameter()][string]$Owner, + [Parameter()][string]$ProjectNumber + ) + + ($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} + + $db = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber + + Remove-Item $db $itemId + + Save-ProjectDatabase -Database $db +} + function Find-ProjectItem{ [CmdletBinding()] param( @@ -173,7 +193,8 @@ function Add-ProjectItemDirect{ param( [Parameter(ValueFromPipeline,Position = 0)][string]$Url, [Parameter()][string]$Owner, - [Parameter()][string]$ProjectNumber + [Parameter()][string]$ProjectNumber, + [Parameter()][switch]$NoCache ) process{ @@ -182,7 +203,8 @@ function Add-ProjectItemDirect{ if([string]::IsNullOrWhiteSpace($owner) -or [string]::IsNullOrWhiteSpace($ProjectNumber)){ "Owner and ProjectNumber are required" | Write-MyError; return $null} # Get project id - $projectId = Get-ProjectId -Owner $Owner -ProjectNumber $ProjectNumber + $db = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber + $projectId = $db.ProjectId if(-not $projectId){ "Project ID not found for Owner [$Owner] and ProjectNumber [$ProjectNumber]" | Write-MyError return $null @@ -204,9 +226,24 @@ function Add-ProjectItemDirect{ return $null } - if($response.data.addProjectV2ItemById.item.id) + $item = $response.data.addProjectV2ItemById.item + + if($item) { - $ret = $response.data.addProjectV2ItemById.item.id + $ret = $item.id + + if(! $NoCache){ + "Adding item [$ret] to cache" | Write-Verbose + + $item = $item | Convert-NodeItemToHash + + Set-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -Item $item + + Save-ProjectDatabase -Database $db + + } + + "Setting global variable ItemId to [$ret]" | Write-Verbose $global:ItemId = $ret return $ret @@ -224,7 +261,8 @@ function Remove-ProjectItemDirect{ param( [Parameter()][string]$Owner, [Parameter()][string]$ProjectNumber, - [Parameter(Mandatory, ValueFromPipeline, Position = 0)][string]$ItemId + [Parameter(Mandatory, ValueFromPipeline, Position = 0)][string]$ItemId, + [Parameter()][switch]$NoCache ) begin{ @@ -232,9 +270,11 @@ function Remove-ProjectItemDirect{ if([string]::IsNullOrWhiteSpace($owner) -or [string]::IsNullOrWhiteSpace($ProjectNumber)){ "Owner and ProjectNumber are required" | Write-MyError; return $null} # Get project id - $projectId = Get-ProjectId -Owner $Owner -ProjectNumber $ProjectNumber - if(-not $projectId){ - "Project ID not found for Owner [$Owner] and ProjectNumber [$ProjectNumber]" | Write-MyError + $db = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber + if($db){ + $projectId = $db.ProjectId + } else { + throw "Project not found for Owner [$Owner] and ProjectNumber [$ProjectNumber]" } } @@ -259,7 +299,16 @@ function Remove-ProjectItemDirect{ return $null } - return $response.data.deleteProjectV2Item.deletedItemId + $ret = $response.data.deleteProjectV2Item.deletedItemId + + if(! $NoCache){ + # Remove item from cache + "Removing item [$ItemId] from cache" | Write-Verbose + Remove-Item $db $ItemId + Save-ProjectDatabase $db + } + + return $ret } } Export-ModuleMember -Function Remove-ProjectItemDirect @@ -267,7 +316,8 @@ function Remove-ProjectItemDirect{ function Get-ProjectItemDirect{ [CmdletBinding()] param( - [Parameter(Mandatory,ValueFromPipeline,Position = 0)][string]$ItemId + [Parameter(Mandatory,ValueFromPipeline,Position = 0)][string]$ItemId, + [Parameter()][switch]$NoCache ) $response = Invoke-MyCommand -Command GetItem -Parameters @{ @@ -287,6 +337,12 @@ function Get-ProjectItemDirect{ $item = $response.data.node | Convert-NodeItemToHash + if(! $NoCache){ + "Adding item [$ItemId] to cache" | Write-Verbose + Set-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -Item $item + Save-ProjectDatabase -Owner $Owner -ProjectNumber $ProjectNumber + } + return $item } Export-ModuleMember -Function Get-ProjectItemDirect From 32d031d715717094920e0f44766c83d2458a0e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20=28Dibildos=29=20Gonz=C3=A1lez?= Date: Mon, 15 Sep 2025 08:55:57 +0200 Subject: [PATCH 08/10] refactor(database): remove NoCache option from Get-ProjectItemDirect calls --- Test/public/project_item.test.ps1 | 2 +- private/projectDatabase/project_database_call.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Test/public/project_item.test.ps1 b/Test/public/project_item.test.ps1 index db9b630..56809e4 100644 --- a/Test/public/project_item.test.ps1 +++ b/Test/public/project_item.test.ps1 @@ -359,7 +359,7 @@ function Test_GetItemDirect_SUCCESS{ MockCallJson -Command "Invoke-GetItem -itemid $itemId" -FileName 'getitemdirect_1.json' - $result = Get-ProjectItemDirect -ItemId $itemId -NoCache + $result = Get-ProjectItemDirect -ItemId $itemId Assert-AreEqual -Expected $itemId -Presented $result.id Assert-AreEqual -Expected $itemUrl -Presented $result.url diff --git a/private/projectDatabase/project_database_call.ps1 b/private/projectDatabase/project_database_call.ps1 index 8474563..74ee7c3 100644 --- a/private/projectDatabase/project_database_call.ps1 +++ b/private/projectDatabase/project_database_call.ps1 @@ -95,7 +95,7 @@ function GetItemInfo { if (-not $item.type) { # Single fetch item from api # Avoid caching as we are beeing called from a Database modification context - $item = Get-ProjectItemDirect -ItemId $ItemId -NoCache + $item = Get-ProjectItemDirect -ItemId $ItemId } if ( $null -eq $item) { From 310ffd301b764ae8772e11d8ae5da0d05f16f700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20=28Dibildos=29=20Gonz=C3=A1lez?= Date: Mon, 15 Sep 2025 08:56:12 +0200 Subject: [PATCH 09/10] fix(database): ensure Remove-Item processes the database items correctly --- private/projectDatabase/project_database_Item.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/private/projectDatabase/project_database_Item.ps1 b/private/projectDatabase/project_database_Item.ps1 index d490202..1075034 100644 --- a/private/projectDatabase/project_database_Item.ps1 +++ b/private/projectDatabase/project_database_Item.ps1 @@ -58,7 +58,9 @@ function Remove-Item{ [Parameter(ValueFromPipeline, Position = 1)][string]$ItemId ) - $db.Items.Remove($ItemId) | Out-Null + process{ + $Database.items.Remove($ItemId) | Out-Null + } } From 668c97c5ed0bbf95001fa45293dc6a1e8f725e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20=28Dibildos=29=20Gonz=C3=A1lez?= Date: Mon, 15 Sep 2025 08:56:27 +0200 Subject: [PATCH 10/10] refactor(database): improve Get-ProjectItem and Remove-ProjectItem for better database handling --- public/project_item.ps1 | 70 +++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/public/project_item.ps1 b/public/project_item.ps1 index 3d34f22..50eef53 100644 --- a/public/project_item.ps1 +++ b/public/project_item.ps1 @@ -19,27 +19,43 @@ function Get-ProjectItem{ [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} - - - # Get Item from Project database - $db = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber -Force:$Force + begin{ + ($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} + + + # Get Item from Project database + $db = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber -Force:$Force - if($db){ - $item = Get-Item $db $itemId + # Durty flag + $durty= $false } - # If item not found on cache get it directly - if($null -eq $item){ - $item = Get-ProjectItemDirect -ItemId $ItemId -NoCache + process{ + if($db){ + $item = Get-Item $db $itemId + } + if($null -eq $item){ + "Item [$ItemId] not found in cache, fetching from API" | Write-Verbose + + # Get direct. No cache as we are in a database modification context + $item = Get-ProjectItemDirect -ItemId $ItemId - Set-Item $db $item + # Add to database + Set-Item $db $item + $durty = $true + } - Save-ProjectDatabase -Database $db + return $item + } + + end{ + if($durty){ + "Saving durty database" | Write-Verbose + Save-ProjectDatabase -Database $db + } } - return $item } Export-ModuleMember -Function Get-ProjectItem function Set-ProjectItem{ @@ -67,14 +83,21 @@ function Remove-ProjectItem{ [Parameter()][string]$ProjectNumber ) - ($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} + begin{ + ($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} + + $db = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber + } - $db = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber + process{ + Remove-Item $db $itemId + } - Remove-Item $db $itemId + end{ + Save-ProjectDatabase -Database $db + } - Save-ProjectDatabase -Database $db } function Find-ProjectItem{ @@ -316,8 +339,7 @@ function Remove-ProjectItemDirect{ function Get-ProjectItemDirect{ [CmdletBinding()] param( - [Parameter(Mandatory,ValueFromPipeline,Position = 0)][string]$ItemId, - [Parameter()][switch]$NoCache + [Parameter(Mandatory,ValueFromPipeline,Position = 0)][string]$ItemId ) $response = Invoke-MyCommand -Command GetItem -Parameters @{ @@ -337,12 +359,6 @@ function Get-ProjectItemDirect{ $item = $response.data.node | Convert-NodeItemToHash - if(! $NoCache){ - "Adding item [$ItemId] to cache" | Write-Verbose - Set-ProjectItem -Owner $Owner -ProjectNumber $ProjectNumber -Item $item - Save-ProjectDatabase -Owner $Owner -ProjectNumber $ProjectNumber - } - return $item } Export-ModuleMember -Function Get-ProjectItemDirect