diff --git a/Test/include/config.mock.ps1 b/Test/include/config.mock.ps1 new file mode 100644 index 0000000..5e86527 --- /dev/null +++ b/Test/include/config.mock.ps1 @@ -0,0 +1,37 @@ + +# CONFIG MOCK +# +# This file is used to mock the config path and the config file +# for the tests. It creates a mock config path and a mock config file +# and sets the config path to the mock config path. +# +# THIS INCLUDE REQURED module.helper.ps1 +if(-not $MODULE_NAME){ throw "Missing MODULE_NAME varaible initialization. Check for module.helerp.ps1 file." } + +$MOCK_CONFIG_PATH = "test_config_path" +$CONFIG_INVOKE_GET_ROOT_PATH_CMD = "Invoke-$($MODULE_NAME)GetConfigRootPath" + +function Mock_Config{ + param( + [Parameter(Position=0)][string] $key = "config", + [Parameter(Position=1)][object] $Config + ) + + # Remove mock config path if exists + if(Test-Path $MOCK_CONFIG_PATH){ + Remove-Item -Path $MOCK_CONFIG_PATH -ErrorAction SilentlyContinue -Recurse -Force + } + + # create mock config path + New-Item -Path $MOCK_CONFIG_PATH -ItemType Directory -Force + + # if $config is not null save it to a file + if($null -ne $Config){ + $configfile = Join-Path -Path $MOCK_CONFIG_PATH -ChildPath "$key.json" + $Config | ConvertTo-Json -Depth 10 | Set-Content $configfile + } + + # Mock invoke call + MockCallToString $CONFIG_INVOKE_GET_ROOT_PATH_CMD -OutString $MOCK_CONFIG_PATH + +} diff --git a/Test/mockfiles.log b/Test/mockfiles.log index effc515..5ab0797 100644 --- a/Test/mockfiles.log +++ b/Test/mockfiles.log @@ -182,5 +182,25 @@ { "FileName": "invoke-GitHubOrgProjectWithFields-octodemo-700-query-field-text.json", "Command": "Invoke-GitHubOrgProjectWithFields -Owner octodemo -ProjectNumber 700 -afterFields \"\" -afterItems \"\" -query \"field-text:text1\"" + }, + { + "FileName": "invoke-GetUser-rulasg.json", + "Command": "Invoke-GetUser -Handle rulasg" + }, + { + "FileName": "invoke-UpdateProjectV2Collaborators-MDQ6VXNlcjY4ODQ0MDg=.json", + "Command": "Invoke-UpdateProjectV2Collaborators -ProjectId PVT_kwDOAlIw4c4BCe3V -collaborators \"MDQ6VXNlcjY4ODQ0MDg=\" -Role \"WRITER\"" + }, + { + "FileName": "invoke-GetUser-rauldibildos.json", + "Command": "Invoke-GetUser -Handle rauldibildos" + }, + { + "FileName": "invoke-UpdateProjectV2Collaborators-MDQ6VXNlcjY4ODQ0MDg=.json", + "Command": "Invoke-UpdateProjectV2Collaborators -ProjectId PVT_kwDOAlIw4c4BCe3V -collaborators \"MDQ6VXNlcjY4ODQ0MDg= U_kgDOC_E3gw\" -Role \"WRITER\"" + }, + { + "FileName": "invoke-UpdateProjectV2Collaborators-MDQ6VXNlcjY4ODQ0MDg=.json", + "Command": "Invoke-UpdateProjectV2Collaborators -ProjectId PVT_kwDOAlIw4c4BCe3V -collaborators \"\" -Role \"WRITER\"" } ] diff --git a/Test/private/MockUsers.ps1 b/Test/private/MockUsers.ps1 new file mode 100644 index 0000000..5f8adc1 --- /dev/null +++ b/Test/private/MockUsers.ps1 @@ -0,0 +1,16 @@ +function Get-Mock_Users{ + $users = @{ + u1 = @{ + id = "MDQ6VXNlcjY4ODQ0MDg=" + name = "rulasg" + file = "invoke-GetUser-rulasg.json" + } + u2 = @{ + id = "U_kgDOC_E3gw" + name = "rauldibildos" + file = "invoke-GetUser-rauldibildos.json" + } + } + + return $users +} \ No newline at end of file diff --git a/Test/private/mocks/invoke-GetUser-rauldibildos.json b/Test/private/mocks/invoke-GetUser-rauldibildos.json new file mode 100644 index 0000000..a87bb61 --- /dev/null +++ b/Test/private/mocks/invoke-GetUser-rauldibildos.json @@ -0,0 +1,4 @@ +{ + "login": "raulDibildos", + "node_id": "U_kgDOC_E3gw" +} diff --git a/Test/private/mocks/invoke-GetUser-rulasg.json b/Test/private/mocks/invoke-GetUser-rulasg.json new file mode 100644 index 0000000..e4d3449 --- /dev/null +++ b/Test/private/mocks/invoke-GetUser-rulasg.json @@ -0,0 +1,47 @@ +{ + "login": "rulasg", + "id": 6884408, + "node_id": "MDQ6VXNlcjY4ODQ0MDg=", + "avatar_url": "https://avatars.githubusercontent.com/u/6884408?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/rulasg", + "html_url": "https://github.com/rulasg", + "followers_url": "https://api.github.com/users/rulasg/followers", + "following_url": "https://api.github.com/users/rulasg/following{/other_user}", + "gists_url": "https://api.github.com/users/rulasg/gists{/gist_id}", + "starred_url": "https://api.github.com/users/rulasg/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/rulasg/subscriptions", + "organizations_url": "https://api.github.com/users/rulasg/orgs", + "repos_url": "https://api.github.com/users/rulasg/repos", + "events_url": "https://api.github.com/users/rulasg/events{/privacy}", + "received_events_url": "https://api.github.com/users/rulasg/received_events", + "type": "User", + "user_view_type": "private", + "site_admin": true, + "name": "Raúl (Dibildos) González", + "company": "GitHub", + "blog": "https://rulasg.github.io", + "location": "Madrid, Spain", + "email": "rulasg@github.com", + "hireable": null, + "bio": "A father, an optimist, an idealist, an engineer, a bit of a philosopher, a strategist, passionate about understanding and learning from everyone.", + "twitter_username": "rulasg", + "public_repos": 82, + "public_gists": 8, + "followers": 18, + "following": 12, + "created_at": "2014-03-07T14:47:11Z", + "updated_at": "2025-11-11T23:39:44Z", + "private_gists": 8, + "total_private_repos": 42, + "owned_private_repos": 42, + "disk_usage": 217904, + "collaborators": 4, + "two_factor_authentication": true, + "plan": { + "name": "pro", + "space": 976562499, + "collaborators": 0, + "private_repos": 9999 + } +} diff --git a/Test/private/mocks/invoke-UpdateProjectV2Collaborators-MDQ6VXNlcjY4ODQ0MDg=-U_kgDOC_E3gw.json b/Test/private/mocks/invoke-UpdateProjectV2Collaborators-MDQ6VXNlcjY4ODQ0MDg=-U_kgDOC_E3gw.json new file mode 100644 index 0000000..341c1d1 --- /dev/null +++ b/Test/private/mocks/invoke-UpdateProjectV2Collaborators-MDQ6VXNlcjY4ODQ0MDg=-U_kgDOC_E3gw.json @@ -0,0 +1,38 @@ +{ + "data": { + "updateProjectV2Collaborators": { + "collaborators": { + "totalCount": 2, + "nodes": [ + { + "__typename": "User", + "id": "MDQ6VXNlcjY4ODQ0MDg=", + "name": "Raúl (Dibildos) González", + "login": "rulasg", + "email": "rulasg@github.com" + }, + { + "__typename": "User", + "id": "U_kgDOC_E3gw", + "name": "Raúl Dibildos", + "login": "rauldibildos", + "email": "rauldibildos@gmail.com" + } + ] + } + } + }, + "extensions": { + "warnings": [ + { + "type": "DEPRECATION", + "message": "The id MDQ6VXNlcjY4ODQ0MDg= is deprecated. Update your cache to use the next_global_id from the data payload.", + "data": { + "legacy_global_id": "MDQ6VXNlcjY4ODQ0MDg=", + "next_global_id": "U_kgDOAGkMOA" + }, + "link": "https://docs.github.com" + } + ] + } +} diff --git a/Test/private/mocks/invoke-UpdateProjectV2Collaborators-MDQ6VXNlcjY4ODQ0MDg=.json b/Test/private/mocks/invoke-UpdateProjectV2Collaborators-MDQ6VXNlcjY4ODQ0MDg=.json new file mode 100644 index 0000000..ba86309 --- /dev/null +++ b/Test/private/mocks/invoke-UpdateProjectV2Collaborators-MDQ6VXNlcjY4ODQ0MDg=.json @@ -0,0 +1,31 @@ +{ + "data": { + "updateProjectV2Collaborators": { + "collaborators": { + "totalCount": 1, + "nodes": [ + { + "__typename": "User", + "id": "MDQ6VXNlcjY4ODQ0MDg=", + "name": "Raúl (Dibildos) González", + "login": "rulasg", + "email": "rulasg@github.com" + } + ] + } + } + }, + "extensions": { + "warnings": [ + { + "type": "DEPRECATION", + "message": "The id MDQ6VXNlcjY4ODQ0MDg= is deprecated. Update your cache to use the next_global_id from the data payload.", + "data": { + "legacy_global_id": "MDQ6VXNlcjY4ODQ0MDg=", + "next_global_id": "U_kgDOAGkMOA" + }, + "link": "https://docs.github.com" + } + ] + } +} diff --git a/Test/public/items/project_item_show.test.ps1 b/Test/public/items/project_item_show.test.ps1 index 2ee17a7..0074309 100644 --- a/Test/public/items/project_item_show.test.ps1 +++ b/Test/public/items/project_item_show.test.ps1 @@ -82,8 +82,8 @@ function Test_ShowProjectItem_SUCESS{ Assert-Contains -Presented $tt -Expected "$($i.status)" Assert-Contains -Presented $tt -Expected "$($i.Body)" - Assert-Contains -Presented $tt -Expected "By: $($i.comments.last.author.login)" - Assert-Contains -Presented $tt -Expected "At: $($i.comments.last.updatedAt)" + Assert-Contains -Presented $tt -Expected "By:[$($i.comments.last.author.login)]" + Assert-Contains -Presented $tt -Expected "At:[$($i.comments.last.updatedAt)]" Assert-Contains -Presented $tt -Expected $i.comments.last.body Assert-Contains -Presented $tt -Expected $i.id diff --git a/Test/public/project/addprojectuser.test.ps1 b/Test/public/project/addprojectuser.test.ps1 new file mode 100644 index 0000000..f345f39 --- /dev/null +++ b/Test/public/project/addprojectuser.test.ps1 @@ -0,0 +1,50 @@ +function Test_AddProjectUser_SUCCESS_SingleUser{ + Reset-InvokeCommandMock + Mock_DatabaseRoot + + # Enable-invokeCommandAliasModule + # Invoke-UpdateProjectV2Collaborators -ProjectId PVT_kwDOAlIw4c4BCe3V -collaborators "MDQ6VXNlcjY4ODQ0MDg=" -Role "WRITER" + + + $p =Get-Mock_Project_700 ; $owner = $p.Owner ; $projectNumber = $p.Number ; $projectId = $p.id + MockCall_GetProject $p -SkipItems + + $u = Get-Mock_Users + + $userId1 = $u.u1.id ; $userName1 = $u.u1.name + $role ="WRITER" + + $fileName = "invoke-UpdateProjectV2Collaborators-$userId1.json" + + MockCallJson -Command "Invoke-GetUser -Handle $userName1" -File $u.u1.file + MockCallJson -Command "Invoke-UpdateProjectV2Collaborators -ProjectId $projectId -collaborators ""$userId1"" -Role ""$role""" -File $fileName + + $result = Add-ProjectUser -Owner $owner -ProjectNumber $projectNumber -Handle $userName1 -Role $role + + Assert-IsTrue $result +} + +function Test_AddProjectUser_SUCCESS_MultipleUser{ + Reset-InvokeCommandMock + Mock_DatabaseRoot + + $p =Get-Mock_Project_700 ; $owner = $p.Owner ; $projectNumber = $p.Number ; $projectId = $p.id + MockCall_GetProject $p -SkipItems + + $u = Get-Mock_Users + + $userId1 = $u.u1.id ; $userName1 = $u.u1.name + $userId2 = $u.u2.id ; $userName2 = $u.u2.name + $userNames = "$userName1","$userName2" + $usersIds ="$userId1 $userId2" + $role ="WRITER" + $fileName = "invoke-UpdateProjectV2Collaborators-$userId1-$userId2.json" + + MockCallJson -Command "Invoke-GetUser -Handle $userName1" -File $u.u1.file + MockCallJson -Command "Invoke-GetUser -Handle $userName2" -File $u.u2.file + MockCallJson -Command "Invoke-UpdateProjectV2Collaborators -ProjectId $projectId -collaborators ""$usersIds"" -Role ""$role""" -File $fileName + + $result = $userNames | Add-ProjectUser -Owner $owner -ProjectNumber $projectNumber -Role $role + + Assert-IsTrue $result +} \ No newline at end of file diff --git a/Test/traceInvoke.log b/Test/traceInvoke.log index b4da7ba..95513a3 100644 --- a/Test/traceInvoke.log +++ b/Test/traceInvoke.log @@ -74,3 +74,7 @@ Invoke-GitHubOrgProjectWithFields -Owner SomeOrg -ProjectNumber 164 -afterFields Invoke-GitHubOrgProjectWithFields -Owner octodemo -ProjectNumber 625 -afterFields "" -afterItems "" -query "" Invoke-GitHubOrgProjectWithFields -Owner octodemo -ProjectNumber 626 -afterFields "" -afterItems "" -query "" Invoke-GitHubOrgProjectWithFields -Owner octodemo -ProjectNumber 700 -afterFields "" -afterItems "" -query "field-text:text1" +Invoke-GetUser -Handle rulasg +Invoke-UpdateProjectV2Collaborators -ProjectId PVT_kwDOAlIw4c4BCe3V -collaborators "MDQ6VXNlcjY4ODQ0MDg=" -Role "WRITER" +Invoke-GetUser -Handle rauldibildos +Invoke-UpdateProjectV2Collaborators -ProjectId PVT_kwDOAlIw4c4BCe3V -collaborators "MDQ6VXNlcjY4ODQ0MDg= U_kgDOC_E3gw" -Role "WRITER" diff --git a/include/callAPI.ps1 b/include/callAPI.ps1 index 3f910b7..efce501 100644 --- a/include/callAPI.ps1 +++ b/include/callAPI.ps1 @@ -44,7 +44,9 @@ function Invoke-GraphQL { # Send the request $start = Get-Date ">>> Invoke-RestMethod - $apiUri" | writedebug - $response = Invoke-RestMethod -Uri $apiUri -Method Post -Body $body -Headers $headers -OutFile $OutFile + if([string]::IsNullOrWhiteSpace($OutFile)) + { $response = Invoke-RestMethod -Uri $apiUri -Method Post -Body $body -Headers $headers } + else { $response = Invoke-RestMethod -Uri $apiUri -Method Post -Body $body -Headers $headers -OutFile $OutFile } "<<< Invoke-RestMethod - $apiUri [ $(((Get-Date) - $start).TotalSeconds) seconds]" | writedebug # Trace response @@ -58,10 +60,12 @@ function Invoke-GraphQL { return $response } catch { + "[[THROW]]" | writedebug + $_.Exception.Message | ConvertTo-Json -Depth 100 | writedebug throw New-Object system.Exception("Error calling GraphQL",$_.Exception) } -} Export-ModuleMember -Function Invoke-GraphQL +} function Invoke-RestAPI { param( @@ -132,7 +136,7 @@ function Invoke-RestAPI { catch { throw } -} Export-ModuleMember -Function Invoke-RestAPI +} #################################################################################################### @@ -158,7 +162,7 @@ function Get-ApiHost { "Default host $DEFAULT_GH_HOST" | writedebug return $DEFAULT_GH_HOST -} Export-ModuleMember -Function Get-ApiHost +} #################################################################################################### @@ -198,7 +202,7 @@ function Get-ApiToken { } return $result -} Export-ModuleMember -Function Get-ApiToken +} #################################################################################################### diff --git a/include/config.ps1 b/include/config.ps1 new file mode 100644 index 0000000..5e372ca --- /dev/null +++ b/include/config.ps1 @@ -0,0 +1,188 @@ +# CONFIG +# +# Configuration management module +# +# Include design description +# This is the function ps1. This file is the same for all modules. +# Create a public psq with variables, Set-MyInvokeCommandAlias call and Invoke public function. +# Invoke function will call back `GetConfigRootPath` to use production root path +# Mock this Invoke function with Set-MyInvokeCommandAlias to set the Store elsewhere +# This ps1 has function `GetConfigFile` that will call `Invoke-MyCommand -Command $CONFIG_INVOKE_GET_ROOT_PATH_ALIAS` +# to use the store path, mocked or not, to create the final store file name. +# All functions of this ps1 will depend on `GetConfigFile` for functionality. +# + +# MODULE_NAME +$MODULE_NAME = ($PSScriptRoot | Split-Path -Parent | Get-ChildItem -Filter *.psd1 | Select-Object -First 1).BaseName +if(-Not $MODULE_NAME){ throw "Module name not found. Please check the module structure." } + +$CONFIG_ROOT = [System.Environment]::GetFolderPath('UserProfile') | Join-Path -ChildPath ".helpers" -AdditionalChildPath $MODULE_NAME, "config" + +# Create the config root if it does not exist +if(-Not (Test-Path $CONFIG_ROOT)){ + New-Item -Path $CONFIG_ROOT -ItemType Directory +} + +function GetConfigRootPath { + [CmdletBinding()] + param() + + $configRoot = $CONFIG_ROOT + return $configRoot +} + +function GetConfigFile { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, Position = 0)][string]$Key + ) + + $configRoot = Invoke-MyCommand -Command $CONFIG_INVOKE_GET_ROOT_PATH_ALIAS + $path = Join-Path -Path $configRoot -ChildPath "$Key.json" + return $path +} + +function Test-ConfigurationFile { + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$Key = "config" + ) + + $path = GetConfigFile -Key $Key + + return Test-Path $path +} + +function Get-Configuration { + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$Key = "config" + ) + + # Check for cached configuration + $configVar = Get-Variable -scope Script -Name "config-$Key" -ErrorAction SilentlyContinue + if($configVar){ + return $configVar + } + + # No cached configuration; read from file + $path = GetConfigFile -Key $Key + + if(-Not (Test-ConfigurationFile -Key $Key)){ + return $null + } + + try{ + $ret = Get-Content $path | ConvertFrom-Json -AsHashtable -ErrorAction Stop + return $ret + } + catch{ + Write-Warning "Error reading configuration ($Key) file: $($path). $($_.Exception.Message)" + return $null + } +} + +function Save-Configuration { + [CmdletBinding()] + param( + [Parameter()][string]$Key = "config", + [Parameter(Mandatory = $true, Position = 1)][Object]$Config + ) + + $path = GetConfigFile -Key $Key + + try { + $Config | ConvertTo-Json -Depth 10 | Set-Content $path -ErrorAction Stop + } + catch { + Write-Warning "Error saving configuration ($Key) to file: $($path). $($_.Exception.Message)" + return $false + } + finally{ + Remove-Variable -Scope Script -Name "config-$Key" -ErrorAction SilentlyContinue + } + + return $true +} + +############ + + +# Define unique aliases for "ModuleName" +$CONFIG_INVOKE_GET_ROOT_PATH_ALIAS = "$($MODULE_NAME)GetConfigRootPath" +$CONFIG_INVOKE_GET_ROOT_PATH_CMD = "Invoke-$($MODULE_NAME)GetConfigRootPath" + +# Set the alias for the root path command +Set-MyInvokeCommandAlias -Alias $CONFIG_INVOKE_GET_ROOT_PATH_ALIAS -Command $CONFIG_INVOKE_GET_ROOT_PATH_CMD + +# Define the function to get the configuration root path +function Invoke-ModuleNameGetConfigRootPath { + [CmdletBinding()] + param() + + $configRoot = GetConfigRootPath + return $configRoot +} +$function = "Invoke-ModuleNameGetConfigRootPath" +$destFunction = $function -replace "ModuleName", $MODULE_NAME +if( -not (Test-Path function:$destFunction )){ + Rename-Item -path Function:$function -NewName $destFunction + Export-ModuleMember -Function $destFunction +} + +# Extra functions not needed by INCLUDE CONFIG + +function Get-ModuleNameConfig{ + [CmdletBinding()] + param() + + $config = Get-Configuration + + return $config +} +$function = "Get-ModuleNameConfig" +$destFunction = $function -replace "ModuleName", $MODULE_NAME +if( -not (Test-Path function:$destFunction )){ + Rename-Item -path Function:$function -NewName $destFunction + Export-ModuleMember -Function $destFunction +} + +function Open-ModuleNameConfig{ + [CmdletBinding()] + param() + + $path = GetConfigFile -Key "config" + + code $path +} +$function = "Open-ModuleNameConfig" +$destFunction = $function -replace "ModuleName", $MODULE_NAME +if( -not (Test-Path function:$destFunction )){ + Rename-Item -path Function:$function -NewName $destFunction + Export-ModuleMember -Function $destFunction +} + +function Set-ModuleNameConfigValue{ + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)][string]$Name, + [Parameter(Mandatory = $true)][object]$Value + ) + + $config = Get-Configuration + + if(-Not $config){ + $config = @{} + } + + $config.$Name = $Value + + Save-Configuration -Key "config" -Config $config +} +$function = "Set-ModuleNameConfigValue" +$destFunction = $function -replace "ModuleName", $MODULE_NAME +if( -not (Test-Path function:$destFunction )){ + # Rename-Item -path Function:$function -NewName $destFunction + Copy-Item -path Function:$function -Destination Function:$destFunction + Export-ModuleMember -Function $destFunction +} diff --git a/include/featureflag.ps1 b/include/featureflag.ps1 new file mode 100644 index 0000000..5184202 --- /dev/null +++ b/include/featureflag.ps1 @@ -0,0 +1,144 @@ +# Feature Flag +# +# Feature Flags management module +# +# Include design description +# This module depends on Config Include +# This module will allow set Feature Flags to the module to quicker release +# features with less risk +# + +$MODULE_NAME_PATH = ($PSScriptRoot | Split-Path -Parent | Get-ChildItem -Filter *.psd1 | Select-Object -First 1) | Split-Path -Parent + +function Get-FeatureFlags{ + [CmdletBinding()] + param() + + $config = Get-Configuration + + if(! $config){ + return @{} + } + + if(! $config.FeatureFlags){ + $config.FeatureFlags = @{} + } + + return $config.FeatureFlags +} + +function Save-FeatureFlags{ + [CmdletBinding()] + param( + [Parameter(Mandatory,Position=0)][hashtable]$FeatureFlags + ) + + $result = Set-ModuleNameConfigValue -Name "FeatureFlags" -Value $FeatureFlags + + if(! $result){ + throw "Failed to save Feature Flags" + } +} + +function Test-FeatureFlag { + [CmdletBinding()] + [Alias("tff")] + param( + [Parameter(Mandatory,Position=0)][string]$Key + ) + + $ffs = Get-FeatureFlags + + $value = $ffs.$Key + + if($null -eq $value){ + Set-FeatureFlag -Key $Key -Value $false + return $false + } + + return $value +} + +function Set-FeatureFlag{ + [CmdletBinding()] + param( + [Parameter(Mandatory,Position=0)][string]$Key, + [Parameter()][bool]$Value = $true + + ) + + $featureFlags = Get-FeatureFlags + + $featureFlags.$Key = $Value + + Save-FeatureFlags $featureFlags + +} + +function Clear-FeatureFlag{ + [CmdletBinding()] + param( + [Parameter(Mandatory,Position=0)][string]$Key + + ) + + Set-FeatureFlag -Key $Key -Value $false + +} + +###### + +# function Get-ModuleNameRegisteredFeatureFlags{ +# [cmdletbinding()] +# param() + +# $ffPath = $MODULE_NAME_PATH | Join-Path -ChildPath "featureflags.json" + +# if(! ($ffPath | Test-Path)){ +# return +# } + +# $Json = Get-Content $ffPath + +# $ff = $Json | ConvertFrom-Json + +# return $ff + +# } +# $function = "Get-ModuleNameRegisteredFeatureFlags" +# $destFunction = $function -replace "ModuleName", $MODULE_NAME +# if( -not (Test-Path function:$destFunction )){ +# Rename-Item -path Function:$function -NewName $destFunction +# Export-ModuleMember -Function $destFunction +# } + +function Get-ModuleNameFeatureFlags{ + [cmdletbinding()] + param() + + $ffs = Get-FeatureFlags + + return $ffs +} +$function = "Get-ModuleNameFeatureFlags" +$destFunction = $function -replace "ModuleName", $MODULE_NAME +if( -not (Test-Path function:$destFunction )){ + Rename-Item -path Function:$function -NewName $destFunction + Export-ModuleMember -Function $destFunction +} + +function Set-ModuleNameFeatureFlag{ + [cmdletbinding()] + param( + [Parameter(Mandatory,Position=0)][string]$Key, + [Parameter()][bool]$Value = $true + ) + + Set-FeatureFlag -Key $Key -Value $Value +} +$function = "Set-ModuleNameFeatureFlag" +$destFunction = $function -replace "ModuleName", $MODULE_NAME +if( -not (Test-Path function:$destFunction )){ + Rename-Item -path Function:$function -NewName $destFunction + Export-ModuleMember -Function $destFunction +} diff --git a/private/invokeRestMethord.ps1 b/private/invokeRestMethord.ps1 index d6ee4bb..a01d66c 100644 --- a/private/invokeRestMethord.ps1 +++ b/private/invokeRestMethord.ps1 @@ -1,29 +1,29 @@ -function Invoke-RestMethod{ - [CmdletBinding()] - param( - [Parameter(Position = 0)][string]$Method, - [Parameter(Position = 1)][string]$Uri, - [Parameter(Position = 2)][hashtable]$Headers, - [Parameter(Position = 3)][string]$Body, - [Parameter()][string]$OutFile - ) +# function Invoke-RestMethod{ +# [CmdletBinding()] +# param( +# [Parameter(Position = 0)][string]$Method, +# [Parameter(Position = 1)][string]$Uri, +# [Parameter(Position = 2)][hashtable]$Headers, +# [Parameter(Position = 3)][string]$Body, +# [Parameter()][string]$OutFile +# ) - $params = @{ - Method = $Method - Uri = $Uri - Headers = $Headers - Body = $Body - } +# $params = @{ +# Method = $Method +# Uri = $Uri +# Headers = $Headers +# Body = $Body +# } - if (-not [string]::IsNullOrWhiteSpace($OutFile)) { - $params.OutFile = $OutFile - } +# if (-not [string]::IsNullOrWhiteSpace($OutFile)) { +# $params.OutFile = $OutFile +# } - ">> $Method $Uri" | Write-MyDebug -section "invokeRestMethod" - $result = Microsoft.PowerShell.Utility\Invoke-RestMethod @params - "<< $Method $Uri" | Write-MyDebug -section "invokeRestMethod" +# ">> $Method $Uri" | Write-MyDebug -section "invokeRestMethod" +# $result = Microsoft.PowerShell.Utility\Invoke-RestMethod @params +# "<< $Method $Uri" | Write-MyDebug -section "invokeRestMethod" - return $result -} \ No newline at end of file +# return $result +# } \ No newline at end of file diff --git a/public/driver/projectv2/updateProjectV2Collaborators.ps1 b/public/driver/projectv2/updateProjectV2Collaborators.ps1 new file mode 100644 index 0000000..d03433e --- /dev/null +++ b/public/driver/projectv2/updateProjectV2Collaborators.ps1 @@ -0,0 +1,32 @@ +function Invoke-UpdateProjectV2Collaborators{ + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)][string]$ProjectId, + [Parameter(Mandatory=$true)][ValidateSet("READER","WRITER","NONE","ADMIN")] + [string]$Role, + [Parameter(Mandatory=$true)][string] $CollaboratorsIds + ) + + $list = $CollaboratorsIds.Split(@(" "),[System.StringSplitOptions]::RemoveEmptyEntries) + + $array = $list | ForEach-Object { + @{ + userId = $_ + role = $Role + } + } + + $query = Get-GraphQLString "updateProjectV2Collaborators.mutant" + + $variables = @{ + input = @{ + projectId = $ProjectId + collaborators = $array + } + } + + $response = Invoke-GraphQL -Query $query -Variables $variables + + return $response + +} Export-ModuleMember -Function Invoke-UpdateProjectV2Collaborators \ No newline at end of file diff --git a/public/driver/user/user.ps1 b/public/driver/user/user.ps1 new file mode 100644 index 0000000..1e429eb --- /dev/null +++ b/public/driver/user/user.ps1 @@ -0,0 +1,42 @@ + +Set-MyinvokeCommandAlias -Alias getUser -Command "Invoke-GetUser -Handle {handle}" +function Invoke-GetUser{ + param( + [Parameter(Mandatory)][string]$Handle + ) + + $result = Invoke-RestAPI -Api /users/$Handle + + return $result + +} Export-ModuleMember -Function Invoke-GetUser + +function Get-User{ + param( + [Parameter(Mandatory)][string]$Handle, + [Parameter()][switch]$Force + ) + + $key = "user-$Handle" + + # Check cache + $cache = Get-Database -Key $key + if(-Not $Force -And ($null -ne $cache)){ + return $cache + } + + $result = Invoke-MyCommand -Command "getUser" -Parameters @{handle=$Handle} + + # Cache + Save-Database -Key "user-$Handle" -Database $result + + $ret = [PSCustomObject]@{ + Id = $result.node_id + Name = $result.Name + Email = $result.Email + Login = $result.Login + } + + return $ret + +} Export-ModuleMember -Function Get-User \ No newline at end of file diff --git a/public/graphql/_content.tag b/public/graphql/_content.tag index 1ade5a8..4ef0df5 100644 --- a/public/graphql/_content.tag +++ b/public/graphql/_content.tag @@ -7,7 +7,11 @@ nodes{createdAt,updatedAt,url,body,fullDatabaseId,author{login}} } }, - ... on Issue{id,body,title,updatedAt,createdAt,number,url,state,repository{name,owner{login}} + ... on Issue{ + id,body,title,updatedAt,createdAt,number,url,state,repository{name,owner{login}} + assignees(first:100){ + nodes{{user}} + } comments(last: $lastComments){ totalCount, nodes{createdAt,updatedAt,url,body,fullDatabaseId,author{login}} diff --git a/public/graphql/_team.tag b/public/graphql/_team.tag new file mode 100644 index 0000000..f042d23 --- /dev/null +++ b/public/graphql/_team.tag @@ -0,0 +1 @@ +{id,name} \ No newline at end of file diff --git a/public/graphql/_user.tag b/public/graphql/_user.tag index d4f771d..c26b82b 100644 --- a/public/graphql/_user.tag +++ b/public/graphql/_user.tag @@ -1 +1 @@ -{login} \ No newline at end of file +{id,name,login,email} diff --git a/public/graphql/updateProjectV2Collaborators.mutant b/public/graphql/updateProjectV2Collaborators.mutant new file mode 100644 index 0000000..0f68f03 --- /dev/null +++ b/public/graphql/updateProjectV2Collaborators.mutant @@ -0,0 +1,12 @@ +mutation UpdateCollaborationUpdate($input:UpdateProjectV2CollaboratorsInput!){ + updateProjectV2Collaborators(input: $input){ + collaborators(first:100) { + totalCount, + nodes{ + __typename + ... on User {{user}} + ...on Team {{team}} + } + } + } +} \ No newline at end of file diff --git a/public/issues/New-ProjectIssue.ps1 b/public/issues/New-ProjectIssue.ps1 index eea982a..263c77b 100644 --- a/public/issues/New-ProjectIssue.ps1 +++ b/public/issues/New-ProjectIssue.ps1 @@ -3,11 +3,13 @@ Set-MyInvokeCommandAlias -Alias CreateIssue -Command 'Invoke-CreateIssue -Reposi function New-ProjectIssueDirect { [CmdletBinding()] + [Alias("New-Issue")] param ( [Parameter(Mandatory, Position = 1)][string]$RepoOwner, [Parameter(Mandatory, Position = 2)][string]$RepoName, [Parameter(Mandatory, Position = 3)][string]$Title, - [Parameter(Position = 4)][string]$Body + [Parameter(Position = 4)][string]$Body, + [Parameter()][switch]$OpenOnCreation ) $repo = Get-Repository -Owner $RepoOwner -Name $RepoName @@ -35,9 +37,13 @@ function New-ProjectIssueDirect { $ret = $issue.url + if( $OpenOnCreation ) { + Open-Url $ret + } + return $ret -} Export-ModuleMember -Function New-ProjectIssueDirect +} Export-ModuleMember -Function New-ProjectIssueDirect -Alias New-Issue function New-ProjectIssue { [CmdletBinding()] diff --git a/public/items/project_item_show.ps1 b/public/items/project_item_show.ps1 index 663ce6a..0e218e2 100644 --- a/public/items/project_item_show.ps1 +++ b/public/items/project_item_show.ps1 @@ -171,11 +171,11 @@ function writeHeader2{ if(-not [string]::IsNullOrWhiteSpace($Author)){ addSpace - $Author | write -Color $subcolor -PreFix "By: " + $Author | write -Color $subcolor -PreFix "By:[" -SuFix "]" } if(-not [string]::IsNullOrWhiteSpace($updatedAt)){ addSpace - $updatedAt | write -Color $subcolor -PreFix "At: " + $updatedAt | write -Color $subcolor -PreFix "At:[" -SuFix "]" } addJumpLine -message "Header 2 End " diff --git a/public/items/use_order.ps1 b/public/items/use_order.ps1 index 70323a2..6cacdb8 100644 --- a/public/items/use_order.ps1 +++ b/public/items/use_order.ps1 @@ -5,7 +5,7 @@ function Use-Order { [Parameter(Position = 0)][int]$Ordinal = -1, [Parameter(ValueFromPipeline)][array]$List, [Parameter()][switch]$OpenInEditor, - [Parameter()][switch]$OpenInBrowser + [Parameter()][Alias("w")][switch]$OpenInBrowser ) begin { diff --git a/public/project/addprojectuser.ps1 b/public/project/addprojectuser.ps1 new file mode 100644 index 0000000..0a55810 --- /dev/null +++ b/public/project/addprojectuser.ps1 @@ -0,0 +1,73 @@ +Set-MyInvokeCommandAlias -Alias "updateProjectV2Collaborators" -Command 'Invoke-UpdateProjectV2Collaborators -ProjectId {projectid} -collaborators "{collaboratorsIds}" -Role "{role}"' + +function Add-ProjectUser { + [CmdletBinding()] + param( + [Parameter()][string]$Owner, + [Parameter()][int]$ProjectNumber, + [Parameter(Mandatory,ValueFromPipeline)][string]$Handle, + [Parameter()][string]$Role ="WRITER" + + ) + + begin{ + + ($Owner, $ProjectNumber) = Get-OwnerAndProjectNumber -Owner $Owner -ProjectNumber $ProjectNumber + if ([string]::IsNullOrWhiteSpace($owner) -or [string]::IsNullOrWhiteSpace($ProjectNumber)) { + throw "Owner and ProjectNumber are required on Get-Project" + } + + $project = Get-Project -Owner $Owner -ProjectNumber $ProjectNumber -SkipItems + + $projectId = $project.ProjectId + + $userIds = @() + + } + + process{ + + $user = Get-User -Handle $Handle + $userId = $user.Id + + if([string]::IsNullOrWhiteSpace($userId)){ + Write-Error "No user found for handle [$Handle]" + } + + $userIds += $userId + } + + end{ + + $userIdsString = $userIds -join " " + + if([string]::IsNullOrWhiteSpace($userIdsString)){ + Write-Error "No users found" + return $false + } + + $response = Invoke-MyCommand -Command "updateProjectV2Collaborators" -Parameters @{ + projectid = $projectId + role = $Role + collaboratorsIds = $userIdsString + } + + # Check reply data to confirm users were added + if($response.data.updateProjectV2Collaborators.collaborators.totalCount -ne $userIds.Count){ + Write-Error "Not all users were added to the project" + return $false + } + + return $true + } + + + + + + + + + + +} Export-ModuleMember -Function Add-ProjectUser diff --git a/public/project/getproject.ps1 b/public/project/getproject.ps1 index c690524..1aa96fd 100644 --- a/public/project/getproject.ps1 +++ b/public/project/getproject.ps1 @@ -49,8 +49,8 @@ function Get-ProjectId { function Open-Project{ [CmdletBinding()] param( - [Parameter()][string]$Owner, - [Parameter()][int]$ProjectNumber + [Parameter(ValueFromPipelineByPropertyName)][string]$Owner, + [Parameter(ValueFromPipelineByPropertyName)][int]$ProjectNumber ) ($Owner, $ProjectNumber) = Get-OwnerAndProjectNumber -Owner $Owner -ProjectNumber $ProjectNumber