Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance Get-VSTeamProcess #300 #322

Closed
wants to merge 10 commits into from
17 changes: 7 additions & 10 deletions Source/Classes/ProcessTemplateCompleter.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ using namespace System.Collections
using namespace System.Collections.Generic
using namespace System.Management.Automation

# This class defines an attribute that allows the user the tab complete
# process templates for function parameters.
# This class defines an attribute that allows the user the tab complete process templates
# for function parameters.
class ProcessTemplateCompleter : IArgumentCompleter {
[IEnumerable[CompletionResult]] CompleteArgument(
[string] $CommandName,
Expand All @@ -14,17 +14,14 @@ class ProcessTemplateCompleter : IArgumentCompleter {

$results = [List[CompletionResult]]::new()

if (_hasProcessTemplateCacheExpired) {
[VSTeamProcessCache]::processes = _getProcesses
[VSTeamProcessCache]::timestamp = (Get-Date).Minute
}

foreach ($p in [VSTeamProcessCache]::processes) {
if ($p -like "*$WordToComplete*") {
foreach ($p in [VSTeamProcessCache]::GetCurrent()) {
if ($p -like "*$WordToComplete*" -and $p -notmatch '\W') {
$results.Add([CompletionResult]::new($p))
}
elseif ($p -like "*$WordToComplete*") {
$results.Add([CompletionResult]::new("'$($p.replace("'","''"))'", $p, 0, $p))
}
}

return $results
}
}
13 changes: 6 additions & 7 deletions Source/Classes/ProcessValidateAttribute.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ class ProcessValidateAttribute : ValidateArgumentsAttribute {
[void] Validate(
[object] $arguments,
[EngineIntrinsics] $EngineIntrinsics) {

if (_hasProcessTemplateCacheExpired) {
[VSTeamProcessCache]::processes = _getProcesses
[VSTeamProcessCache]::timestamp = (Get-Date).Minute
}

if (($null -ne [VSTeamProcessCache]::processes) -and (-not ($arguments -in [VSTeamProcessCache]::processes))) {
#Do not fail on null or empty, leave that to other validation conditions
if ([string]::IsNullOrEmpty($arguments)) {return}
#tests count HTTP calls and expect 1 from reading the cache, but for a call on each read, so only read once! #
$CachedProcesses = [VSTeamProcessCache]::GetCurrent()
if (($null -ne $CachedProcesses) -and ($arguments -notin $CachedProcesses) ) {
throw [ValidationMetadataException]::new(
"'$arguments' is not a valid process. Valid processes are: '" +
([VSTeamProcessCache]::processes -join "', '") + "'")
($CachedProcesses -join "', '") + "'")
}
}
}
15 changes: 7 additions & 8 deletions Source/Classes/ProjectCompleter.ps1
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using namespace System.Collections
using namespace System.Collections.Generic
using namespace System.Management.Automation

# This class defines an attribute that allows the user the tab complete project names
# for function parameters.
class ProjectCompleter : IArgumentCompleter {
[IEnumerable[CompletionResult]] CompleteArgument(
[string] $CommandName,
Expand All @@ -12,15 +13,13 @@ class ProjectCompleter : IArgumentCompleter {

$results = [List[CompletionResult]]::new()

if (_hasProjectCacheExpired) {
[VSTeamProjectCache]::projects = _getProjects
[VSTeamProjectCache]::timestamp = (Get-Date).Minute
}

foreach ($p in [VSTeamProjectCache]::projects) {
if ($p -like "*$WordToComplete*") {
foreach ($p in [VSTeamProjectCache]::GetCurrent()) {
if ($p -like "*$WordToComplete*" -and $p -notmatch "\W") {
$results.Add([CompletionResult]::new($p))
}
elseif ($p -like "*$WordToComplete*"){
$results.Add([CompletionResult]::new("'$($p.replace("'","''"))'", $p, 0, $p))
}
}

return $results
Expand Down
10 changes: 4 additions & 6 deletions Source/Classes/ProjectValidateAttribute.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ class ProjectValidateAttribute : ValidateArgumentsAttribute {
[void] Validate(
[object] $arguments,
[EngineIntrinsics] $EngineIntrinsics) {

if (_hasProjectCacheExpired) {
[VSTeamProjectCache]::projects = _getProjects
[VSTeamProjectCache]::timestamp = (Get-Date).Minute
}

if (($null -ne [VSTeamProjectCache]::projects) -and (-not ($arguments -in [VSTeamProjectCache]::projects))) {
#Do not fail on null or empty, leave that to other validation conditions
if ([string]::IsNullOrEmpty($arguments)) {return}

if (($null -ne [VSTeamProjectCache]::GetCurrent()) -and (-not ($arguments -in [VSTeamProjectCache]::projects))) {
throw [ValidationMetadataException]::new(
"'$arguments' is not a valid project. Valid projects are: '" +
([VSTeamProjectCache]::projects -join "', '") + "'")
Expand Down
25 changes: 16 additions & 9 deletions Source/Classes/VSTeamProcess.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,30 @@ class VSTeamProcess {
[string]$URL = $null
[string]$Description = $null
[string]$Name = $null
[string]$ReferenceName = $null
[bool]$IsEnabled = $true
[bool]$IsDefault = $false
[string]$Type = $null

[string]$ProcessTemplate = $null
[string]$ParentProcessTypeId = $null
VSTeamProcess (
[object]$obj
) {
$this.ID = $obj.id
$this.URL = $obj.url
$this.IsDefault = $obj.isDefault
$this.Name = $obj.name
$this.Type = $obj.type

$this.ID = $obj.typeId
$this.URL = [VSTeamVersions]::Account + "/_apis/work/processes/" + $obj.typeId
$this.IsEnabled = $obj.isEnabled
$this.IsDefault = $obj.isDefault
$this.ReferenceName = $obj.referenceName
$this.Name = $obj.name
$this.ProcessTemplate = $obj.name
$this.Type = $obj.customizationType
# The description is not always returned so protect yourself.
if ($obj.PSObject.Properties.Match('description').count -gt 0) {
$this.Description = $obj.description
$this.Description = $obj.description
}
if ($obj.PSObject.Properties.Match('parentProcessTypeId').count -gt 0) {
$this.ParentProcessTypeId = $obj.parentProcessTypeId
}

$this.AddTypeName('Team.Process')
}

Expand Down
24 changes: 24 additions & 0 deletions Source/Classes/VSTeamProcessCache.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,28 @@
class VSTeamProcessCache {
static [int] $timestamp = -1
static [object] $processes = $null
static [hashtable] $urls = @{}
static [Void] Update () {
#Allow unit tests to mock returning the project list and testing freshness
$list = _getProcesses
if ($list) {
foreach ($process in $list) {
[VSTeamProcessCache]::urls[$process.name] = (_getInstance) + "/_apis/work/processes/" + $process.Id
}
[VSTeamProcessCache]::processes = $List.name | Sort-Object
}
else {
[VSTeamProcessCache]::processes = $null
}
[VSTeamProcessCache]::timestamp = (Get-Date).Minute
# "save current minute" refreshes on average after 30secs but not after exact hours timeOfDayTotalMinutes might be a better base
}
static [object] GetCurrent () {
if (_hasProcessTemplateCacheExpired) { [VSTeamProcessCache]::Update() }
return ([VSTeamProcessCache]::processes)
}
static [object] GetURl ([string]$ProcessName) {
if (_hasProcessTemplateCacheExpired) { [VSTeamProcessCache]::Update() }
return ([VSTeamProcessCache]::urls[$ProcessName])
}
}
13 changes: 13 additions & 0 deletions Source/Classes/VSTeamProjectCache.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,17 @@
class VSTeamProjectCache {
static [int] $timestamp = -1
static [object] $projects = $null
static [Void] Update () {
#Allow unit tests to mock returning the project list and testing freshness
[VSTeamProjectCache]::projects = _getProjects
[VSTeamProjectCache]::timestamp = (Get-Date).Minute
# "save current minute" refreshes on average after 30secs but not after exact hours timeOfDayTotalMinutes might be a better base
}
static [void] Invalidate () {
[VSTeamProjectCache]::timestamp = -1
}
static [object] GetCurrent () {
if (_hasProjectCacheExpired) { [VSTeamProjectCache]::Update() }
return ([VSTeamProjectCache]::projects)
}
}
36 changes: 17 additions & 19 deletions Source/Public/Get-VSTeamProcess.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
function Get-VSTeamProcess {
[CmdletBinding(DefaultParameterSetName = 'List')]
[OutputType([vsteamprocess])]
param(
[Parameter(ParameterSetName = 'ByName', Position=0)]
[ArgumentCompleter([ProcessTemplateCompleter])]
[Alias('ProcessName','ProcessTemplate')]
$Name = '*',

[Parameter(ParameterSetName = 'List')]
[int] $Top = 100,

Expand All @@ -9,12 +15,7 @@ function Get-VSTeamProcess {

[Parameter(ParameterSetName = 'ByID')]
[Alias('ProcessTemplateID')]
[string] $Id,

[Parameter(ParameterSetName = 'ByName', Mandatory = $true)]
[ProcessValidateAttribute()]
[ArgumentCompleter([ProcessTemplateCompleter])]
[string] $Name
[string] $Id
)
process {
if ($id) {
Expand All @@ -29,28 +30,25 @@ function Get-VSTeamProcess {

Write-Output $project
}
elseif ($Name) {
# Lookup Process ID by Name
Get-VSTeamProcess | where-object { $_.name -eq $Name }
}

else {
# Return list of processes
try {
# Call the REST API
$resp = _callAPI -area 'process' -resource 'processes' `
-Version $(_getApiVersion Core) -NoProject `
$resp = _callAPI -Area 'work' -resource 'processes' `
-Version $(_getApiVersion Graph) -NoProject `
-QueryString @{
'$top' = $top
'$skip' = $skip
}

$objs = @()

foreach ($item in $resp.value) {
$objs += [VSTeamProcess]::new($item)
}

Write-Output $objs
#we just fetched the processes so let's update the cache. Also Cache the URLS for processes
[VSTeamProcessCache]::processes = $resp.value.name | Sort-Object
jhoneill marked this conversation as resolved.
Show resolved Hide resolved
[VSTeamProcessCache]::timestamp = (Get-Date).Minute
$resp.value | ForEach-Object {
[VSTeamProcessCache]::urls[$_.name] = _getInstance + "/_apis/work/processes/" + $_.Id
[VSTeamProcess]::new($_)
} | Where-Object {$_.name -like $Name} | Sort-Object -Property Name
}
catch {
# I catch because using -ErrorAction Stop on the Invoke-RestMethod
Expand Down
2 changes: 1 addition & 1 deletion unit/test/ProcessValidateAttribute.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Describe "ProcessValidateAttribute" {
Context "Existing Process" {
BeforeAll {
Mock _hasProcessTemplateCacheExpired { return $true }
Mock _getProcesses { return @("Test1", "Test2") }
Mock _getProcesses { return @(@{Name='Test1';Id=''},@{Name='Test2';Id=''})}
}

It "it is not in list and should throw" {
Expand Down