diff --git a/GitHubCustomersHelper.psd1 b/GitHubCustomersHelper.psd1 index b4ba6f3..374e377 100644 --- a/GitHubCustomersHelper.psd1 +++ b/GitHubCustomersHelper.psd1 @@ -51,7 +51,7 @@ Copyright = '(c) rulasg. All rights reserved.' # ProcessorArchitecture = '' # Modules that must be imported into the global environment prior to importing this module -# RequiredModules = @() +RequiredModules = @(@{ModuleName="InvokeHelper"; ModuleVersion="1.2.4"}) # Assemblies that must be loaded prior to importing this module # RequiredAssemblies = @() diff --git a/Test/helper/module.helper.ps1 b/Test/helper/module.helper.ps1 new file mode 100644 index 0000000..670d090 --- /dev/null +++ b/Test/helper/module.helper.ps1 @@ -0,0 +1,190 @@ +# Helper for module variables + +function Find-ModuleRootPath{ + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline,Position = 0)] + [string]$Path + ) + + $path = Convert-Path -Path $Path + + while (-not [string]::IsNullOrWhiteSpace($Path)){ + $psd1 = Get-ChildItem -Path $Path -Filter *.psd1 | Select-Object -First 1 + + if ($psd1 | Test-Path) { + + if($psd1.BaseName -eq "Test"){ + #foudn testing module. Continue + $path = $path | Split-Path -Parent + continue + } + + # foudn module + return $path + } + # folder without psd1 file + $path = $path | Split-Path -Parent + } + + # Path is null. Reached driver root. Module not found + return $null +} + +$MODULE_ROOT_PATH = $PSScriptRoot | Find-ModuleRootPath +$MODULE_NAME = (Get-ChildItem -Path $MODULE_ROOT_PATH -Filter *.psd1 | Select-Object -First 1).BaseName + +# Helper for module variables + +# Folders names that IncludeHelper may add content to +$VALID_INCLUDE_FOLDER_NAMES = @( + 'Root', + 'Include', + 'DevContainer', + 'WorkFlows', + 'GitHub', + # 'Config', + 'Helper', + # 'Private', + # 'Public', + 'Tools', + + 'TestRoot', + # 'TestConfig' + 'TestInclude', + 'TestHelper', + # 'TestPrivate', + # 'TestPublic', + + "TestHelperRoot", + "TestHelperPrivate", + "TestHelperPublic" + + "VsCode" +) + +# Folders names that IncludeHelper should not add content to. +# In this folders is the module code itself +$VALID_MODULE_FOLDER_NAMES = @( + 'Config', + 'Private', + 'Public', + 'TestConfig' + 'TestPrivate', + 'TestPublic' +) + +$VALID_FOLDER_NAMES = $VALID_INCLUDE_FOLDER_NAMES + $VALID_MODULE_FOLDER_NAMES + +class ValidFolderNames : System.Management.Automation.IValidateSetValuesGenerator { + [String[]] GetValidValues() { + return $script:VALID_FOLDER_NAMES + } +} + +function Get-Ps1FullPath{ + [CmdletBinding()] + param( + [Parameter(Mandatory,Position = 0)][string]$Name, + [Parameter(Position = 1)][ValidateSet([ValidFolderNames])][string]$FolderName, + [Parameter(Position = 0)][string]$ModuleRootPath + ) + + # If folderName is not empty + if($FolderName -ne $null){ + $folder = Get-ModuleFolder -FolderName $FolderName -ModuleRootPath $ModuleRootPath + $path = $folder | Join-Path -ChildPath $Name + } else { + $path = $Name + } + + # Check if file exists + if(-Not (Test-Path $path)){ + throw "File $path not found" + } + + # Get Path item + $item = Get-item -Path $path + + return $item +} +function Get-ModuleRootPath{ + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$ModuleRootPath + ) + + # if ModuleRootPath is not provided, default to local module path + if([string]::IsNullOrWhiteSpace($ModuleRootPath)){ + $ModuleRootPath = $MODULE_ROOT_PATH + } + + # Convert to full path + $ModuleRootPath = Convert-Path -Path $ModuleRootPath + + return $ModuleRootPath +} + +function Get-ModuleName{ + [CmdletBinding()] + param( + [Parameter(Position = 0)] [string]$ModuleRootPath + ) + + $ModuleRootPath = Get-ModuleRootPath -ModuleRootPath $ModuleRootPath + + $MODULE_NAME = (Get-ChildItem -Path $MODULE_ROOT_PATH -Filter *.psd1 | Select-Object -First 1).BaseName + + + return $MODULE_NAME +} + +function Get-ModuleFolder{ + [CmdletBinding()] + param( + [Parameter(Mandatory,Position = 0)][ValidateSet([ValidFolderNames])][string]$FolderName, + [Parameter(Position = 1)][string]$ModuleRootPath + ) + + $ModuleRootPath = Get-ModuleRootPath -ModuleRootPath $ModuleRootPath + + # TestRootPath + $testRootPath = $ModuleRootPath | Join-Path -ChildPath "Test" + $testHelperRootPath = $ModuleRootPath | Join-Path -ChildPath "tools/Test_Helper" + + switch ($FolderName){ + + # VALID_INCLUDE_FOLDER_NAMES + 'Root' { $moduleFolder = $ModuleRootPath } + 'Include' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "include" } + 'DevContainer'{ $moduleFolder = $ModuleRootPath | Join-Path -ChildPath ".devcontainer" } + 'WorkFlows' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath ".github/workflows" } + 'GitHub' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath ".github" } + 'Helper' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "helper" } + 'Tools' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "tools" } + + 'TestRoot' { $moduleFolder = $testRootPath } + 'TestInclude' { $moduleFolder = $testRootPath | Join-Path -ChildPath "include" } + 'TestHelper' { $moduleFolder = $testRootPath | Join-Path -ChildPath "helper" } + + 'TestHelperRoot' { $moduleFolder = $testHelperRootPath } + 'TestHelperPrivate' { $moduleFolder = $testHelperRootPath | Join-Path -ChildPath "private" } + 'TestHelperPublic' { $moduleFolder = $testHelperRootPath | Join-Path -ChildPath "public" } + + "VsCode" { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath ".vscode" } + + # VALID_MODULE_FOLDER_NAMES + 'Config' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "config" } + 'Private' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "private" } + 'Public' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "public" } + 'TestConfig' { $moduleFolder = $testRootPath | Join-Path -ChildPath "config" } + 'TestPrivate' { $moduleFolder = $testRootPath | Join-Path -ChildPath "private" } + 'TestPublic' { $moduleFolder = $testRootPath | Join-Path -ChildPath "public" } + + + default{ + throw "Folder [$FolderName] is unknown" + } + } + return $moduleFolder +} Export-ModuleMember -Function Get-ModuleFolder \ No newline at end of file diff --git a/Test/include/InvokeMockList.ps1 b/Test/include/InvokeMockList.ps1 new file mode 100644 index 0000000..85ea7bd --- /dev/null +++ b/Test/include/InvokeMockList.ps1 @@ -0,0 +1,54 @@ + +# $MockCommandFile = $testRootPath | Join-Path -ChildPath "mockfiles.log" + +# function Trace-MockCommandFile{ +# [CmdletBinding()] +# param( +# [string] $Command, +# [string] $FileName +# ) + +# # read content +# $content = readMockCommandFile + +# # Check that the entry is already there +# $result = $content | Where-Object{$_.command -eq $command} +# if($null -ne $result) {return} + +# # add entry +# $new = @{ +# Command = $command +# FileName = $fileName +# } + +# $ret = @() +# $ret += $content +# $ret += $new + +# # Save list +# writeMockCommandFile -Content $ret +# } + +# function readMockCommandFile{ + +# # Return empty list if the file does not exist +# if(-not (Test-Path -Path $MockCommandFile)){ +# return @() +# } + +# $ret = Get-Content -Path $MockCommandFile | ConvertFrom-Json + +# # return an empty aray if content does not exists +# $ret = $ret ?? @() + +# return $ret +# } + +# function writeMockCommandFile($Content){ + +# $list = $Content | ConvertTo-Json + +# $sorted = $list | Sort-Object fileName + +# $sorted | Out-File -FilePath $MockCommandFile +# } \ No newline at end of file diff --git a/Test/include/invokeCommand.mock.ps1 b/Test/include/invokeCommand.mock.ps1 new file mode 100644 index 0000000..3bf6773 --- /dev/null +++ b/Test/include/invokeCommand.mock.ps1 @@ -0,0 +1,365 @@ + +# INVOKE COMMAND MOCK +# +# This includes help commands to mock invokes in a test module +# +# Set $env:TraceInvokeMock to trave Invoke dependencies +# "traceInvoke.log" | %{touch $_ ; $env:TraceInvokeMock = $_ | Resolve-Path} +# +# THIS INCLUDE REQURED module.helper.ps1 +if(-not $MODULE_NAME){ throw "Missing MODULE_NAME varaible initialization. Check for module.helerp.ps1 file." } +if(-not $MODULE_ROOT_PATH){ throw "Missing MODULE_ROOT_PATH varaible initialization. Check for module.helerp.ps1 file." } + + +$testRootPath = $MODULE_ROOT_PATH | Join-Path -ChildPath 'Test' +$MOCK_PATH = $testRootPath | Join-Path -ChildPath 'private' -AdditionalChildPath 'mocks' + +$MODULE_INVOKATION_TAG = "$($MODULE_NAME)Module" +$MODULE_INVOKATION_TEST_TAG = "$($MODULE_NAME)TestModule" +$MODULE_INVOKATION_TAG_MOCK = "$($MODULE_INVOKATION_TAG)_Mock" + +$TraceInvokeFilePathCommand = "Get-$($MODULE_NAME)TraceInvokeFilePath" + +function Invoke-ModuleNameGetTraceInvokeFilePath{ + [CmdletBinding()] + param() + + $filePath = $testRootPath | Join-Path -ChildPath "traceInvoke.log" + + return $filePath +} +Copy-Item -path Function:Invoke-ModuleNameGetTraceInvokeFilePath -Destination Function:"Invoke-$($MODULE_NAME)GetTraceInvokeFilePath" +Export-ModuleMember -Function "Invoke-$($MODULE_NAME)GetTraceInvokeFilePath" +InvokeHelper\Set-InvokeCommandAlias -Alias $TraceInvokeFilePathCommand -Command "Invoke-$($MODULE_NAME)GetTraceInvokeFilePath" -Tag $MODULE_INVOKATION_TEST_TAG + +function Trace-InvokeCommandAlias{ + [CmdletBinding()] + param( + [Parameter(Mandatory,Position=0)][string]$Alias + ) + + $filePath = Invoke-MyCommand -Command $TraceInvokeFilePathCommand + + if(! (Test-Path $filePath)) {return} + + $content = Get-Content $filePath + + $content = $content ?? @() + + if($content.Contains($Alias)) { return} + + $alias | Out-File $filePath -Append -Force + +} + +function Set-InvokeCommandMock{ + [CmdletBinding()] + param( + [Parameter(Mandatory,Position=0)][string]$Alias, + [Parameter(Mandatory,Position=1)][string]$Command + ) + + InvokeHelper\Set-InvokeCommandAlias -Alias $Alias -Command $Command -Tag $MODULE_INVOKATION_TAG_MOCK + + Trace-InvokeCommandAlias $alias +} + +function Reset-InvokeCommandMock{ + [CmdletBinding()] + param() + + # Remove all mocks + InvokeHelper\Reset-InvokeCommandAlias -Tag $MODULE_INVOKATION_TAG_MOCK + + # Disable all dependecies of the library + Disable-InvokeCommandAlias -Tag $MODULE_INVOKATION_TAG + + # Clear Enviroment variables used + Get-Variable -scope Global -Name "$($MODULE_INVOKATION_TAG_MOCK)_*" | Remove-Variable -Force -Scope Global + +} Export-ModuleMember -Function Reset-InvokeCommandMock + +function Enable-InvokeCommandAliasModule{ + [CmdletBinding()] + param() + + Enable-InvokeCommandAlias -Tag $MODULE_INVOKATION_TAG +} Export-ModuleMember -Function Enable-InvokeCommandAliasModule + +function MockCall{ + param( + [Parameter(Position=0)][string] $command, + [Parameter(Position=1)][string] $filename + ) + + Assert-MockFileNotfound $fileName + + Trace-MockCommandFile -Command $command -Filename $filename + + Set-InvokeCommandMock -Alias $command -Command "Get-MockFileContent -filename $filename" +} + +function MockCallAsync{ + param( + [Parameter(Position=0)][string] $command, + [Parameter(Position=1)][string] $filename + ) + + Assert-MockFileNotfound $fileName + + Trace-MockCommandFile -Command $command -Filename $filename + + $moduleTest = $PSScriptRoot | Split-Path -Parent | Convert-Path + + Set-InvokeCommandMock -Alias $command -Command "Import-Module $moduleTest ; Get-MockFileContent -filename $filename" +} + +function MockCallJson{ + param( + [Parameter(Position=0)][string] $command, + [Parameter(Position=1)][string] $filename, + [Parameter()][switch] $AsHashtable + + ) + + Assert-MockFileNotfound $fileName + + Trace-MockCommandFile -Command $command -Filename $filename + + $asHashTableString = $AsHashtable ? '$true' : '$false' + + $commandstr ='Get-MockFileContentJson -filename {filename} -AsHashtable:{asHashTableString}' + $commandstr = $commandstr -replace "{asHashTableString}", $asHashTableString + $commandstr = $commandstr -replace "{filename}", $filename + + Set-InvokeCommandMock -Alias $command -Command $commandstr +} + +function MockCallJsonAsync{ + param( + [Parameter(Position=0)][string] $command, + [Parameter(Position=1)][string] $filename + + ) + + Assert-MockFileNotfound $fileName + + Trace-MockCommandFile -Command $command -Filename $filename + + $moduleTest = $PSScriptRoot | Split-Path -Parent | Convert-Path + + Set-InvokeCommandMock -Alias $command -Command "Import-Module $moduleTest ; Get-MockFileContentJson -filename $filename" +} + +function Get-MockFileFullPath{ + param( + [parameter(Mandatory,Position=0)][string] $fileName + ) + + $filePath = $MOCK_PATH | Join-Path -ChildPath $fileName + + return $filePath +} Export-ModuleMember -Function Get-MockFileFullPath + +function Get-MockFileContent{ + param( + [parameter(Mandatory,Position=0)][string] $fileName + ) + + Assert-MockFileNotfound $FileName + + $filePath = Get-MockFileFullPath -fileName $fileName + + $content = Get-Content -Path $filePath | Out-String + + return $content +} Export-ModuleMember -Function Get-MockFileContent + +function Get-MockFileContentJson{ + param( + [parameter(Mandatory,Position=0)][string] $fileName, + [Parameter()][switch] $AsHashtable + ) + + Assert-MockFileNotfound $FileName + + $content = Get-MockFileContent -fileName $filename | ConvertFrom-Json -AsHashtable:$AsHashtable -Depth 100 + + return $content +} Export-ModuleMember -Function Get-MockFileContentJson + +function MockCallToString{ + param( + [Parameter(Position=0)][string] $command, + [Parameter(Position=1)][string] $OutString + ) + + $outputstring = 'echo "{output}"' + $outputstring = $outputstring -replace "{output}", $OutString + + Set-InvokeCommandMock -Alias $command -Command $outputstring +} + + +function MockCallToObject{ + param( + [Parameter(Position=0)][string] $command, + [Parameter(Position=1)][object] $OutObject + ) + + $random = [System.Guid]::NewGuid().ToString() + $varName = "$MODULE_INVOKATION_TAG_MOCK" + "_$random" + + Set-Variable -Name $varName -Value $OutObject -Scope Global + + Set-InvokeCommandMock -Alias $command -Command "(Get-Variable -Name $varName -Scope Global).Value" +} + +function MockCallToNull{ + param( + [Parameter(Position=0)][string] $command + ) + + Set-InvokeCommandMock -Alias $command -Command 'return $null' +} + +function MockCallThrow{ + param( + [Parameter(Position=0)][string] $command, + [Parameter(Position=1)][string] $ExceptionMessage + + ) + + $mockCommand = 'throw "{message}"' + $mockCommand = $mockCommand -replace "{message}", $exceptionMessage + + Set-InvokeCommandMock -Alias $command -Command $mockCommand +} + +function MockCallExpression{ + param( + [Parameter(Position=0)][string] $command, + [Parameter(Position=1)][string] $expression + ) + + $mockCommand = @' + Invoke-Expression -Command '{expression}' +'@ + $mockCommand = $mockCommand -replace "{expression}", $expression + + Set-InvokeCommandMock -Alias $command -Command $expression +} + + +function Save-InvokeAsMockFile{ + param( + [Parameter(Mandatory=$true)] [string]$Command, + [Parameter(Mandatory=$true)] [string]$FileName, + [Parameter(Mandatory=$false)] [switch]$Force + ) + + $filePath = Get-MockFileFullPath -fileName $fileName + + $result = Invoke-Expression -Command $Command + + $json = $result | ConvertTo-Json -Depth 100 + + $json | Out-File -FilePath $filePath + + Write-Host $FileName +} Export-ModuleMember -Function Save-InvokeAsMockFile + +function Save-InvokeAsMockFileJson{ + param( + [Parameter(Mandatory=$true)] [string]$Command, + [Parameter(Mandatory=$true)] [string]$FileName + ) + + $filePath = Get-MockFileFullPath -fileName $fileName + + $result = Invoke-Expression -Command $Command + + $result | Out-File -FilePath $filePath + + Write-Host $FileName +} Export-ModuleMember -Function Save-InvokeAsMockFileJson + +function Assert-MockFileNotfound{ + param( + [Parameter(Mandatory=$true,Position=0)] [string]$FileName + ) + + $filePath = Get-MockFileFullPath -fileName $fileName + + if(-Not (Test-Path -Path $filePath)){ + throw "File not found: $fileName" + } + + # Throw if $file.name and the $filename parameter have different case + # We need to check this to avoid test bugs for mock files not found on linux that the FS is case sensitive + $file = Get-ChildItem -Path $MOCK_PATH | Where-Object { $_.Name.ToLower() -eq $fileName.ToLower() } + if($file.name -cne $fileName){ + Write-host "Wait-Debugger - File not found or wrong case - $($file.name)" + Wait-Debugger + throw "File not found or wrong case name. Expected[ $filename ] - Found[$( $file.name )]" + } +} + + +#region Mock Command File Trace + +$MockCommandFile = $testRootPath | Join-Path -ChildPath "mockfiles.log" + +function Trace-MockCommandFile{ + [CmdletBinding()] + param( + [string] $Command, + [string] $FileName + ) + + # read content + $content = readMockCommandFile + + # Check that the entry is already there + $result = $content | Where-Object{$_.command -eq $command} + if($null -ne $result) {return} + + # add entry + $new = @{ + Command = $command + FileName = $fileName + } + + $ret = @() + $ret += $content + $ret += $new + + # Save list + writeMockCommandFile -Content $ret +} + +function readMockCommandFile{ + + # Return empty list if the file does not exist + if(-not (Test-Path -Path $MockCommandFile)){ + return @() + } + + $ret = Get-Content -Path $MockCommandFile | ConvertFrom-Json + + # return an empty aray if content does not exists + $ret = $ret ?? @() + + return $ret +} + +function writeMockCommandFile($Content){ + + $list = $Content | ConvertTo-Json + + $sorted = $list | Sort-Object fileName + + $sorted | Out-File -FilePath $MockCommandFile +} + +# End region Mock Command File Trace \ No newline at end of file diff --git a/Test/mockfiles.log b/Test/mockfiles.log new file mode 100644 index 0000000..b5b3399 --- /dev/null +++ b/Test/mockfiles.log @@ -0,0 +1,30 @@ +[ + { + "Command": "Find-Project -owner githubcustomers -pattern creator:testuser", + "FileName": "testuser-find-project.json" + }, + { + "Command": "Get-ProjectItems -owner githubcustomers -projectNumber 3023", + "FileName": "get-projectitems-githubcustomers-3023.json" + }, + { + "Command": "Get-ProjectItems -owner githubcustomers -projectNumber 2683", + "FileName": "get-projectitems-githubcustomers-2683.json" + }, + { + "Command": "Get-ProjectItems -owner githubcustomers -projectNumber 2988", + "FileName": "get-projectitems-githubcustomers-2988.json" + }, + { + "Command": "Get-ProjectItems -owner githubcustomers -projectNumber 2683 -IncludeDone", + "FileName": "get-projectitems-githubcustomers-2683.json" + }, + { + "Command": "Get-ProjectItems -owner githubcustomers -projectNumber 2988 -IncludeDone", + "FileName": "get-projectitems-githubcustomers-2988.json" + }, + { + "Command": "Get-ProjectItems -owner githubcustomers -projectNumber 3023 -IncludeDone", + "FileName": "get-projectitems-githubcustomers-3023.json" + } +] diff --git a/Test/private/mocks/get-projectitems-githubcustomers-2683.json b/Test/private/mocks/get-projectitems-githubcustomers-2683.json new file mode 100644 index 0000000..d9f3683 --- /dev/null +++ b/Test/private/mocks/get-projectitems-githubcustomers-2683.json @@ -0,0 +1,99 @@ +[ + { + "RepositoryName": "bit21", + "Status": "Done", + "urlPanel": "https://github.com/orgs/githubcustomers/projects/2683/views/1?pane=issue&itemId=111695760", + "comments": { + "url": "https://github.com/githubcustomers/bit21/issues/7#issuecomment-2864738616", + "author": "github-actions", + "createdAt": "2025-05-09T00:11:11", + "body": "No updates in 7 days", + "id": "2864738616", + "updatedAt": "2025-05-09T00:11:11" + }, + "Labels": "\"stale-issue\"", + "projectId": "PVT_kwDOAOnouM4AzN5W", + "state": "CLOSED", + "Title": "Access Review: May 01 2025", + "projectUrl": "https://github.com/orgs/githubcustomers/projects/2683", + "number": 7, + "contentId": "I_kwDON9o2oM600IIw", + "body": "# Access Review: May 01 2025\n\nAs part of our shared responsibility, we place great importance on managing\naccess to this repository during collaborations with GitHub. We need our\ncustomers to take responsibility once invited to this repository, to monitor and\nmanage access. This includes ensuring the right individuals, teams, or\ncontractors within your organization have appropriate access.\n\nTo support you in this task, each month on the first day a reminder will be\nissued. This will include a list of all collaborators who currently have access\nto this repository. Please review this list to manage collaborators, adding or\nremoving members as necessary to maintain security.\n\n## Collaborators\n\n| **Username** | **Permission** |\n| ------------ | -------------- |\n| `raulgeu` | WRITE", + "createdAt": "2025-05-01T10:02:10", + "urlContent": "https://github.com/githubcustomers/bit21/issues/7", + "updatedAt": "2025-05-23T00:11:21", + "id": "PVTI_lADOAOnouM4AzN5WzgaoV5A", + "databaseId": "111695760", + "url": "https://github.com/githubcustomers/bit21/issues/7", + "type": "Issue", + "Repository": "https://github.com/githubcustomers/bit21", + "commentLast": { + "url": "https://github.com/githubcustomers/bit21/issues/7#issuecomment-2864738616", + "author": "github-actions", + "createdAt": "2025-05-09T00:11:11", + "body": "No updates in 7 days", + "id": "2864738616", + "updatedAt": "2025-05-09T00:11:11" + }, + "RepositoryOwner": "githubcustomers" + }, + { + "RepositoryName": "bit21", + "Status": "In Progress", + "urlPanel": "https://github.com/orgs/githubcustomers/projects/2683/views/1?pane=issue&itemId=161870439", + "Comment": "the value of a comment2", + "projectUrl": "https://github.com/orgs/githubcustomers/projects/2683", + "projectId": "PVT_kwDOAOnouM4AzN5W", + "state": "OPEN", + "Title": "Prueba de ediccion de isue", + "createdAt": "2026-03-03T19:54:57", + "number": 28, + "contentId": "I_kwDON9o2oM7vhGx-", + "body": "Este es el body de la issue", + "updatedAt": "2026-03-03T19:54:57", + "urlContent": "https://github.com/githubcustomers/bit21/issues/28", + "url": "https://github.com/githubcustomers/bit21/issues/28", + "id": "PVTI_lADOAOnouM4AzN5Wzgml8mc", + "databaseId": "161870439", + "type": "Issue", + "Repository": "https://github.com/githubcustomers/bit21", + "RepositoryOwner": "githubcustomers" + }, + { + "RepositoryName": "bit21", + "urlPanel": "https://github.com/orgs/githubcustomers/projects/2683/views/1?pane=issue&itemId=160425591", + "comments": { + "url": "https://github.com/githubcustomers/bit21/issues/26#issuecomment-4008649289", + "author": "github-actions", + "createdAt": "2026-03-06T00:21:01", + "body": "No updates in 7 days", + "id": "4008649289", + "updatedAt": "2026-03-06T00:21:01" + }, + "Labels": "\"stale-issue\"", + "projectId": "PVT_kwDOAOnouM4AzN5W", + "state": "OPEN", + "Title": "Demo de Julio", + "projectUrl": "https://github.com/orgs/githubcustomers/projects/2683", + "number": 26, + "contentId": "I_kwDON9o2oM7uNP2n", + "body": "", + "createdAt": "2026-02-26T16:16:28", + "urlContent": "https://github.com/githubcustomers/bit21/issues/26", + "updatedAt": "2026-03-06T00:21:01", + "id": "PVTI_lADOAOnouM4AzN5WzgmP5nc", + "databaseId": "160425591", + "url": "https://github.com/githubcustomers/bit21/issues/26", + "type": "Issue", + "Repository": "https://github.com/githubcustomers/bit21", + "commentLast": { + "url": "https://github.com/githubcustomers/bit21/issues/26#issuecomment-4008649289", + "author": "github-actions", + "createdAt": "2026-03-06T00:21:01", + "body": "No updates in 7 days", + "id": "4008649289", + "updatedAt": "2026-03-06T00:21:01" + }, + "RepositoryOwner": "githubcustomers" + } +] diff --git a/Test/private/mocks/get-projectitems-githubcustomers-2988.json b/Test/private/mocks/get-projectitems-githubcustomers-2988.json new file mode 100644 index 0000000..e69de29 diff --git a/Test/private/mocks/get-projectitems-githubcustomers-3023.json b/Test/private/mocks/get-projectitems-githubcustomers-3023.json new file mode 100644 index 0000000..2046352 --- /dev/null +++ b/Test/private/mocks/get-projectitems-githubcustomers-3023.json @@ -0,0 +1,65 @@ +[ + { + "RepositoryName": "kk", + "Status": "Todo", + "urlPanel": "https://github.com/orgs/githubcustomers/projects/3023/views/1?pane=issue&itemId=163618956", + "projectUrl": "https://github.com/orgs/githubcustomers/projects/3023", + "projectId": "PVT_kwDOAOnouM4BFJgO", + "state": "OPEN", + "Title": "Case for customer 1 in repo kk 2", + "createdAt": "2026-03-09T15:57:36", + "number": 3, + "contentId": "I_kwDORU1g0c7xL4O-", + "body": "", + "updatedAt": "2026-03-09T15:57:36", + "urlContent": "https://github.com/githubcustomers/kk/issues/3", + "url": "https://github.com/githubcustomers/kk/issues/3", + "id": "PVTI_lADOAOnouM4BFJgOzgnAoIw", + "databaseId": "163618956", + "type": "Issue", + "Repository": "https://github.com/githubcustomers/kk", + "RepositoryOwner": "githubcustomers" + }, + { + "RepositoryName": "kk", + "Status": "Todo", + "urlPanel": "https://github.com/orgs/githubcustomers/projects/3023/views/1?pane=issue&itemId=163618348", + "projectUrl": "https://github.com/orgs/githubcustomers/projects/3023", + "projectId": "PVT_kwDOAOnouM4BFJgO", + "state": "OPEN", + "Title": "Case for customer 1 in repo kk", + "createdAt": "2026-03-09T15:56:12", + "number": 2, + "contentId": "I_kwDORU1g0c7xL2DF", + "body": "", + "updatedAt": "2026-03-09T15:56:12", + "urlContent": "https://github.com/githubcustomers/kk/issues/2", + "url": "https://github.com/githubcustomers/kk/issues/2", + "id": "PVTI_lADOAOnouM4BFJgOzgnAniw", + "databaseId": "163618348", + "type": "Issue", + "Repository": "https://github.com/githubcustomers/kk", + "RepositoryOwner": "githubcustomers" + }, + { + "RepositoryName": "kk3", + "Status": "Todo", + "urlPanel": "https://github.com/orgs/githubcustomers/projects/3023/views/1?pane=issue&itemId=163618994", + "projectUrl": "https://github.com/orgs/githubcustomers/projects/3023", + "projectId": "PVT_kwDOAOnouM4BFJgO", + "state": "OPEN", + "Title": "Case for customer 1 in repo kk 3", + "createdAt": "2026-03-09T15:57:41", + "number": 4, + "contentId": "I_kwDORU1g0c7xL4bB", + "body": "", + "updatedAt": "2026-03-09T15:57:41", + "urlContent": "https://github.com/githubcustomers/kk3/issues/4", + "url": "https://github.com/githubcustomers/kk/issues/4", + "id": "PVTI_lADOAOnouM4BFJgOzgnAoLI", + "databaseId": "163618994", + "type": "Issue", + "Repository": "https://github.com/githubcustomers/kk3", + "RepositoryOwner": "githubcustomers" + } +] diff --git a/Test/private/mocks/testuser-find-project-3.json b/Test/private/mocks/testuser-find-project-3.json new file mode 100644 index 0000000..bacae24 --- /dev/null +++ b/Test/private/mocks/testuser-find-project-3.json @@ -0,0 +1,52 @@ +[ + { + "id": "PVT_kwDOAOnouM4BFJgO", + "title": "Customer_1", + "shortDescription": null, + "readme": null, + "number": 3023, + "url": "https://github.com/orgs/githubcustomers/projects/3023", + "template": false, + "createdAt": "2025-10-09T16:10:54Z", + "updatedAt": "2025-10-09T16:10:54Z", + "closedAt": null, + "repositories": { + "nodes": [] + } + }, + { + "id": "PVT_kwDOAOnouM4BCTX_", + "title": "[TEMPLATE] Customer Project", + "shortDescription": null, + "readme": null, + "number": 2988, + "url": "https://github.com/orgs/githubcustomers/projects/2988", + "template": true, + "createdAt": "2025-09-04T17:46:28Z", + "updatedAt": "2025-09-04T17:49:49Z", + "closedAt": null, + "repositories": { + "nodes": [] + } + }, + { + "id": "PVT_kwDOAOnouM4AzN5W", + "title": "BiT21", + "shortDescription": null, + "readme": null, + "number": 2683, + "url": "https://github.com/orgs/githubcustomers/projects/2683", + "template": false, + "createdAt": "2025-02-27T11:36:28Z", + "updatedAt": "2026-03-04T06:35:05Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "bit21", + "url": "https://github.com/githubcustomers/bit21" + } + ] + } + } +] diff --git a/Test/private/mocks/testuser-find-project.json b/Test/private/mocks/testuser-find-project.json new file mode 100644 index 0000000..c580c09 --- /dev/null +++ b/Test/private/mocks/testuser-find-project.json @@ -0,0 +1,312 @@ +[ + { + "id": "PVT_kwDOAOnouM4BP70V", + "title": "microsoft-spain-internal", + "shortDescription": null, + "readme": null, + "number": 3159, + "url": "https://github.com/orgs/githubcustomers/projects/3159", + "template": false, + "createdAt": "2026-02-23T13:24:08Z", + "updatedAt": "2026-02-23T13:24:08Z", + "closedAt": null, + "repositories": { + "nodes": [] + } + }, + { + "id": "PVT_kwDOAOnouM4BPmq_", + "title": "microsoft-spain-internal", + "shortDescription": null, + "readme": null, + "number": 3155, + "url": "https://github.com/orgs/githubcustomers/projects/3155", + "template": false, + "createdAt": "2026-02-19T11:24:47Z", + "updatedAt": "2026-02-19T11:24:47Z", + "closedAt": null, + "repositories": { + "nodes": [] + } + }, + { + "id": "PVT_kwDOAOnouM4BPbzF", + "title": "indra-group", + "shortDescription": null, + "readme": null, + "number": 3144, + "url": "https://github.com/orgs/githubcustomers/projects/3144", + "template": false, + "createdAt": "2026-02-17T14:55:39Z", + "updatedAt": "2026-02-17T15:06:03Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "indra-group", + "url": "https://github.com/githubcustomers/indra-group" + } + ] + } + }, + { + "id": "PVT_kwDOAOnouM4BO5UB", + "title": "santander-internal", + "shortDescription": null, + "readme": null, + "number": 3137, + "url": "https://github.com/orgs/githubcustomers/projects/3137", + "template": false, + "createdAt": "2026-02-11T06:14:38Z", + "updatedAt": "2026-03-06T00:44:12Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "santander-internal", + "url": "https://github.com/githubcustomers/santander-internal" + } + ] + } + }, + { + "id": "PVT_kwDOAOnouM4BKdRD", + "title": "sw-se-team", + "shortDescription": null, + "readme": null, + "number": 3075, + "url": "https://github.com/orgs/githubcustomers/projects/3075", + "template": false, + "createdAt": "2025-12-12T12:24:10Z", + "updatedAt": "2026-02-17T09:53:05Z", + "closedAt": null, + "repositories": { + "nodes": [] + } + }, + { + "id": "PVT_kwDOAOnouM4BHTsP", + "title": "MAPFRE", + "shortDescription": null, + "readme": null, + "number": 3042, + "url": "https://github.com/orgs/githubcustomers/projects/3042", + "template": false, + "createdAt": "2025-11-05T11:11:34Z", + "updatedAt": "2026-03-08T22:55:23Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "mapfre", + "url": "https://github.com/githubcustomers/mapfre" + } + ] + } + }, + { + "id": "PVT_kwDOAOnouM4BGWaF", + "title": "bbva-internal", + "shortDescription": null, + "readme": null, + "number": 3034, + "url": "https://github.com/orgs/githubcustomers/projects/3034", + "template": false, + "createdAt": "2025-10-24T14:34:05Z", + "updatedAt": "2026-03-09T01:07:03Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "bbva-internal", + "url": "https://github.com/githubcustomers/bbva-internal" + } + ] + } + }, + { + "id": "PVT_kwDOAOnouM4BFJgO", + "title": "Customer_1", + "shortDescription": null, + "readme": null, + "number": 3023, + "url": "https://github.com/orgs/githubcustomers/projects/3023", + "template": false, + "createdAt": "2025-10-09T16:10:54Z", + "updatedAt": "2025-10-09T16:10:54Z", + "closedAt": null, + "repositories": { + "nodes": [] + } + }, + { + "id": "PVT_kwDOAOnouM4BCTaN", + "title": "CaixaBank", + "shortDescription": null, + "readme": null, + "number": 2989, + "url": "https://github.com/orgs/githubcustomers/projects/2989", + "template": false, + "createdAt": "2025-09-04T17:52:36Z", + "updatedAt": "2026-03-06T11:32:28Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "caixabank", + "url": "https://github.com/githubcustomers/caixabank" + } + ] + } + }, + { + "id": "PVT_kwDOAOnouM4BCTX_", + "title": "[TEMPLATE] Customer Project", + "shortDescription": null, + "readme": null, + "number": 2988, + "url": "https://github.com/orgs/githubcustomers/projects/2988", + "template": true, + "createdAt": "2025-09-04T17:46:28Z", + "updatedAt": "2025-09-04T17:49:49Z", + "closedAt": null, + "repositories": { + "nodes": [] + } + }, + { + "id": "PVT_kwDOAOnouM4BBrCn", + "title": "BBVA", + "shortDescription": "SolutionEngineer @rulasg", + "readme": null, + "number": 2985, + "url": "https://github.com/orgs/githubcustomers/projects/2985", + "template": false, + "createdAt": "2025-08-28T11:04:46Z", + "updatedAt": "2026-03-09T01:06:59Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "bbva", + "url": "https://github.com/githubcustomers/bbva" + } + ] + } + }, + { + "id": "PVT_kwDOAOnouM4AzN5W", + "title": "BiT21", + "shortDescription": null, + "readme": null, + "number": 2683, + "url": "https://github.com/orgs/githubcustomers/projects/2683", + "template": false, + "createdAt": "2025-02-27T11:36:28Z", + "updatedAt": "2026-03-04T06:35:05Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "bit21", + "url": "https://github.com/githubcustomers/bit21" + } + ] + } + }, + { + "id": "PVT_kwDOAOnouM4AzN5I", + "title": "Indra", + "shortDescription": null, + "readme": null, + "number": 2682, + "url": "https://github.com/orgs/githubcustomers/projects/2682", + "template": false, + "createdAt": "2025-02-27T11:35:47Z", + "updatedAt": "2026-03-01T10:06:05Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "indra", + "url": "https://github.com/githubcustomers/indra" + } + ] + } + }, + { + "id": "PVT_kwDOAOnouM4AzI72", + "title": "BBVA-Accenture", + "shortDescription": null, + "readme": null, + "number": 2680, + "url": "https://github.com/orgs/githubcustomers/projects/2680", + "template": false, + "createdAt": "2025-02-26T13:00:51Z", + "updatedAt": "2026-03-01T10:04:42Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "bbva-accenture", + "url": "https://github.com/githubcustomers/bbva-accenture" + } + ] + } + }, + { + "id": "PVT_kwDOAOnouM4Ayw_m", + "title": "Santander-Cybersecurity", + "shortDescription": null, + "readme": null, + "number": 2671, + "url": "https://github.com/orgs/githubcustomers/projects/2671", + "template": false, + "createdAt": "2025-02-21T12:23:45Z", + "updatedAt": "2026-03-05T00:13:38Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "santander-cybersecurity", + "url": "https://github.com/githubcustomers/santander-cybersecurity" + } + ] + } + }, + { + "id": "PVT_kwDOAOnouM4AwGnG", + "title": "Iberdrola", + "shortDescription": null, + "readme": null, + "number": 2598, + "url": "https://github.com/orgs/githubcustomers/projects/2598", + "template": false, + "createdAt": "2025-01-15T12:31:02Z", + "updatedAt": "2026-02-23T13:15:23Z", + "closedAt": null, + "repositories": { + "nodes": [ + { + "name": "iberdrola", + "url": "https://github.com/githubcustomers/iberdrola" + } + ] + } + }, + { + "id": "PVT_kwDOAOnouM4AuHlc", + "title": "Inditex", + "shortDescription": null, + "readme": null, + "number": 2566, + "url": "https://github.com/orgs/githubcustomers/projects/2566", + "template": false, + "createdAt": "2024-12-11T11:01:01Z", + "updatedAt": "2025-10-02T07:26:08Z", + "closedAt": null, + "repositories": { + "nodes": [] + } + } +] diff --git a/Test/private/run_BeforeAfter.ps1 b/Test/private/run_BeforeAfter.ps1 new file mode 100644 index 0000000..2ed9bfe --- /dev/null +++ b/Test/private/run_BeforeAfter.ps1 @@ -0,0 +1,29 @@ +# Run Before and After any test +# +# Supported by TestingHelper 4.1.0 we can specify code that will run : +# - Before each test +# - After each test +# - Before all tests +# - After all tests +# +# Copy this file to Test Private to avoid being replaced by updates + +# function Run_BeforeAll{ +# Write-Verbose "Run_BeforeAll" +# } + +# function Run_AfterAll{ +# Write-Verbose "Run_AfterAll" +# } + +function Run_BeforeEach{ + # Write-Verbose "Run_BeforeEach" + Reset-InvokeCommandMock + +} + +# function Run_AfterEach{ +# Write-Verbose "Run_AfterEach" +# } + +Export-ModuleMember -Function Run_* diff --git a/Test/public/SampleFunctionTests.ps1 b/Test/public/SampleFunctionTests.ps1 deleted file mode 100644 index 90b81e0..0000000 --- a/Test/public/SampleFunctionTests.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -$TESTED_MODULE_PATH = $PSScriptRoot | split-path -Parent | split-path -Parent - -function Test_GetPublicString{ - - $sampleString = "this is a sample string" - - $result = Get-PublicString -Param1 $sampleString - - Assert-AreEqual -Expected ("Public string [{0}]" -f $samplestring) -presented $result -Comment "Sample test failed" - -} - -function Test_GetPrivateString { - - $testedModulePath = $TESTED_MODULE_PATH | Join-Path -ChildPath "GitHubCustomersHelper.psd1" - $testedModule = Import-Module -Name $testedModulePath -Force -PassThru - - $sampleString = "this is a sample string" - - $result = & $testedModule { - $sampleString = "this is a sample string" - Get-PrivateString -Param1 $sampleString - } - - Assert-AreEqual -Expected ("Private string [{0}]" -f $samplestring) -presented $result -Comment "Sample test failed" - -} - -Export-ModuleMember -Function Test_* diff --git a/Test/public/items/GcProjectItem.test.ps1 b/Test/public/items/GcProjectItem.test.ps1 new file mode 100644 index 0000000..907948e --- /dev/null +++ b/Test/public/items/GcProjectItem.test.ps1 @@ -0,0 +1,12 @@ +function Test_GetGcProjectItem_SUCCESS{ + + MockCall_GetAllItems + + $result = Get-GcProjectItem -ItemId "PVTI_lADOAOnouM4AzN5WzgaoV5A" + + Assert-AreEqual -Presented $result.Id -Expected "PVTI_lADOAOnouM4AzN5WzgaoV5A" + Assert-AreEqual -Presented $result.projectUrl -Expected "https://github.com/orgs/githubcustomers/projects/2683" + Assert-AreEqual -Presented $result.projectOwner -Expected "githubcustomers" + Assert-AreEqual -Presented $result.projectNumber -Expected 2683 +} + diff --git a/Test/public/items/GcProjectItems.test.ps1 b/Test/public/items/GcProjectItems.test.ps1 new file mode 100644 index 0000000..404ca34 --- /dev/null +++ b/Test/public/items/GcProjectItems.test.ps1 @@ -0,0 +1,59 @@ +function Test_GetProjectItems_Success{ + + MockCall_GetAllItems + + # All + # Act + $result = Get-GcProjectItems + # Assert + Assert-Count -Expected 5 -Presented $result + + # IncludeDone + # Act + $result = Get-GcProjectItems -IncludeDone + # Assert + Assert-Count -Expected 6 -Presented $result + + # RepositoryName + # Act + $result = Get-GcProjectItems -RepositoryName "kk" + # Assert + Assert-Count -Expected 2 -Presented $result + + # ProjectNumber + # Act + $result = Get-GcProjectItems -ProjectNumber 2683 + # Assert + Assert-Count -Expected 2 -Presented $result + Assert-Contains -Presented $result.id -Expected "PVTI_lADOAOnouM4AzN5Wzgml8mc" + Assert-Contains -Presented $result.id -Expected "PVTI_lADOAOnouM4AzN5WzgmP5nc" + + # Filter + # Act + $result = Get-GcProjectItems -Filter "Demo" + # Assert + Assert-Count -Expected 1 -Presented $result + Assert-Contains -Presented $result.id -Expected "PVTI_lADOAOnouM4AzN5WzgmP5nc" + + +} + +function MockCall_GetProject($ProjectNumber){ + MockCallJson -Command "Get-ProjectItems -owner githubcustomers -projectNumber $ProjectNumber -IncludeDone" -filename "get-projectitems-githubcustomers-$ProjectNumber.json" +} + +function GetMockFiles($ProjectNumber){ + enable-invokeCommandAliasModule + + $cmd = "Get-ProjectItems -owner githubcustomers -projectNumber $ProjectNumber -IncludeDone -Force" + + save-invokeAsMockFile $cmd -FileName "get-projectitems-githubcustomers-$ProjectNumber.json" +} + +function MockCall_GetAllItems{ + + MockCallToString -Command 'gh api user --jq ".login"' -OutString 'testuser' + MockCallJson -Command "Find-Project -owner githubcustomers -pattern creator:testuser" -filename "testuser-find-project-3.json" + MockCall_GetProject 2683 ; MockCall_GetProject 2988 ; MockCall_GetProject 3023 + +} \ No newline at end of file diff --git a/Test/public/items/GcProjects.test.ps1 b/Test/public/items/GcProjects.test.ps1 new file mode 100644 index 0000000..6af8728 --- /dev/null +++ b/Test/public/items/GcProjects.test.ps1 @@ -0,0 +1,18 @@ +function Test_GetGcProjects { + + MockCallToString -Command 'gh api user --jq ".login"' -OutString 'testuser' + + MockCallJson -Command "Find-Project -owner githubcustomers -pattern creator:testuser" -filename "testuser-find-project.json" + + $projects = Get-GcProjects + + Assert-Count -Expected 16 -Presented $projects + + # Pick one random and check the structure + $testProject = $projects."bit21" + Assert-AreEqual -Expected "bit21" -Presented $testProject.Title + Assert-AreEqual -Expected "githubcustomers" -Presented $testProject.Owner + Assert-AreEqual -Expected 2683 -Presented $testProject.ProjectNumber + Assert-AreEqual -Expected "https://github.com/orgs/githubcustomers/projects/2683" -Presented $testProject.Url + +} \ No newline at end of file diff --git a/helper/invokeCommand.helper.ps1 b/helper/invokeCommand.helper.ps1 new file mode 100644 index 0000000..5a08f46 --- /dev/null +++ b/helper/invokeCommand.helper.ps1 @@ -0,0 +1,49 @@ + +# SET MY INVOKE COMMAND ALIAS +# +# Allows calling constitely InvokeHelper with the module tag +# Need to define a variable called $MODULE_INVOKATION_TAG +# + +$moduleRootPath = $PSScriptRoot | Split-Path -Parent +$MODULE_NAME = (Get-ChildItem -Path $moduleRootPath -Filter *.psd1 | Select-Object -First 1).BaseName +$MODULE_INVOKATION_TAG = "$($MODULE_NAME)Module" + +function Set-MyInvokeCommandAlias{ + [CmdletBinding(SupportsShouldProcess)] + param( + [Parameter(Mandatory,Position=0)][string]$Alias, + [Parameter(Mandatory,Position=1)][string]$Command + ) + + 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-MyDebug "invoke" $Command $Parameters + + return InvokeHelper\Invoke-MyCommand -Command $Command -Parameters $Parameters +} + + +function Reset-MyInvokeCommandAlias{ + [CmdletBinding(SupportsShouldProcess)] + param() + + # throw if MODULE_INVOKATION_TAG is not set or is "MyModuleModule" + if (-not $MODULE_INVOKATION_TAG) { + throw "MODULE_INVOKATION_TAG is not set. Please set it to a unique value before calling Set-MyInvokeCommandAlias." + } + InvokeHelper\Reset-InvokeCommandAlias -Tag $MODULE_INVOKATION_TAG +} + +# Reset all aliases for this module on each refresh +Reset-MyInvokeCommandAlias diff --git a/helper/module.helper.ps1 b/helper/module.helper.ps1 new file mode 100644 index 0000000..670d090 --- /dev/null +++ b/helper/module.helper.ps1 @@ -0,0 +1,190 @@ +# Helper for module variables + +function Find-ModuleRootPath{ + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline,Position = 0)] + [string]$Path + ) + + $path = Convert-Path -Path $Path + + while (-not [string]::IsNullOrWhiteSpace($Path)){ + $psd1 = Get-ChildItem -Path $Path -Filter *.psd1 | Select-Object -First 1 + + if ($psd1 | Test-Path) { + + if($psd1.BaseName -eq "Test"){ + #foudn testing module. Continue + $path = $path | Split-Path -Parent + continue + } + + # foudn module + return $path + } + # folder without psd1 file + $path = $path | Split-Path -Parent + } + + # Path is null. Reached driver root. Module not found + return $null +} + +$MODULE_ROOT_PATH = $PSScriptRoot | Find-ModuleRootPath +$MODULE_NAME = (Get-ChildItem -Path $MODULE_ROOT_PATH -Filter *.psd1 | Select-Object -First 1).BaseName + +# Helper for module variables + +# Folders names that IncludeHelper may add content to +$VALID_INCLUDE_FOLDER_NAMES = @( + 'Root', + 'Include', + 'DevContainer', + 'WorkFlows', + 'GitHub', + # 'Config', + 'Helper', + # 'Private', + # 'Public', + 'Tools', + + 'TestRoot', + # 'TestConfig' + 'TestInclude', + 'TestHelper', + # 'TestPrivate', + # 'TestPublic', + + "TestHelperRoot", + "TestHelperPrivate", + "TestHelperPublic" + + "VsCode" +) + +# Folders names that IncludeHelper should not add content to. +# In this folders is the module code itself +$VALID_MODULE_FOLDER_NAMES = @( + 'Config', + 'Private', + 'Public', + 'TestConfig' + 'TestPrivate', + 'TestPublic' +) + +$VALID_FOLDER_NAMES = $VALID_INCLUDE_FOLDER_NAMES + $VALID_MODULE_FOLDER_NAMES + +class ValidFolderNames : System.Management.Automation.IValidateSetValuesGenerator { + [String[]] GetValidValues() { + return $script:VALID_FOLDER_NAMES + } +} + +function Get-Ps1FullPath{ + [CmdletBinding()] + param( + [Parameter(Mandatory,Position = 0)][string]$Name, + [Parameter(Position = 1)][ValidateSet([ValidFolderNames])][string]$FolderName, + [Parameter(Position = 0)][string]$ModuleRootPath + ) + + # If folderName is not empty + if($FolderName -ne $null){ + $folder = Get-ModuleFolder -FolderName $FolderName -ModuleRootPath $ModuleRootPath + $path = $folder | Join-Path -ChildPath $Name + } else { + $path = $Name + } + + # Check if file exists + if(-Not (Test-Path $path)){ + throw "File $path not found" + } + + # Get Path item + $item = Get-item -Path $path + + return $item +} +function Get-ModuleRootPath{ + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$ModuleRootPath + ) + + # if ModuleRootPath is not provided, default to local module path + if([string]::IsNullOrWhiteSpace($ModuleRootPath)){ + $ModuleRootPath = $MODULE_ROOT_PATH + } + + # Convert to full path + $ModuleRootPath = Convert-Path -Path $ModuleRootPath + + return $ModuleRootPath +} + +function Get-ModuleName{ + [CmdletBinding()] + param( + [Parameter(Position = 0)] [string]$ModuleRootPath + ) + + $ModuleRootPath = Get-ModuleRootPath -ModuleRootPath $ModuleRootPath + + $MODULE_NAME = (Get-ChildItem -Path $MODULE_ROOT_PATH -Filter *.psd1 | Select-Object -First 1).BaseName + + + return $MODULE_NAME +} + +function Get-ModuleFolder{ + [CmdletBinding()] + param( + [Parameter(Mandatory,Position = 0)][ValidateSet([ValidFolderNames])][string]$FolderName, + [Parameter(Position = 1)][string]$ModuleRootPath + ) + + $ModuleRootPath = Get-ModuleRootPath -ModuleRootPath $ModuleRootPath + + # TestRootPath + $testRootPath = $ModuleRootPath | Join-Path -ChildPath "Test" + $testHelperRootPath = $ModuleRootPath | Join-Path -ChildPath "tools/Test_Helper" + + switch ($FolderName){ + + # VALID_INCLUDE_FOLDER_NAMES + 'Root' { $moduleFolder = $ModuleRootPath } + 'Include' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "include" } + 'DevContainer'{ $moduleFolder = $ModuleRootPath | Join-Path -ChildPath ".devcontainer" } + 'WorkFlows' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath ".github/workflows" } + 'GitHub' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath ".github" } + 'Helper' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "helper" } + 'Tools' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "tools" } + + 'TestRoot' { $moduleFolder = $testRootPath } + 'TestInclude' { $moduleFolder = $testRootPath | Join-Path -ChildPath "include" } + 'TestHelper' { $moduleFolder = $testRootPath | Join-Path -ChildPath "helper" } + + 'TestHelperRoot' { $moduleFolder = $testHelperRootPath } + 'TestHelperPrivate' { $moduleFolder = $testHelperRootPath | Join-Path -ChildPath "private" } + 'TestHelperPublic' { $moduleFolder = $testHelperRootPath | Join-Path -ChildPath "public" } + + "VsCode" { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath ".vscode" } + + # VALID_MODULE_FOLDER_NAMES + 'Config' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "config" } + 'Private' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "private" } + 'Public' { $moduleFolder = $ModuleRootPath | Join-Path -ChildPath "public" } + 'TestConfig' { $moduleFolder = $testRootPath | Join-Path -ChildPath "config" } + 'TestPrivate' { $moduleFolder = $testRootPath | Join-Path -ChildPath "private" } + 'TestPublic' { $moduleFolder = $testRootPath | Join-Path -ChildPath "public" } + + + default{ + throw "Folder [$FolderName] is unknown" + } + } + return $moduleFolder +} Export-ModuleMember -Function Get-ModuleFolder \ No newline at end of file diff --git a/include/MyHandle.ps1 b/include/MyHandle.ps1 new file mode 100644 index 0000000..88c7269 --- /dev/null +++ b/include/MyHandle.ps1 @@ -0,0 +1,10 @@ +Set-MyInvokeCommandAlias -Alias GetGhHandle -Command 'gh api user --jq ".login"' + +function Get-MyHandle{ + [CmdletBinding()] + param() + + $user = Invoke-MyCommand -Command GetGhHandle + + return $user +} \ No newline at end of file diff --git a/include/MyWrite.ps1 b/include/MyWrite.ps1 new file mode 100644 index 0000000..3703184 --- /dev/null +++ b/include/MyWrite.ps1 @@ -0,0 +1,339 @@ +# Include MyWrite.ps1 +# Provides Write-MyError, Write-MyWarning, Write-MyVerbose, Write-MyHost, Write-MyDebug +# and Test-MyVerbose, Test-MyDebug functions for consistent logging and debugging output. +# Use env variables ModuleHelper_VERBOSE and ModuleHelper_DEBUG to control verbosity and debug output. +# Example: $env:ModuleHelper_DEBUG="all" or $env:ModuleHelper_DEBUG="Sync-Project" + +$ModuleRootPath = Get-ModuleRootPath -ModuleRootPath $ModuleRootPath +$MODULE_NAME = (Get-ChildItem -Path $ModuleRootPath -Filter *.psd1 | Select-Object -First 1).BaseName + +$ERROR_COLOR = "Red" +$WARNING_COLOR = "Yellow" +$VERBOSE_COLOR = "DarkYellow" +$OUTPUT_COLOR = "DarkCyan" +$DEBUG_COLOR = "DarkGray" + +function Write-MyError { + [CmdletBinding()] + [Alias("Write-Error")] + param( + [Parameter(Mandatory, ValueFromPipeline)][string]$Message + ) + # Write-Host "Error: $message" -ForegroundColor $ERROR_COLOR + Write-ToConsole "Error: $message" -Color $ERROR_COLOR +} + +function Write-MyWarning { + [CmdletBinding()] + [Alias("Write-Warning")] + param( + [Parameter(Mandatory, ValueFromPipeline)][string]$Message + ) + # Write-Host "Error: $message" -ForegroundColor $WARNING_COLOR + Write-ToConsole $message -Color $WARNING_COLOR +} + +function Write-MyVerbose { + [CmdletBinding()] + [Alias("Write-Verbose")] + param( + [Parameter(ValueFromPipeline)][string]$Message + ) + + if (Test-MyVerbose) { + Write-ToConsole $message -Color $VERBOSE_COLOR + } +} + +function Write-MyHost { + [CmdletBinding()] + [Alias("Write-Host")] + param( + [Parameter(ValueFromPipeline)][string]$Message, + [Parameter()][string]$ForegroundColor = $OUTPUT_COLOR, + [Parameter()][switch]$NoNewLine + ) + # Write-Host $message -ForegroundColor $OUTPUT_COLOR + Write-ToConsole $message -Color $ForegroundColor -NoNewLine:$NoNewLine +} + +function Write-MyDebug { + [CmdletBinding()] + [Alias("Write-Debug")] + param( + [Parameter(Position = 0)][string]$section = "none", + [Parameter(Position = 1, ValueFromPipeline)][string]$Message, + [Parameter(Position = 2)][object]$Object + ) + + process{ + + if (Test-MyDebug -section $section) { + + if ($Object) { + $objString = $Object | Get-ObjetString + $message = $message + " - " + $objString + } + $timestamp = Get-Date -Format 'HH:mm:ss.fff' + + # Write on host + $logMessage ="[$timestamp][D][$section] $message" + + $logMessage | Write-ToConsole -Color $DEBUG_COLOR + $logMessage | Write-MyDebugLogging + } + } +} + +function Write-MyDebugLogging { + param( + [Parameter(Position = 1, ValueFromPipeline)][string]$LogMessage + ) + + process{ + + $moduleDebugLoggingVarName = $MODULE_NAME + "_DEBUG_LOGGING_FILEPATH" + $loggingFilePath = [System.Environment]::GetEnvironmentVariable($moduleDebugLoggingVarName) + + # Check if logging is enabled + if ([string]::IsNullOrWhiteSpace( $loggingFilePath )) { + return + } + + # Check if file exists + # This should always exist as logging checks for parent path to be enabled + # It may happen if since enable to execution the parent folder aka loggingFilePath is deleted. + if(-not (Test-Path -Path $loggingFilePath -PathType Leaf) ){ + Write-Warning "Debug logging file path not accesible : '$loggingFilePath'" + return $false + } + + # Write to log file + Add-Content -Path $loggingFilePath -Value $LogMessage + } +} + +function Write-ToConsole { + param( + [Parameter(ValueFromPipeline)][string]$Color, + [Parameter(ValueFromPipeline, Position = 0)][string]$Message, + [Parameter()][switch]$NoNewLine + + ) + if([string]::IsNullOrWhiteSpace($Color)){ + Microsoft.PowerShell.Utility\Write-Host $message -NoNewLine:$NoNewLine + } else { + Microsoft.PowerShell.Utility\Write-Host $message -ForegroundColor:$Color -NoNewLine:$NoNewLine + } + +} + + +function Test-MyVerbose { + param( + [Parameter(Position = 0)][string]$section + ) + + $moduleDebugVarName = $MODULE_NAME + "_VERBOSE" + $flag = [System.Environment]::GetEnvironmentVariable($moduleDebugVarName) + + if ([string]::IsNullOrWhiteSpace( $flag )) { + return $false + } + + $trace = ($flag -like '*all*') -or ( $section -like "*$flag*") + return $trace +} + +function Enable-ModuleNameVerbose{ + param( + [Parameter(Position = 0)][string]$section + ) + + if( [string]::IsNullOrWhiteSpace( $section )) { + $flag = "all" + } else { + $flag = $section + } + + $moduleDebugVarName = $MODULE_NAME + "_VERBOSE" + [System.Environment]::SetEnvironmentVariable($moduleDebugVarName, $flag) +} +Copy-Item -path Function:Enable-ModuleNameVerbose -Destination Function:"Enable-$($MODULE_NAME)Verbose" +Export-ModuleMember -Function "Enable-$($MODULE_NAME)Verbose" + +function Disable-ModuleNameVerbose{ + param() + + $moduleDebugVarName = $MODULE_NAME + "_VERBOSE" + [System.Environment]::SetEnvironmentVariable($moduleDebugVarName, $null) +} +Copy-Item -path Function:Disable-ModuleNameVerbose -Destination Function:"Disable-$($MODULE_NAME)Verbose" +Export-ModuleMember -Function "Disable-$($MODULE_NAME)Verbose" + +function Test-MyDebug { + param( + [Parameter(Position = 0)][string]$section, + [Parameter()][switch]$Logging + ) + + function testSection($section,$flags){ + if($flags.Count -eq 0){ + return $false + } + $flags = $flags.ToLower() + $section = $section.ToLower() + + return ($flags.Contains("all")) -or ( $flags -eq $section) + } + + $moduleDebugVarName = $MODULE_NAME + "_DEBUG" + $flagsString = [System.Environment]::GetEnvironmentVariable($moduleDebugVarName) + + # No configuration means no debug + if([string]::IsNullOrWhiteSpace( $flagsString )) { + return $false + } + + # Get flags from flagdsString + $flags = getFlagsFromSectionsString $flagsString + + # Add all if allow is empty. + # This mean stat flagsString only contains filters. + $flags.allow = $flags.allow.Count -eq 0 ? @("all") : $flags.allow + + # Get the module debug environment variable + $isAllow = testSection -Section:$section -Flags:$flags.allow + $isFiltered = testSection -Section:$section -Flags:$flags.filter + + $trace = $isAllow -and -not $isFiltered + + return $trace +} + +function Enable-ModuleNameDebug{ + param( + [Parameter(Position = 0)][string[]]$Sections, + [Parameter()][string[]]$AddSections, + [Parameter()][string]$LoggingFilePath + ) + + # Check if logging file path is provided + if( -Not ( [string]::IsNullOrWhiteSpace( $LoggingFilePath )) ) { + if(Test-Path -Path $LoggingFilePath -PathType Leaf){ + set-LogFile $LoggingFilePath + } else { + Write-Error "Logging file path '$LoggingFilePath' does not exist. Debug logging will not be enabled." + return + } + } + + $flagsString = $sections -join " " + $addedFlagsString = $AddSections -join " " + + # if no section get value from env and is still mepty set to all + if([string]::IsNullOrWhiteSpace( $flagsString )) { + $flagsString = get-Sections + if( [string]::IsNullOrWhiteSpace( $flagsString )) { + $flagsString = "all" + } + } + + # Add added to flagsString if provided + if(-Not [string]::IsNullOrWhiteSpace( $addedFlagsString )) { + $flagsString += " " + $addedFlagsString + } + + set-Sections $flagsString + +} +Copy-Item -path Function:Enable-ModuleNameDebug -Destination Function:"Enable-$($MODULE_NAME)Debug" +Export-ModuleMember -Function "Enable-$($MODULE_NAME)Debug" + +function getFlagsFromSectionsString($sectionsString){ + $flags = @{ + allow = $null + filter = $null + } + + if([string]::IsNullOrWhiteSpace($sectionsString) ){ + $flags.allow = @("all") + return $flags + } + + $list = $sectionsString.Split(" ", [StringSplitOptions]::RemoveEmptyEntries) + + $split = @($list).Where({ $_ -like '-*' }, 'Split') + + $flags.filter = $split[0] | ForEach-Object { $_ -replace '^-', '' } # -> API, Auth + $flags.allow = $split[1] # -> Sync, Cache + + return $flags +} + +function Disable-ModuleNameDebug { + param() + + $moduleDebugVarName = $MODULE_NAME + "_DEBUG" + [System.Environment]::SetEnvironmentVariable($moduleDebugVarName, $null) + + $moduleDEbugLoggingVarName = $MODULE_NAME + "_DEBUG_LOGGING_FILEPATH" + [System.Environment]::SetEnvironmentVariable($moduleDEbugLoggingVarName, $null) +} +Copy-Item -path Function:Disable-ModuleNameDebug -Destination Function:"Disable-$($MODULE_NAME)Debug" +Export-ModuleMember -Function "Disable-$($MODULE_NAME)Debug" + +function Get-ModuleNameDebug { + [cmdletbinding()] + param() + + return @{ + Sections = get-Sections + LoggingFilePath = get-LogFile + } +} +Copy-Item -path Function:Get-ModuleNameDebug -Destination Function:"Get-$($MODULE_NAME)Debug" +Export-ModuleMember -Function "Get-$($MODULE_NAME)Debug" + +function Get-ObjetString { + param( + [Parameter(ValueFromPipeline, Position = 0)][object]$Object + ) + + process{ + + if ($null -eq $Object) { + return "null" + } + + if ($Object -is [string]) { + return $Object + } + + return $Object | ConvertTo-Json -Depth 10 -ErrorAction SilentlyContinue + } +} + +function get-Sections(){ + $moduleDebugVarName = $MODULE_NAME + "_DEBUG" + $sections = [System.Environment]::GetEnvironmentVariable($moduleDebugVarName) + + return $sections +} + +function set-Sections($sections){ + $moduleDebugVarName = $MODULE_NAME + "_DEBUG" + [System.Environment]::SetEnvironmentVariable($moduleDebugVarName, $sections) +} + +function get-LogFile(){ + $moduleDEbugLoggingVarName = $MODULE_NAME + "_DEBUG_LOGGING_FILEPATH" + $logfile = [System.Environment]::GetEnvironmentVariable($moduleDEbugLoggingVarName) + + return $logfile +} + +function set-LogFile($logFilePath){ + $moduleDEbugLoggingVarName = $MODULE_NAME + "_DEBUG_LOGGING_FILEPATH" + [System.Environment]::SetEnvironmentVariable($moduleDEbugLoggingVarName, $logFilePath) +} diff --git a/public/getrepos.ps1 b/public/getrepos.ps1 index b629266..5f00e5c 100644 --- a/public/getrepos.ps1 +++ b/public/getrepos.ps1 @@ -7,7 +7,7 @@ Gets the GitHubCustomers repository owned by a particular SolutionEngineer .PARAMETER Handle The GitHub handle of the SolutionEngineer Custom Property value of the repository owner #> -function Get-GCRepo { +function Get-GcRepo { param ( [Parameter(Mandatory,Position=0)][string]$PropertyValue, [Parameter()][string]$PropertyName = 'SolutionEngineer' @@ -23,4 +23,4 @@ function Get-GCRepo { return $ret -} Export-ModuleMember -Function Get-GCRepo \ No newline at end of file +} Export-ModuleMember -Function Get-GcRepo \ No newline at end of file diff --git a/public/items/GcProjectItem.ps1 b/public/items/GcProjectItem.ps1 new file mode 100644 index 0000000..a7e900e --- /dev/null +++ b/public/items/GcProjectItem.ps1 @@ -0,0 +1,22 @@ +function Get-GcProjectItem { + [CmdletBinding()] + [Alias ("gcpi")] + param( + [Parameter(Mandatory,ValueFromPipelineByPropertyName, ValueFromPipeline, Position = 0)][Alias("id")][string]$ItemId + ) + + $all = Get-AllItems + + if([string]::IsNullOrEmpty($ItemId)){ + return $all + } else { + $item = $all.$ItemId + + if(-Not $item){ + throw ("Item not found: "+$ItemId) + } + + return $item + } + +} Export-ModuleMember -Function Get-GcProjectItem -Alias gcpi \ No newline at end of file diff --git a/public/items/GcProjectItems.ps1 b/public/items/GcProjectItems.ps1 new file mode 100644 index 0000000..32aa4a2 --- /dev/null +++ b/public/items/GcProjectItems.ps1 @@ -0,0 +1,57 @@ +function Get-GcProjectItems{ + [CmdletBinding()] + [Alias ("scpi")] + param( + [Parameter(Position = 0)] [string[]]$Filter, + [Parameter(Position = 1)][string[]]$Attributes, + [Parameter()][string]$ProjectOwner, + [Parameter()][string]$ProjectNumber, + [Parameter()][switch]$IncludeDone, + [Parameter()][switch]$Force, + [Parameter()][switch]$PassThru, + # [Parameter()][string]$FieldName, + # [Parameter()][switch]$AnyField, + # [Parameter()][switch]$Exact + + [Parameter()][string]$RepositoryName + + ) + + $found = @((Get-AllItems -Force:$Force).Values) + + # Owner and ProjectNumber filtering + if(-Not [string]::IsNullOrEmpty($Owner)){ + $found = @($found | Where-Object {$_.projectOwner -eq $Owner}) + } + + # ProjectNumber filtering if(-Not [string]::IsNullOrEmpty($ProjectNumber)){ + if(-Not [string]::IsNullOrEmpty($ProjectNumber)){ + $found = @($found | Where-Object {$_.projectNumber -eq $ProjectNumber}) + } + + #IncludeDone + if($IncludeDone){ + $found = @($found) + } else { + $found = @($found | Where-Object {$_.Status -ne "Done"}) + } + + #RepositoryName + if(-Not [string]::IsNullOrEmpty($RepositoryName)){ + $found = @($found | Where-Object {$_.RepositoryName -eq $RepositoryName}) + } + + #Filter + if(-Not [string]::IsNullOrEmpty($Filter)){ + $found = @($found | Where-Object {$_.Title -match $Filter}) + } + + + if($PassThru){ + $ret = $found + } else { + $ret = $found | Format-ProjectItem -Attributes $Attributes + } + + return $ret +} Export-ModuleMember -Function Get-GcProjectItems -Alias scpi \ No newline at end of file diff --git a/public/items/formatProjectItem.ps1 b/public/items/formatProjectItem.ps1 new file mode 100644 index 0000000..a6d7d7c --- /dev/null +++ b/public/items/formatProjectItem.ps1 @@ -0,0 +1,33 @@ + +function Format-ProjectItem{ + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline)][object]$Item, + [Parameter(Position = 1)][string[]]$Attributes + ) + + begin { + if([string]::IsNullOrWhiteSpace($Attributes)){ + $Attributes = @("id","Title","RepositoryName") + } + } + + process{ + + $ret = [pscustomobject]::new() + + foreach($a in $Attributes){ + # just in case attribute has an empty name + if( [string]::IsNullOrWhiteSpace($a) ){ + continue + } + + # Add value even if it't empty value + $value = $Item.$a ?? "" + + $ret | Add-Member -MemberType NoteProperty -Name $a -Value $value -force + } + + return $ret + } +} \ No newline at end of file diff --git a/public/items/getAllItems.ps1 b/public/items/getAllItems.ps1 new file mode 100644 index 0000000..b6c1042 --- /dev/null +++ b/public/items/getAllItems.ps1 @@ -0,0 +1,47 @@ +Set-MyInvokeCommandAlias -Alias GetProjectItems -Command 'Get-ProjectItems -owner {owner} -projectNumber {ProjectNumber} -IncludeDone' +Set-MyInvokeCommandAlias -Alias GetProjectItemsForce -Command 'Get-ProjectItems -owner {owner} -projectNumber {ProjectNumber} -IncludeDone -Force' + +function Get-AllItems{ + [CmdletBinding()] + param( + # force + [Parameter()][switch]$IncludeDone, + [Parameter()][switch]$Force + ) + + $gcp = Get-GcProjects + + $itemlist = @{} + + foreach($project in $gcp.Values){ + + $params = @{owner=$project.Owner; ProjectNumber=$project.ProjectNumber} + + if($Force){ + $items = Invoke-MyCommand -Command GetProjectItemsForce -Parameters $params + } else { + $items = Invoke-MyCommand -Command GetProjectItems -Parameters $params + } + + foreach($item in $items){ + $id = $item.id + + $itemlist.$id = @{ + Id = $id + databaseId = $item.databaseId + projectNumber = $project.ProjectNumber + projectOwner = $project.Owner + RepositoryName = $item.RepositoryName + RepositoryOwner = $item.RepositoryOwner + projectUrl = $project.Url + Status = $item.status + Title = $item.title + State = $item.state + Url = $item.url + } + } + } + + return $itemlist + +} \ No newline at end of file diff --git a/public/projects/GcProjects.ps1 b/public/projects/GcProjects.ps1 new file mode 100644 index 0000000..fb2ca58 --- /dev/null +++ b/public/projects/GcProjects.ps1 @@ -0,0 +1,42 @@ +Set-MyInvokeCommandAlias -Alias FindProjectByCreator -Command 'Find-Project -owner {owner} -pattern creator:{handle}' + + +function Get-GcProjects { + [CmdletBinding()] + param( + [parameter()][switch]$IncludeClosed + ) + + $me = Get-MyHandle + $owner = 'githubcustomers' + + $params = @{ + owner = $owner + handle = $me + } + + $result = Invoke-MyCommand -Command FindProjectByCreator -Parameters $params + + # Filter closed projects + # TODO: this filter is not working. Ignore the filter for the moment + # $filtered = $result | Where-Object {$null -ne $_.closedAt} + $filtered = $result + + # Select attributes to return + $ret = @{} + + foreach($p in $filtered) { + $name = $p.title -replace '[^a-zA-Z0-9]', '_' + $n = [pscustomobject]@{ + Title = $p.title + Owner = $owner + ProjectNumber = $p.number + Url = $p.url + } + + $ret.$name= $n + } + + return $ret + +} Export-ModuleMember -Function Get-GcProjects \ No newline at end of file diff --git a/public/samplePublicFunction.ps1 b/public/samplePublicFunction.ps1 deleted file mode 100644 index ad678be..0000000 --- a/public/samplePublicFunction.ps1 +++ /dev/null @@ -1,8 +0,0 @@ - -function Get-PublicString { - param ( - [Parameter()][string]$Param1 - ) - - return ("Public string [{0}]" -f $param1) -} Export-ModuleMember -Function Get-PublicString