From d383ac235178bfd6f5ac25fd07ce7cc3efbe1579 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Wed, 24 Jul 2024 15:27:23 -0300 Subject: [PATCH 01/90] Fix typo --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c3c9139d..6a858481 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -184,8 +184,8 @@ jobs: git checkout --track origin/gh-pages unzip -o ../../TfsCmdlets-Docs-*.zip popd - Write-Output build_info: TfsCmdlets v${BUILD_NAME}, released $(date +%F) >> _config.yml - Write-Output >> _config.yml + echo build_info: TfsCmdlets v${BUILD_NAME}, released $(date +%F) >> _config.yml + echo >> _config.yml git config --local user.name "Igor Abade" git config --local user.email igoravl@gmail.com git add * From adaa14dba59197a80df34e7601e4ae595f58aa4e Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 25 Jul 2024 10:49:45 -0300 Subject: [PATCH 02/90] Remove obsolete projects --- ...TfsCmdlets.Tests.Integration.PSCore.csproj | 16 ----- .../Properties/AssemblyInfo.cs | 20 ------ ...Cmdlets.Tests.Integration.PSDesktop.csproj | 68 ------------------- .../packages.config | 5 -- CSharp/TfsCmdlets.sln | 4 -- 5 files changed, 113 deletions(-) delete mode 100644 CSharp/TfsCmdlets.Tests.Integration.PSCore/TfsCmdlets.Tests.Integration.PSCore.csproj delete mode 100644 CSharp/TfsCmdlets.Tests.Integration.PSDesktop/Properties/AssemblyInfo.cs delete mode 100644 CSharp/TfsCmdlets.Tests.Integration.PSDesktop/TfsCmdlets.Tests.Integration.PSDesktop.csproj delete mode 100644 CSharp/TfsCmdlets.Tests.Integration.PSDesktop/packages.config diff --git a/CSharp/TfsCmdlets.Tests.Integration.PSCore/TfsCmdlets.Tests.Integration.PSCore.csproj b/CSharp/TfsCmdlets.Tests.Integration.PSCore/TfsCmdlets.Tests.Integration.PSCore.csproj deleted file mode 100644 index 8dc9555f..00000000 --- a/CSharp/TfsCmdlets.Tests.Integration.PSCore/TfsCmdlets.Tests.Integration.PSCore.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - netcoreapp3.1 - - false - - - - - - - - - - diff --git a/CSharp/TfsCmdlets.Tests.Integration.PSDesktop/Properties/AssemblyInfo.cs b/CSharp/TfsCmdlets.Tests.Integration.PSDesktop/Properties/AssemblyInfo.cs deleted file mode 100644 index 9dfed73f..00000000 --- a/CSharp/TfsCmdlets.Tests.Integration.PSDesktop/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("TfsCmdlets.Tests.Integration.PSDesktop")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("TfsCmdlets.Tests.Integration.PSDesktop")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] - -[assembly: Guid("0a52a2c0-75eb-438a-ba6f-8fc28faebfdf")] - -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CSharp/TfsCmdlets.Tests.Integration.PSDesktop/TfsCmdlets.Tests.Integration.PSDesktop.csproj b/CSharp/TfsCmdlets.Tests.Integration.PSDesktop/TfsCmdlets.Tests.Integration.PSDesktop.csproj deleted file mode 100644 index 858befd6..00000000 --- a/CSharp/TfsCmdlets.Tests.Integration.PSDesktop/TfsCmdlets.Tests.Integration.PSDesktop.csproj +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - Debug - AnyCPU - {0A52A2C0-75EB-438A-BA6F-8FC28FAEBFDF} - Library - Properties - TfsCmdlets.Tests.Integration.PSDesktop - TfsCmdlets.Tests.Integration.PSDesktop - v4.8 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - - - ..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - \ No newline at end of file diff --git a/CSharp/TfsCmdlets.Tests.Integration.PSDesktop/packages.config b/CSharp/TfsCmdlets.Tests.Integration.PSDesktop/packages.config deleted file mode 100644 index 96c954f2..00000000 --- a/CSharp/TfsCmdlets.Tests.Integration.PSDesktop/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/CSharp/TfsCmdlets.sln b/CSharp/TfsCmdlets.sln index 7b328801..0a3463c5 100644 --- a/CSharp/TfsCmdlets.sln +++ b/CSharp/TfsCmdlets.sln @@ -7,10 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets.Tests.UnitTests" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{990DD3E9-3685-472A-AB57-2D304A5C86D6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets.Tests.Integration.PSCore", "TfsCmdlets.Tests.Integration.PSCore\TfsCmdlets.Tests.Integration.PSCore.csproj", "{6CC762DD-A6C9-435C-8CA2-ECE8CE615309}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TfsCmdlets.Tests.Integration.PSDesktop", "TfsCmdlets.Tests.Integration.PSDesktop\TfsCmdlets.Tests.Integration.PSDesktop.csproj", "{0A52A2C0-75EB-438A-BA6F-8FC28FAEBFDF}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets", "TfsCmdlets\TfsCmdlets.csproj", "{63E2B0B3-DB12-4DE6-ADE8-660E8323E38A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets.SourceGenerators", "TfsCmdlets.SourceGenerators\TfsCmdlets.SourceGenerators.csproj", "{1045CB54-F17F-42EA-9497-5D60815BDC64}" From 9da48788ac5d5fd552f3a0456f1d5d6029f33fc1 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 25 Jul 2024 10:51:29 -0300 Subject: [PATCH 03/90] Enable C# Dev Kit extension --- .vscode/settings.json | 2 +- CSharp/TfsCmdlets.sln | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0ff5ca05..2a5a0b52 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -41,5 +41,5 @@ "titleBar.inactiveForeground": "#e7e7e799" }, "peacock.color": "Indigo", - "dotnet.preferCSharpExtension": true + "dotnet.preferCSharpExtension": false } \ No newline at end of file diff --git a/CSharp/TfsCmdlets.sln b/CSharp/TfsCmdlets.sln index 0a3463c5..80ecd1fc 100644 --- a/CSharp/TfsCmdlets.sln +++ b/CSharp/TfsCmdlets.sln @@ -23,14 +23,6 @@ Global {1D239B0E-69CB-46E3-9D9A-A41349AF3BB9}.Debug|Any CPU.Build.0 = Debug|Any CPU {1D239B0E-69CB-46E3-9D9A-A41349AF3BB9}.Release|Any CPU.ActiveCfg = Release|Any CPU {1D239B0E-69CB-46E3-9D9A-A41349AF3BB9}.Release|Any CPU.Build.0 = Release|Any CPU - {6CC762DD-A6C9-435C-8CA2-ECE8CE615309}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6CC762DD-A6C9-435C-8CA2-ECE8CE615309}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6CC762DD-A6C9-435C-8CA2-ECE8CE615309}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6CC762DD-A6C9-435C-8CA2-ECE8CE615309}.Release|Any CPU.Build.0 = Release|Any CPU - {0A52A2C0-75EB-438A-BA6F-8FC28FAEBFDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0A52A2C0-75EB-438A-BA6F-8FC28FAEBFDF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0A52A2C0-75EB-438A-BA6F-8FC28FAEBFDF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0A52A2C0-75EB-438A-BA6F-8FC28FAEBFDF}.Release|Any CPU.Build.0 = Release|Any CPU {63E2B0B3-DB12-4DE6-ADE8-660E8323E38A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {63E2B0B3-DB12-4DE6-ADE8-660E8323E38A}.Debug|Any CPU.Build.0 = Debug|Any CPU {63E2B0B3-DB12-4DE6-ADE8-660E8323E38A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -49,8 +41,6 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {1D239B0E-69CB-46E3-9D9A-A41349AF3BB9} = {990DD3E9-3685-472A-AB57-2D304A5C86D6} - {6CC762DD-A6C9-435C-8CA2-ECE8CE615309} = {990DD3E9-3685-472A-AB57-2D304A5C86D6} - {0A52A2C0-75EB-438A-BA6F-8FC28FAEBFDF} = {990DD3E9-3685-472A-AB57-2D304A5C86D6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FE0EFD1C-9A73-41C3-BD86-7E8112462C2B} From 21195d4cad7a79cb68b8bf604230913ab4d8d34f Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 26 Jul 2024 09:56:24 -0300 Subject: [PATCH 04/90] Remove obsolete projects from solution --- CSharp/TfsCmdlets.sln | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CSharp/TfsCmdlets.sln b/CSharp/TfsCmdlets.sln index 80ecd1fc..99e41947 100644 --- a/CSharp/TfsCmdlets.sln +++ b/CSharp/TfsCmdlets.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets.SourceGenerators EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets.Common", "TfsCmdlets.Common\TfsCmdlets.Common.csproj", "{CD6FA384-22B2-42E8-8D4B-69BBF66DC792}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TfsCmdlets.SourceGeneratores.UnitTests", "TfsCmdlets.SourceGeneratores.UnitTests\TfsCmdlets.SourceGeneratores.UnitTests.csproj", "{76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -35,12 +37,17 @@ Global {CD6FA384-22B2-42E8-8D4B-69BBF66DC792}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD6FA384-22B2-42E8-8D4B-69BBF66DC792}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD6FA384-22B2-42E8-8D4B-69BBF66DC792}.Release|Any CPU.Build.0 = Release|Any CPU + {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {1D239B0E-69CB-46E3-9D9A-A41349AF3BB9} = {990DD3E9-3685-472A-AB57-2D304A5C86D6} + {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7} = {990DD3E9-3685-472A-AB57-2D304A5C86D6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FE0EFD1C-9A73-41C3-BD86-7E8112462C2B} From ab925e21197b8d18582d45b3609f6e2c49eb4f68 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 26 Jul 2024 09:57:07 -0300 Subject: [PATCH 05/90] Move conditional usings --- .../ProcessTemplate/ExportProcessTemplate.cs | 104 +++++++++--------- .../ProcessTemplate/ImportProcessTemplate.cs | 16 +-- .../NewTeamProjectCollection.cs | 11 +- 3 files changed, 68 insertions(+), 63 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/ExportProcessTemplate.cs b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/ExportProcessTemplate.cs index 82bf5195..dff4ddbf 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/ExportProcessTemplate.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/ExportProcessTemplate.cs @@ -1,14 +1,16 @@ using System.Management.Automation; -#if NET471_OR_GREATER -using TfsCmdlets.Cmdlets.ProcessTemplate; -using Microsoft.TeamFoundation.Server; -using System.Xml; -using System.Xml.Linq; -using System.IO.Compression; -#endif namespace TfsCmdlets.Cmdlets.ProcessTemplate { + +#if NET471_OR_GREATER + using TfsCmdlets.Cmdlets.ProcessTemplate; + using Microsoft.TeamFoundation.Server; + using System.Xml; + using System.Xml.Linq; + using System.IO.Compression; +#endif + /// /// Exports a XML-based process template definition to disk. /// @@ -29,7 +31,7 @@ namespace TfsCmdlets.Cmdlets.ProcessTemplate /// Exports the Scrum process template from the DefaultCollection project collection in the VSALM server, saving the template files to the C:\PT\MyScrum directory in the local computer. Notice that the process template is being renamed from Scrum to MyScrum, so that it can be later reimported as a new process template instead of overwriting the original one. /// [TfsCmdlet(CmdletScope.Collection, DesktopOnly = true)] - partial class ExportProcessTemplate + partial class ExportProcessTemplate { /// /// Specifies the name of the process template(s) to be exported. Wildcards are supported. @@ -83,63 +85,63 @@ protected override IEnumerable Run() var destinationPath = Parameters.Get(nameof(ExportProcessTemplate.DestinationPath)); var force = Parameters.Get(nameof(ExportProcessTemplate.Force)); - var tpc = Data.GetCollection(); - var processTemplateSvc = Data.GetService(); - IList templates = Data.GetItems().ToList(); + var tpc = Data.GetCollection(); + var processTemplateSvc = Data.GetService(); + IList templates = Data.GetItems().ToList(); - if (templates.Count == 0) return null; + if (templates.Count == 0) return null; - if ((!string.IsNullOrEmpty(newName) || !string.IsNullOrEmpty(newDescription)) && templates.Count > 1) - { - PowerShell.WriteError(new ArgumentException($"Cannot specify a new name or a new description when " + - $"exporting multiple processes. The search criteria '{processTemplate}' matched the " + - $"following processes: {string.Join(", ", templates.Select(th => $"'{th.Name}'"))}")); + if ((!string.IsNullOrEmpty(newName) || !string.IsNullOrEmpty(newDescription)) && templates.Count > 1) + { + PowerShell.WriteError(new ArgumentException($"Cannot specify a new name or a new description when " + + $"exporting multiple processes. The search criteria '{processTemplate}' matched the " + + $"following processes: {string.Join(", ", templates.Select(th => $"'{th.Name}'"))}")); return null; - } - else - { - templates[0].Name = newName ?? templates[0].Name; - templates[0].Description = newDescription ?? templates[0].Description; - } + } + else + { + templates[0].Name = newName ?? templates[0].Name; + templates[0].Description = newDescription ?? templates[0].Description; + } - foreach (var template in templates) - { - if (!PowerShell.ShouldProcess($"Team Project Collection '{tpc.DisplayName}'", - $"Export process template '{template.Name}'")) - { - continue; - } + foreach (var template in templates) + { + if (!PowerShell.ShouldProcess($"Team Project Collection '{tpc.DisplayName}'", + $"Export process template '{template.Name}'")) + { + continue; + } - var outDir = Path.Combine(destinationPath ?? PowerShell.GetCurrentDirectory(), template.Name); + var outDir = Path.Combine(destinationPath ?? PowerShell.GetCurrentDirectory(), template.Name); - if (Directory.Exists(outDir) && Directory.GetFileSystemEntries(outDir).Length > 0) - { - if (!force && !PowerShell.ShouldContinue($"Overwrite destination directory {outDir}? All its content will be lost.", "Delete directory")) - { - continue; - } + if (Directory.Exists(outDir) && Directory.GetFileSystemEntries(outDir).Length > 0) + { + if (!force && !PowerShell.ShouldContinue($"Overwrite destination directory {outDir}? All its content will be lost.", "Delete directory")) + { + continue; + } - Directory.Delete(outDir, true); - } + Directory.Delete(outDir, true); + } - Directory.CreateDirectory(outDir); + Directory.CreateDirectory(outDir); - var tempFile = processTemplateSvc.GetTemplateData(template.TemplateId); - var zipFile = $"{tempFile}.zip"; + var tempFile = processTemplateSvc.GetTemplateData(template.TemplateId); + var zipFile = $"{tempFile}.zip"; - File.Move(tempFile, zipFile); + File.Move(tempFile, zipFile); - ZipFile.ExtractToDirectory(zipFile, outDir); + ZipFile.ExtractToDirectory(zipFile, outDir); - var ptFile = Path.Combine(outDir, "ProcessTemplate.xml"); - var ptXml = XDocument.Load(ptFile); + var ptFile = Path.Combine(outDir, "ProcessTemplate.xml"); + var ptXml = XDocument.Load(ptFile); - ptXml.Element("ProcessTemplate").Element("metadata").Element("name").Value = template.Name; - ptXml.Element("ProcessTemplate").Element("metadata").Element("description").Value = template.Description; - ptXml.Save(ptFile); + ptXml.Element("ProcessTemplate").Element("metadata").Element("name").Value = template.Name; + ptXml.Element("ProcessTemplate").Element("metadata").Element("description").Value = template.Description; + ptXml.Save(ptFile); - File.Delete(zipFile); - } + File.Delete(zipFile); + } #endif return null; } diff --git a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/ImportProcessTemplate.cs b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/ImportProcessTemplate.cs index 6c1c7bfb..f2703953 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/ImportProcessTemplate.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/ImportProcessTemplate.cs @@ -1,14 +1,16 @@ using System.Management.Automation; -#if NET471_OR_GREATER -using TfsCmdlets.Cmdlets.ProcessTemplate; -using Microsoft.TeamFoundation.Server; -using System.Xml; -using System.Xml.Linq; -using System.IO.Compression; -#endif namespace TfsCmdlets.Cmdlets.ProcessTemplate { + +#if NET471_OR_GREATER + using TfsCmdlets.Cmdlets.ProcessTemplate; + using Microsoft.TeamFoundation.Server; + using System.Xml; + using System.Xml.Linq; + using System.IO.Compression; +#endif + /// /// Imports a process template definition from disk. /// diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/NewTeamProjectCollection.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/NewTeamProjectCollection.cs index a087ea7b..45c5eee8 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/NewTeamProjectCollection.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/NewTeamProjectCollection.cs @@ -4,12 +4,13 @@ using Microsoft.TeamFoundation.Framework.Common; using TfsCmdlets.Models; +namespace TfsCmdlets.Cmdlets.TeamProjectCollection +{ + #if NET471_OR_GREATER -using Microsoft.TeamFoundation.Framework.Client; + using Microsoft.TeamFoundation.Framework.Client; #endif -namespace TfsCmdlets.Cmdlets.TeamProjectCollection -{ /// /// Creates a new team project collection. /// @@ -42,11 +43,11 @@ partial class NewTeamProjectCollection [ValidateSet("Started", "Stopped")] public string InitialState { get; set; } = "Started"; - [Parameter] + [Parameter] [ValidateRange(5, 60)] public int PollingInterval { get; set; } = 5; - [Parameter] + [Parameter] public TimeSpan Timeout { get; set; } = TimeSpan.MaxValue; } From bf29c892bdc76ffcf94227fcdcd568fca9f62a5e Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 26 Jul 2024 09:57:19 -0300 Subject: [PATCH 06/90] Add new cmdlets --- .../WorkItem/Field/GetWorkItemField.cs | 85 ++++++++++++++++++ .../WorkItem/Field/NewWorkItemField.cs | 86 +++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs create mode 100644 CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/NewWorkItemField.cs diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs new file mode 100644 index 00000000..affc22af --- /dev/null +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs @@ -0,0 +1,85 @@ +using Microsoft.TeamFoundation.WorkItemTracking.WebApi; +using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; + +namespace TfsCmdlets.Cmdlets.WorkItem.Field +{ + /// + /// Gets information from one or more process templates. + /// + [TfsCmdlet(CmdletScope.Collection, OutputType = typeof(WorkItemField))] + partial class GetWorkItemField + { + /// + /// Specifies the name or the reference name of the field(s) to be returned. Wildcards are supported. + /// When omitted, all fields in the given organization are returned. + /// + [Parameter(Position = 0)] + [Alias("Name")] + [SupportsWildcards()] + public object Field { get; set; } = "*"; + + /// + /// Limits the search to the specified project. + /// + [Parameter] + public object Project { get; set; } + + /// + /// Specifies whether to include extension fields in the result. + /// + [Parameter] + public SwitchParameter IncludeExtensionFields { get; set; } + + /// + /// Specifies whether to include deleted fields in the result. + /// + [Parameter] + public SwitchParameter IncludeDeleted { get; set; } + } + + // Controller + + [CmdletController] + partial class GetWorkItemFieldController + { + protected override IEnumerable Run() + { + var client = Data.GetClient(); + + string tpName; + + if(Has_Project) { + var tp = Data.GetProject(); + tpName = tp.Name; + } + + foreach (var f in Field) + { + switch (f) + { + case string s when s.IsWildcard(): + { + var expand = GetFieldsExpand.None | + (IncludeExtensionFields ? GetFieldsExpand.ExtensionFields : GetFieldsExpand.None) | + (IncludeDeleted ? GetFieldsExpand.IncludeDeleted : GetFieldsExpand.None); + + yield return client.GetFieldsAsync(expand) + .GetResult($"Error getting field '{s}'") + .Where(field => field.Name.IsLike(s) || field.ReferenceName.IsLike(s)); + break; + } + case string s: + { + yield return client.GetFieldAsync(fieldNameOrRefName: s) + .GetResult($"Error getting field '{s}'"); + break; + } + default: + { + throw new ArgumentException($"Invalid or non-existent field '{f}'"); + } + } + } + } + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/NewWorkItemField.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/NewWorkItemField.cs new file mode 100644 index 00000000..abbf22a0 --- /dev/null +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/NewWorkItemField.cs @@ -0,0 +1,86 @@ +using Microsoft.TeamFoundation.WorkItemTracking.WebApi; +using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; + +namespace TfsCmdlets.Cmdlets.WorkItem.Field +{ + /// + /// Gets information from one or more process templates. + /// + [TfsCmdlet(CmdletScope.Collection, OutputType = typeof(WorkItemField))] + partial class NewWorkItemField + { + /// + /// Specifies the name of the field. + /// + [Parameter(Position = 0, Mandatory = true)] + [Alias("Name")] + public string Field { get; set; } + + /// + /// Specifies the reference name of the field. It should contain only letters, numbers, dots and underscores. + /// + [Parameter(Position = 1)] + [Alias("Name")] + public string ReferenceName { get; set; } + + /// + /// Specifies the description of the field. + /// + [Parameter] + public string Description { get; set; } + + /// + /// Specifies the type of the field. + /// + [Parameter] + public FieldType Type { get; set; } = FieldType.String; + + /// + /// Specifies whether the field is read-only. + /// + [Parameter] + public bool ReadOnly { get; set; } + + /// + /// Specifies whether the field is sortable in server queries. + /// + [Parameter] + public bool CanSortBy { get; set; } + + /// + /// Specifies whether the field can be queried in the server. + /// + [Parameter] + public bool IsQueryable { get; set; } + + /// + /// Specifies whether the field is an identity field. + /// + [Parameter] + public SwitchParameter IsIdentity { get; set; } + + /// + /// Specifies the contents of the picklist. Supplying values to this parameter will automatically + /// turn the field into a picklist. + /// + [Parameter] + public object[] PicklistItems { get; set; } + + /// + /// Specifies whether the user can enter a custom value in the picklist,making it a list of suggested values instead of allowed values. + /// + [Parameter] + public SwitchParameter PicklistSuggested { get; set; } + } + + // Controller + + [CmdletController] + partial class NewWorkItemFieldController + { + protected override IEnumerable Run() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file From e959c8e931f2c93c6642c10ff05d599d790635a2 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 26 Jul 2024 09:57:42 -0300 Subject: [PATCH 07/90] Remove empty condiitonal --- .../Cmdlets/TeamProjectCollection/MountTeamProjectCollection.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/CSharp/TfsCmdlets.Legacy/Cmdlets/TeamProjectCollection/MountTeamProjectCollection.cs b/CSharp/TfsCmdlets.Legacy/Cmdlets/TeamProjectCollection/MountTeamProjectCollection.cs index 3be1e432..f0eb70f7 100644 --- a/CSharp/TfsCmdlets.Legacy/Cmdlets/TeamProjectCollection/MountTeamProjectCollection.cs +++ b/CSharp/TfsCmdlets.Legacy/Cmdlets/TeamProjectCollection/MountTeamProjectCollection.cs @@ -1,7 +1,5 @@ using System; using System.Management.Automation; -#if NET471_OR_GREATER -#endif namespace TfsCmdlets.Cmdlets.TeamProjectCollection { From 088e5cd08d9b377c6758d800bb3ddd8274439839 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 26 Jul 2024 09:59:18 -0300 Subject: [PATCH 08/90] Add new unit test project --- .../CSharpSourceGeneratorVerifier.cs | 42 ++++++++ .../Resolve_Default_Value_Namespace.cs | 97 +++++++++++++++++++ ...sCmdlets.SourceGenerators.UnitTests.csproj | 35 +++++++ 3 files changed, 174 insertions(+) create mode 100644 CSharp/TfsCmdlets.SourceGeneratores.UnitTests/CSharpSourceGeneratorVerifier.cs create mode 100644 CSharp/TfsCmdlets.SourceGeneratores.UnitTests/Controllers/Resolve_Default_Value_Namespace.cs create mode 100644 CSharp/TfsCmdlets.SourceGeneratores.UnitTests/TfsCmdlets.SourceGenerators.UnitTests.csproj diff --git a/CSharp/TfsCmdlets.SourceGeneratores.UnitTests/CSharpSourceGeneratorVerifier.cs b/CSharp/TfsCmdlets.SourceGeneratores.UnitTests/CSharpSourceGeneratorVerifier.cs new file mode 100644 index 00000000..b679873e --- /dev/null +++ b/CSharp/TfsCmdlets.SourceGeneratores.UnitTests/CSharpSourceGeneratorVerifier.cs @@ -0,0 +1,42 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Testing; +using System.Collections.Immutable; + +namespace TfsCmdlets.SourceGenerators.UnitTests +{ + public static class CSharpSourceGeneratorVerifier + where TSourceGenerator : ISourceGenerator, new() + { + public class Test : CSharpSourceGeneratorTest + { + public Test() + { + } + + protected override CompilationOptions CreateCompilationOptions() + { + var compilationOptions = base.CreateCompilationOptions(); + return compilationOptions.WithSpecificDiagnosticOptions( + compilationOptions.SpecificDiagnosticOptions.SetItems(GetNullableWarningsFromCompiler())); + } + + public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.Default; + + private static ImmutableDictionary GetNullableWarningsFromCompiler() + { + string[] args = { "/warnaserror:nullable" }; + var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory); + var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions; + + return nullableWarnings; + } + + protected override ParseOptions CreateParseOptions() + { + return ((CSharpParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion); + } + } + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets.SourceGeneratores.UnitTests/Controllers/Resolve_Default_Value_Namespace.cs b/CSharp/TfsCmdlets.SourceGeneratores.UnitTests/Controllers/Resolve_Default_Value_Namespace.cs new file mode 100644 index 00000000..bdc64e2d --- /dev/null +++ b/CSharp/TfsCmdlets.SourceGeneratores.UnitTests/Controllers/Resolve_Default_Value_Namespace.cs @@ -0,0 +1,97 @@ +using Microsoft.CodeAnalysis.Text; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TfsCmdlets.SourceGenerators.Generators.Controllers; +using Xunit; +using VerifyCS = TfsCmdlets.SourceGenerators.UnitTests.CSharpSourceGeneratorVerifier; + +namespace TfsCmdlets.SourceGenerators.UnitTests.Controllers +{ + public class Resolve_Default_Value_Namespace + { + [Fact] + public async Task Can_Resolve_Enum_Namespaces() + { + var code = """ + using Microsoft.TeamFoundation.WorkItemTracking.WebApi; + using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; + + namespace TfsCmdlets.Cmdlets.WorkItem.Field + { + /// + /// Gets information from one or more process templates. + /// + [TfsCmdlet(CmdletScope.Collection, OutputType = typeof(WorkItemField))] + partial class NewWorkItemField + { + /// + /// Specifies the type of the field. + /// + [Parameter] + public FieldType Type { get; set; } = FieldType.String; + } + } + """; + + var generated = """ + namespace TfsCmdlets.Cmdlets.WorkItem.Field + { + internal partial class NewWorkItemFieldController: ControllerBase + { + // Type + protected bool Has_Type { get; set; } + protected Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.FieldType Type { get; set; } + + // Passthru + protected bool Has_Passthru {get;set;} // => Parameters.HasParameter("Passthru"); + protected bool Passthru {get;set;} // => _Passthru; // Parameters.Get("Passthru"); + + // Collection + protected bool Has_Collection => Parameters.HasParameter("Collection"); + protected Models.Connection Collection => Data.GetCollection(); + + // Server + protected bool Has_Server => Parameters.HasParameter("Server"); + protected Models.Connection Server => Data.GetServer(); + + // ParameterSetName + protected bool Has_ParameterSetName {get;set;} + protected string ParameterSetName {get;set;} + + + protected override void CacheParameters() + { + // Field + Has_Field = Parameters.HasParameter("Field"); + Field = Parameters.Get("Field"); + + // ReferenceName + Has_ReferenceName = Parameters.HasParameter("ReferenceName"); + ReferenceName = Parameters.Get("ReferenceName"); + + // Description + Has_Description = Parameters.HasParameter("Description"); + Description = Parameters.Get("Description"); + + // Type + Has_Type = Parameters.HasParameter("Type"); + Type = Parameters.Get("Type", FieldType.String); + """; + + await new VerifyCS.Test + { + TestState = + { + Sources = { code }, + GeneratedSources = + { + (typeof(ControllerGenerator), "NewWorkItemFieldController.cs", SourceText.From(generated, Encoding.UTF8, SourceHashAlgorithm.Sha256)), + }, + }, + }.RunAsync(); + } + } +} diff --git a/CSharp/TfsCmdlets.SourceGeneratores.UnitTests/TfsCmdlets.SourceGenerators.UnitTests.csproj b/CSharp/TfsCmdlets.SourceGeneratores.UnitTests/TfsCmdlets.SourceGenerators.UnitTests.csproj new file mode 100644 index 00000000..be5c2165 --- /dev/null +++ b/CSharp/TfsCmdlets.SourceGeneratores.UnitTests/TfsCmdlets.SourceGenerators.UnitTests.csproj @@ -0,0 +1,35 @@ + + + + net8.0 + enable + enable + false + true + + + + allruntime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + From d4cb82b25077fd96bd7ce1a4f6cecf1586dac5a9 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 26 Jul 2024 09:59:57 -0300 Subject: [PATCH 09/90] Update generator to bring usings to controller code --- .../TfsCmdlets.SourceGenerators/Extensions.cs | 47 +++++++++++++++---- .../Generators/Cmdlets/CmdletInfo.cs | 12 ++++- .../Generators/Controllers/ControllerInfo.cs | 14 ++++-- .../Generators/Controllers/TypeProcessor.cs | 4 +- .../Properties/launchSettings.json | 6 +-- .../TfsCmdlets.SourceGenerators.csproj | 1 + 6 files changed, 66 insertions(+), 18 deletions(-) diff --git a/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs b/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs index 0c9c604c..39982907 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs @@ -36,18 +36,23 @@ public static T GetAttributeNamedValue(this INamedTypeSymbol symbol, string a return (T)(arg.Value.Value ?? default(T)); } - public static bool GetAttributeNamedValue(INamedTypeSymbol symbol, string attributeName, string argumentName, bool defaultValue = false) - { - var attr = symbol - .GetAttributes() - .FirstOrDefault(a => a.AttributeClass.Name.Equals(attributeName)); + // public static bool GetAttributeNamedValue(INamedTypeSymbol symbol, string attributeName, string argumentName, bool defaultValue = false) + // { + // var attr = symbol + // .GetAttributes() + // .FirstOrDefault(a => a.AttributeClass.Name.Equals(attributeName)); - if (attr == null) return defaultValue; + // if (attr == null) return defaultValue; - var arg = attr.NamedArguments.FirstOrDefault(a => a.Key.Equals(argumentName)); + // var arg = attr.NamedArguments.FirstOrDefault(a => a.Key.Equals(argumentName)); - return (arg.Value.Value?.ToString() ?? string.Empty).Equals("True", StringComparison.OrdinalIgnoreCase); - } + // return (arg.Value.Value?.ToString() ?? string.Empty).Equals("True", StringComparison.OrdinalIgnoreCase); + // } + + public static bool HasAttributeNamedValue(this INamedTypeSymbol symbol, string attributeName, string argumentName) + => symbol.GetAttributes() + .First(a => a.AttributeClass.Name.Equals(attributeName))? + .NamedArguments.Any(a => a.Key.Equals(argumentName)) ?? false; public static bool HasAttribute(this ISymbol symbol, string attributeName) => symbol.GetAttributes().Any(a => a.AttributeClass.Name.Equals(attributeName)); @@ -152,5 +157,29 @@ public static IEnumerable WritableScalarProperties(this INamedT public static bool IsPartial(this ClassDeclarationSyntax cds) => cds.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)); + public static T GetDeclaringSyntax(this ISymbol symbol) where T : SyntaxNode + { + var syntaxRefs = symbol.DeclaringSyntaxReferences; + if (syntaxRefs.Length == 0) return null; + return syntaxRefs[0].GetSyntax() as T; + } + + public static T FindParentOfType(this SyntaxNode syntaxNode) where T : SyntaxNode + { + var parent = syntaxNode.Parent; + + while(parent != null && !(parent is T)) + { + parent = parent.Parent; + } + + return parent as T; + } + + + public static IEnumerable GetDeclaringSyntaxes(this ISymbol symbol) where T : SyntaxNode + { + return symbol.DeclaringSyntaxReferences.Select(sr => sr.GetSyntax()).OfType(); + } } } diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/Cmdlets/CmdletInfo.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/Cmdlets/CmdletInfo.cs index c5e69083..1112b060 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/Cmdlets/CmdletInfo.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/Cmdlets/CmdletInfo.cs @@ -43,18 +43,28 @@ public CmdletInfo(INamedTypeSymbol cmdlet, Logger logger) DefaultParameterSetName = cmdlet.GetAttributeNamedValue("CmdletAttribute", "DefaultParameterSetName"); OutputType = cmdlet.GetAttributeNamedValue("TfsCmdletAttribute", "OutputType"); DataType = cmdlet.GetAttributeNamedValue("TfsCmdletAttribute", "DataType") ?? OutputType; - SupportsShouldProcess = cmdlet.GetAttributeNamedValue("TfsCmdletAttribute", "SupportsShouldProcess"); DefaultParameterSetName = cmdlet.GetAttributeNamedValue("TfsCmdletAttribute", "DefaultParameterSetName"); CustomControllerName = cmdlet.GetAttributeNamedValue("TfsCmdletAttribute", "CustomControllerName"); ReturnsValue = cmdlet.GetAttributeNamedValue("TfsCmdletAttribute", "ReturnsValue"); SkipGetProperty = cmdlet.GetAttributeNamedValue("TfsCmdletAttribute", "SkipGetProperty"); AdditionalCredentialParameterSets = cmdlet.GetAttributeNamedValue("TfsCmdletAttribute", "AdditionalCredentialParameterSets"); + SupportsShouldProcess = SetSupportsShouldProcess(cmdlet); CmdletAttribute = GenerateCmdletAttribute(this); OutputTypeAttribute = GenerateOutputTypeAttribute(this); GenerateProperties(); } + private bool SetSupportsShouldProcess(INamedTypeSymbol cmdlet) + { + if(cmdlet.HasAttributeNamedValue("TfsCmdletAttribute", "SupportsShouldProcess")) + { + return cmdlet.GetAttributeNamedValue("TfsCmdletAttribute", "SupportsShouldProcess"); + } + + return (Verb != "Get") && (Verb != "Test") && (Verb != "Search") && (Verb != "Connect") && (Verb != "Disconnect"); + } + private void GenerateProperties() { foreach (var (condition, generator, generatorName) in _generators) diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs index a593f97e..9f50e0e3 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; using TfsCmdlets.SourceGenerators.Generators.Cmdlets; namespace TfsCmdlets.SourceGenerators.Generators.Controllers @@ -19,6 +20,7 @@ public class ControllerInfo : GeneratorState public string CtorArgs { get; } public string BaseCtorArgs { get; } public string ImportingConstructorBody { get; } + public object Usings { get; } public IDictionary DeclaredProperties { get; } public IDictionary ImplicitProperties { get; } public string BaseClassName => BaseClass.Name; @@ -49,6 +51,7 @@ internal ControllerInfo(INamedTypeSymbol controller, GeneratorExecutionContext c CtorArgs = controller.GetImportingConstructorArguments(BaseClass); BaseCtorArgs = BaseClass.GetConstructorArguments(); ImportingConstructorBody = GetImportingConstructorBody(controller); + Usings = GetUsingStatements(Cmdlet); CmdletInfo = new CmdletInfo(Cmdlet, Logger); DeclaredProperties = Cmdlet.GetPropertiesWithAttribute("ParameterAttribute").Select(p => new GeneratedProperty(p, string.Empty)).ToDictionary(p => p.Name); @@ -81,6 +84,9 @@ private static string GetImportingConstructorBody(INamedTypeSymbol type) => string.Join("\n", type.GetPropertiesWithAttribute("ImportAttribute") .Select(p => $" {p.Name} = {p.Name[0].ToString().ToLower()}{p.Name.Substring(1)};")); + private string GetUsingStatements(INamedTypeSymbol cmdlet) + => cmdlet.GetDeclaringSyntax().FindParentOfType()?.Usings.ToString(); + private static IEnumerable GenerateParameterSetProperty(ControllerInfo controller) { yield return new GeneratedProperty("ParameterSetName", "string", $@" // ParameterSetName @@ -123,12 +129,12 @@ private static IEnumerable GenerateGetInputProperty(Controlle var initializer = string.IsNullOrEmpty(prop.DefaultValue) ? string.Empty : $", {prop.DefaultValue}"; yield return new GeneratedProperty(prop.Name, "IEnumerable", $@" // {prop.Name} - protected bool Has_{prop.Name} => Parameters.HasParameter(""{prop.Name}""); + protected bool Has_{prop.Name} => Parameters.HasParameter(nameof({prop.Name})); protected IEnumerable {prop.Name} {{ get {{ - var paramValue = Parameters.Get(""{prop.Name}""{initializer}); + var paramValue = Parameters.Get(nameof({prop.Name}){initializer}); if(paramValue is ICollection col) return col; return new[] {{ paramValue }}; }} @@ -162,8 +168,8 @@ private static IEnumerable GenerateImplicitParameters(Control if (prop.IsHidden) continue; yield return new GeneratedProperty(prop.Name, type, $@" // {prop.Name} - protected bool Has_{prop.Name} {{get;set;}} // => Parameters.HasParameter(""{prop.Name}""); - protected {type} {prop.Name} {{get;set;}} // => _{prop.Name}; // Parameters.Get<{type}>(""{prop.Name}""); + protected bool Has_{prop.Name} {{get;set;}} // => Parameters.HasParameter(nameof({prop.Name})); + protected {type} {prop.Name} {{get;set;}} // => _{prop.Name}; // Parameters.Get<{type}>(nameof({prop.Name})); "); } diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs index 1d8bc685..ca1b1213 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs @@ -45,7 +45,9 @@ public override string GenerateCode() cacheProps.Append($" {prop.Name} = Parameters.Get<{type}>(\"{prop.Name}\"{initializer});\n\n"); } - return $@"namespace {controller.Namespace} + return $@"{controller.Usings} + +namespace {controller.Namespace} {{ internal partial class {controller.Name}: {controller.BaseClassName} {{ diff --git a/CSharp/TfsCmdlets.SourceGenerators/Properties/launchSettings.json b/CSharp/TfsCmdlets.SourceGenerators/Properties/launchSettings.json index b5eed525..5ba02617 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Properties/launchSettings.json +++ b/CSharp/TfsCmdlets.SourceGenerators/Properties/launchSettings.json @@ -1,8 +1,8 @@ { "profiles": { - "Profile 1": { - "commandName": "Executable", - "executablePath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Preview\\Common7\\IDE\\devenv.exe" + "SourceGenerator": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\TfsCmdlets\\TfsCmdlets.csproj" } } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj b/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj index 685b024f..28b0146a 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj +++ b/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj @@ -2,6 +2,7 @@ netstandard2.0 + true From 81eaa5cf125a2e272a12c19ad478817bef186f88 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 26 Jul 2024 10:00:17 -0300 Subject: [PATCH 10/90] Update solution --- CSharp/TfsCmdlets.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CSharp/TfsCmdlets.sln b/CSharp/TfsCmdlets.sln index 99e41947..1ddf2fda 100644 --- a/CSharp/TfsCmdlets.sln +++ b/CSharp/TfsCmdlets.sln @@ -13,7 +13,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets.SourceGenerators EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets.Common", "TfsCmdlets.Common\TfsCmdlets.Common.csproj", "{CD6FA384-22B2-42E8-8D4B-69BBF66DC792}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TfsCmdlets.SourceGeneratores.UnitTests", "TfsCmdlets.SourceGeneratores.UnitTests\TfsCmdlets.SourceGeneratores.UnitTests.csproj", "{76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TfsCmdlets.SourceGenerators.UnitTests", "TfsCmdlets.SourceGenerators.UnitTests\TfsCmdlets.SourceGenerators.UnitTests.csproj", "{76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 4c2deff59837aaf2617c7b94260d240c38139407 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 26 Jul 2024 10:00:30 -0300 Subject: [PATCH 11/90] Add view definition --- PS/_Formats/Views/WorkItemFields.View.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 PS/_Formats/Views/WorkItemFields.View.yml diff --git a/PS/_Formats/Views/WorkItemFields.View.yml b/PS/_Formats/Views/WorkItemFields.View.yml new file mode 100644 index 00000000..2182be87 --- /dev/null +++ b/PS/_Formats/Views/WorkItemFields.View.yml @@ -0,0 +1,7 @@ +ViewSelectedBy: + - Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemField +TableControl: + Name: + ReferenceName: + Type: + Description: \ No newline at end of file From 39f0589d8f482214429609a4bc98cec6406d25cd Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 1 Aug 2024 18:30:53 -0300 Subject: [PATCH 12/90] Add HttpClient sourcegen support --- .../Analyzers/ClassMustBePartial.cs | 6 +- .../TfsCmdlets.SourceGenerators/BaseFilter.cs | 10 +- .../BaseGenerator.cs | 4 +- .../BaseTypeProcessor.cs | 4 +- .../TfsCmdlets.SourceGenerators/Extensions.cs | 34 ++++++- .../GeneratedMethod.cs | 61 ++++++++++++ .../GeneratorState.cs | 21 +++-- .../Generators/Controllers/Analyzers.cs | 2 +- .../Generators/Controllers/ControllerInfo.cs | 2 +- .../Generators/HttpClients/Filter.cs | 9 ++ .../Generators/HttpClients/Generator.cs | 10 ++ .../Generators/HttpClients/HttpClientInfo.cs | 36 +++++++ .../Generators/HttpClients/TypeProcessor.cs | 94 +++++++++++++++++++ CSharp/TfsCmdlets.SourceGenerators/IFilter.cs | 2 +- .../ITypeProcessor.cs | 4 +- CSharp/TfsCmdlets.SourceGenerators/Logger.cs | 2 +- .../TfsCmdlets.SourceGenerators.csproj | 1 + .../HttpClients/HttpClientAttribute.cs | 12 +++ .../TfsCmdlets/HttpClients/IVssHttpClient.cs | 10 ++ .../IWorkItemTrackingHttpClient.cs | 10 ++ CSharp/TfsCmdlets/TfsCmdlets.csproj | 2 +- 21 files changed, 307 insertions(+), 29 deletions(-) create mode 100644 CSharp/TfsCmdlets.SourceGenerators/GeneratedMethod.cs create mode 100644 CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/Filter.cs create mode 100644 CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/Generator.cs create mode 100644 CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/HttpClientInfo.cs create mode 100644 CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/TypeProcessor.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/HttpClientAttribute.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IVssHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IWorkItemTrackingHttpClient.cs diff --git a/CSharp/TfsCmdlets.SourceGenerators/Analyzers/ClassMustBePartial.cs b/CSharp/TfsCmdlets.SourceGenerators/Analyzers/ClassMustBePartial.cs index b006960d..21dced41 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Analyzers/ClassMustBePartial.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Analyzers/ClassMustBePartial.cs @@ -33,7 +33,7 @@ private static void AnalyzeNamedType(SymbolAnalysisContext context) foreach (var declaringSyntaxReference in type.DeclaringSyntaxReferences) { - if (!(declaringSyntaxReference.GetSyntax() is ClassDeclarationSyntax cds)) continue; + if (!(declaringSyntaxReference.GetSyntax() is TypeDeclarationSyntax cds)) continue; if (cds.IsPartial()) continue; if (!filters.Any(filter => filter.ShouldProcessType(type))) continue; @@ -105,7 +105,7 @@ private static IEnumerable GetFilters() // return newDoc; // } - // private static ClassDeclarationSyntax FindClassDeclaration( + // private static TypeDeclarationSyntax FindClassDeclaration( // Diagnostic makePartial, // SyntaxNode root) // { @@ -113,7 +113,7 @@ private static IEnumerable GetFilters() // return root.FindToken(diagnosticSpan.Start) // .Parent?.AncestorsAndSelf() - // .OfType() + // .OfType() // .First(); // } //} diff --git a/CSharp/TfsCmdlets.SourceGenerators/BaseFilter.cs b/CSharp/TfsCmdlets.SourceGenerators/BaseFilter.cs index d6f4e140..1f4ec8fd 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/BaseFilter.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/BaseFilter.cs @@ -7,7 +7,7 @@ namespace TfsCmdlets.SourceGenerators { public abstract class BaseFilter : IFilter { - public IDictionary TypesToProcess { get; } = new Dictionary(); + public IDictionary TypesToProcess { get; } = new Dictionary(); public abstract bool ShouldProcessType(INamedTypeSymbol type); @@ -20,15 +20,15 @@ public void Initialize(Logger logger) public void OnVisitSyntaxNode(GeneratorSyntaxContext context) { - if (!(context.Node is ClassDeclarationSyntax cds)) return; + if (!(context.Node is TypeDeclarationSyntax tds)) return; try { - var type = (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(cds); + var type = (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(tds); if (type == null) { - Logger.LogError(new Exception($"Unexpected error: Type {cds.Identifier} not found.")); + Logger.LogError(new Exception($"Unexpected error: Type {tds.Identifier} not found.")); return; } @@ -36,7 +36,7 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context) //Logger.Log($"Found '{type.FullName()}'"); - TypesToProcess[type.FullName()] = (type, cds); + TypesToProcess[type.FullName()] = (type, tds); } catch (Exception ex) { diff --git a/CSharp/TfsCmdlets.SourceGenerators/BaseGenerator.cs b/CSharp/TfsCmdlets.SourceGenerators/BaseGenerator.cs index 841a0863..e2b7aae7 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/BaseGenerator.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/BaseGenerator.cs @@ -37,8 +37,8 @@ public void Execute(GeneratorExecutionContext context) try { - var typesToProcess = new List<(INamedTypeSymbol, ClassDeclarationSyntax)>( - receiver.TypesToProcess?.Values ?? Enumerable.Empty<(INamedTypeSymbol, ClassDeclarationSyntax)>()); + var typesToProcess = new List<(INamedTypeSymbol, TypeDeclarationSyntax)>( + receiver.TypesToProcess?.Values ?? Enumerable.Empty<(INamedTypeSymbol, TypeDeclarationSyntax)>()); Logger.Log($"Preparing to generate code for {typesToProcess.Count} types"); diff --git a/CSharp/TfsCmdlets.SourceGenerators/BaseTypeProcessor.cs b/CSharp/TfsCmdlets.SourceGenerators/BaseTypeProcessor.cs index 5f90628c..64639588 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/BaseTypeProcessor.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/BaseTypeProcessor.cs @@ -9,7 +9,7 @@ public abstract class BaseTypeProcessor : ITypeProcessor { public INamedTypeSymbol Type { get; set; } - public ClassDeclarationSyntax ClassDeclaration { get; set; } + public TypeDeclarationSyntax ClassDeclaration { get; set; } public GeneratorExecutionContext Context { get; set; } @@ -27,7 +27,7 @@ public abstract class BaseTypeProcessor : ITypeProcessor protected Logger Logger { get; private set; } - public void Initialize(Logger logger, INamedTypeSymbol type, ClassDeclarationSyntax cds, GeneratorExecutionContext context) + public void Initialize(Logger logger, INamedTypeSymbol type, TypeDeclarationSyntax cds, GeneratorExecutionContext context) { Type = type; ClassDeclaration = cds; diff --git a/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs b/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs index 39982907..81c925d6 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -36,6 +37,9 @@ public static T GetAttributeNamedValue(this INamedTypeSymbol symbol, string a return (T)(arg.Value.Value ?? default(T)); } + public static string GetUsingStatements(this INamedTypeSymbol symbol) + => symbol.GetDeclaringSyntax().FindParentOfType()?.Usings.ToString(); + // public static bool GetAttributeNamedValue(INamedTypeSymbol symbol, string attributeName, string argumentName, bool defaultValue = false) // { // var attr = symbol @@ -96,6 +100,15 @@ public static IEnumerable GetPropertiesWithAttribute(this IName .Where(p => p.GetAttributes().Any( a => a.AttributeClass.Name.Equals(attributeName))); + public static string FullName(this ITypeSymbol symbol) + { + if (symbol == null) return null; + + if(symbol.Name.Equals("Void")) return "void"; + + return symbol.ToDisplayString(); + } + public static string FullName(this INamedTypeSymbol symbol) { if (symbol == null) @@ -154,7 +167,7 @@ public static IEnumerable WritableScalarProperties(this INamedT public static bool HasParameters(this IPropertySymbol symbol) => symbol.Parameters.Any(); - public static bool IsPartial(this ClassDeclarationSyntax cds) + public static bool IsPartial(this TypeDeclarationSyntax cds) => cds.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)); public static T GetDeclaringSyntax(this ISymbol symbol) where T : SyntaxNode @@ -176,10 +189,27 @@ public static T FindParentOfType(this SyntaxNode syntaxNode) where T : Syntax return parent as T; } - public static IEnumerable GetDeclaringSyntaxes(this ISymbol symbol) where T : SyntaxNode { return symbol.DeclaringSyntaxReferences.Select(sr => sr.GetSyntax()).OfType(); } + + public static IEnumerable GetMembersRecursively(this ITypeSymbol type, SymbolKind kind, string stopAt) + { + while (type != null) + { + foreach (var member in type.GetMembers().Where(m => m.Kind == kind)) + { + yield return member; + } + + if ((type.FullName().Equals(stopAt)) || (type.FullName().Equals("System.Object"))) + { + yield break; + } + + type = type.BaseType; + } + } } } diff --git a/CSharp/TfsCmdlets.SourceGenerators/GeneratedMethod.cs b/CSharp/TfsCmdlets.SourceGenerators/GeneratedMethod.cs new file mode 100644 index 00000000..01871cbd --- /dev/null +++ b/CSharp/TfsCmdlets.SourceGenerators/GeneratedMethod.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; + +namespace TfsCmdlets.SourceGenerators +{ + internal record GeneratedMethod + { + public GeneratedMethod(IMethodSymbol method) + { + var parms1 = new List(); + var parms2 = new List(); + + Name = method.Name; + ReturnType = method.ReturnType; + + foreach (var p in method.Parameters) + { + var defaultValue = p.HasExplicitDefaultValue ? $" = {p.ExplicitDefaultValue?? (p.Type.IsValueType? $"default({p.Type.ToDisplayString()})" : "null")}" : string.Empty; + parms1.Add($"{p.Type.FullName()} {p.Name}{defaultValue}"); + parms2.Add(p.Name); + } + + Signature = $"({string.Join(", ", parms1)})"; + SignatureNamesOnly = $"({string.Join(", ", parms2)})"; + } + + public string SignatureNamesOnly { get; set; } + + public string Name { get; } + + public string Signature { get; } + + public ITypeSymbol ReturnType { get; } + + public override string ToString() + { + return ToString(null); + } + + public string ToString(string body) + { + var sb = new StringBuilder(); + sb.Append($"public {ReturnType.FullName()} {Name}{Signature}"); + + if (string.IsNullOrWhiteSpace(body)) return sb.ToString(); + + var isMethodBody = body.TrimStart().StartsWith("=>"); + + sb.AppendLine(); + + if(!isMethodBody) sb.AppendLine("{"); + sb.AppendLine(body); + if (!isMethodBody) sb.AppendLine("}"); + + return sb.ToString(); + } + }; +} diff --git a/CSharp/TfsCmdlets.SourceGenerators/GeneratorState.cs b/CSharp/TfsCmdlets.SourceGenerators/GeneratorState.cs index 7f356c99..3f722c34 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/GeneratorState.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/GeneratorState.cs @@ -9,25 +9,30 @@ public class GeneratorState { private Dictionary PropertyBag { get; } = new Dictionary(); - public GeneratorState(INamedTypeSymbol type, Logger logger) + public GeneratorState(INamedTypeSymbol targetType, Logger logger) { - Name = type.Name; - Namespace = type.FullNamespace(); - FullName = FileName = type.FullName(); + if (targetType == null) throw new ArgumentNullException(nameof(targetType)); + + Name = targetType.Name; + Namespace = targetType.FullNamespace(); + FullName = FileName = targetType.FullName(); Logger = logger; + TargetType = targetType; } - public string Name { get; } + public INamedTypeSymbol TargetType { get; set; } + + public string Name { get; } - public string Namespace{ get; } + public string Namespace { get; } public string FullName { get; } public string FileName { get; } - + public IDictionary GeneratedProperties = new Dictionary(); - protected Logger Logger { get; } + protected Logger Logger { get; } public object this[string key] { diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/Analyzers.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/Analyzers.cs index 3794ea0f..9f8631c7 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/Analyzers.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/Analyzers.cs @@ -31,7 +31,7 @@ private static void AnalyzeNamedType(SymbolAnalysisContext context) foreach (var declaringSyntaxReference in type.DeclaringSyntaxReferences) { - if (!(declaringSyntaxReference.GetSyntax() is ClassDeclarationSyntax cds)) continue; + if (!(declaringSyntaxReference.GetSyntax() is TypeDeclarationSyntax cds)) continue; if (cds.IsPartial()) continue; if (!filters.Any(filter => filter.ShouldProcessType(type) && !type.Name.EndsWith("Controller"))) continue; diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs index 9f50e0e3..dc3a8b88 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs @@ -85,7 +85,7 @@ private static string GetImportingConstructorBody(INamedTypeSymbol type) .Select(p => $" {p.Name} = {p.Name[0].ToString().ToLower()}{p.Name.Substring(1)};")); private string GetUsingStatements(INamedTypeSymbol cmdlet) - => cmdlet.GetDeclaringSyntax().FindParentOfType()?.Usings.ToString(); + => cmdlet.GetDeclaringSyntax().FindParentOfType()?.Usings.ToString(); private static IEnumerable GenerateParameterSetProperty(ControllerInfo controller) { diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/Filter.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/Filter.cs new file mode 100644 index 00000000..90fd5381 --- /dev/null +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/Filter.cs @@ -0,0 +1,9 @@ +using Microsoft.CodeAnalysis; + +namespace TfsCmdlets.SourceGenerators.Generators.HttpClients +{ + public class Filter : BaseFilter + { + public override bool ShouldProcessType(INamedTypeSymbol type) => type.HasAttribute("HttpClientAttribute"); + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/Generator.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/Generator.cs new file mode 100644 index 00000000..5ddb5f69 --- /dev/null +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/Generator.cs @@ -0,0 +1,10 @@ +using Microsoft.CodeAnalysis; + +namespace TfsCmdlets.SourceGenerators.Generators.HttpClients +{ + [Generator] + public class HttpClientGenerator : BaseGenerator + { + protected override string GeneratorName => nameof(HttpClientGenerator); + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/HttpClientInfo.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/HttpClientInfo.cs new file mode 100644 index 00000000..cbec53de --- /dev/null +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/HttpClientInfo.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace TfsCmdlets.SourceGenerators.Generators.HttpClients +{ + public class HttpClientInfo : GeneratorState + { + internal HttpClientInfo(INamedTypeSymbol symbol, GeneratorExecutionContext context, Logger logger) + : base(symbol, logger) + { + OriginalType = symbol.GetAttributeConstructorValue("HttpClientAttribute"); + } + + public INamedTypeSymbol OriginalType { get; } + + internal IEnumerable GenerateMethods() + { + var methods = OriginalType + .GetMembersRecursively(SymbolKind.Method, "Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase") + .Cast() + .Where(m => + m.MethodKind == MethodKind.Ordinary && + m.DeclaredAccessibility == Accessibility.Public && + !m.HasAttribute("ObsoleteAttribute")) + .ToList(); + + foreach (var method in methods) + { + yield return new GeneratedMethod(method); + } + } + } +} diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/TypeProcessor.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/TypeProcessor.cs new file mode 100644 index 00000000..b1bcfad1 --- /dev/null +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/TypeProcessor.cs @@ -0,0 +1,94 @@ +using System; +using System.Text; + +namespace TfsCmdlets.SourceGenerators.Generators.HttpClients +{ + public class TypeProcessor : BaseTypeProcessor + { + private HttpClientInfo _client; + + protected override void OnInitialize() + { + try + { + _client = new HttpClientInfo(Type, Context, Logger); + } + catch (Exception ex) + { + Logger.LogError(ex, $"Error initializing HttpClientInfo for {Type.FullName()}"); + Ignore = true; + } + } + + protected string GetInterfaceBody() + { + var sb = new StringBuilder(); + + foreach (var method in _client.GenerateMethods()) + { + sb.Append($"\t\t{method}"); + sb.AppendLine(";"); + } + + return sb.ToString(); + } + + private string GetClassBody() + { + var sb = new StringBuilder(); + + foreach (var method in _client.GenerateMethods()) + { + sb.Append($"\t\t{method.ToString($"\t\t\t=> Client.{method.Name}{method.SignatureNamesOnly};")}"); + } + + return sb.ToString(); + } + + + public override string GenerateCode() + { + var client = _client; + + return $$""" + using System.Composition; + + namespace {{client.Namespace}} + { + public partial interface {{client.Name}}: IVssHttpClient + { + {{GetInterfaceBody()}} + } + + [Export(typeof({{client.Name}}))] + internal class {{client.Name}}Impl: {{client.Name}} + { + private {{client.OriginalType}} _client; + + protected IDataManager Data { get; } + + [ImportingConstructor] + public {{client.Name}}Impl(IDataManager data) + { + Data = data; + } + + private {{client.OriginalType}} Client + { + get + { + if(_client == null) + { + _client = (Data.GetCollection() as TfsCmdlets.Services.ITfsServiceProvider)?.GetClient(typeof({{client.OriginalType}})) as {{client.OriginalType}}; + } + return _client; + } + } + + {{GetClassBody()}} + } + } + """; + } + } +} diff --git a/CSharp/TfsCmdlets.SourceGenerators/IFilter.cs b/CSharp/TfsCmdlets.SourceGenerators/IFilter.cs index b7dde64b..281651ac 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/IFilter.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/IFilter.cs @@ -10,7 +10,7 @@ public interface IFilter: ISyntaxContextReceiver { void Initialize(Logger logger); - IDictionary TypesToProcess { get; } + IDictionary TypesToProcess { get; } bool ShouldProcessType(INamedTypeSymbol type); } diff --git a/CSharp/TfsCmdlets.SourceGenerators/ITypeProcessor.cs b/CSharp/TfsCmdlets.SourceGenerators/ITypeProcessor.cs index 2e73f5fa..3ec05b96 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/ITypeProcessor.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/ITypeProcessor.cs @@ -8,9 +8,9 @@ namespace TfsCmdlets.SourceGenerators { public interface ITypeProcessor { - void Initialize(Logger Logger, INamedTypeSymbol type, ClassDeclarationSyntax cds, GeneratorExecutionContext context); + void Initialize(Logger Logger, INamedTypeSymbol type, TypeDeclarationSyntax cds, GeneratorExecutionContext context); INamedTypeSymbol Type { get; } - ClassDeclarationSyntax ClassDeclaration { get; } + TypeDeclarationSyntax ClassDeclaration { get; } GeneratorExecutionContext Context { get; } string Name { get; } string FullName { get; } diff --git a/CSharp/TfsCmdlets.SourceGenerators/Logger.cs b/CSharp/TfsCmdlets.SourceGenerators/Logger.cs index 48fa441e..6f3e6086 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Logger.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Logger.cs @@ -25,7 +25,7 @@ public Logger(string generatorName) [Conditional("DEBUG")] internal void LogError(Exception ex, string msg) => Log($"[ERROR] {msg} ({ex})"); - internal void ReportDiagnostic_ClassMustBePartial(GeneratorExecutionContext context, ClassDeclarationSyntax cds) + internal void ReportDiagnostic_ClassMustBePartial(GeneratorExecutionContext context, TypeDeclarationSyntax cds) => context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ClassMustBePartial, cds.Identifier.GetLocation(), cds.Identifier)); diff --git a/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj b/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj index 28b0146a..31e76d6a 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj +++ b/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj @@ -3,6 +3,7 @@ netstandard2.0 true + 11.0 diff --git a/CSharp/TfsCmdlets/HttpClients/HttpClientAttribute.cs b/CSharp/TfsCmdlets/HttpClients/HttpClientAttribute.cs new file mode 100644 index 00000000..62f31e6f --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/HttpClientAttribute.cs @@ -0,0 +1,12 @@ +namespace TfsCmdlets.HttpClients +{ + internal class HttpClientAttribute : Attribute + { + public HttpClientAttribute(Type type) + { + Type = type; + } + + public Type Type { get; } + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IVssHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IVssHttpClient.cs new file mode 100644 index 00000000..cb4a70b2 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IVssHttpClient.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TfsCmdlets.HttpClients +{ + public interface IVssHttpClient + { + } +} diff --git a/CSharp/TfsCmdlets/HttpClients/IWorkItemTrackingHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IWorkItemTrackingHttpClient.cs new file mode 100644 index 00000000..b8d3ea62 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IWorkItemTrackingHttpClient.cs @@ -0,0 +1,10 @@ +using Microsoft.TeamFoundation.WorkItemTracking.WebApi; + +namespace TfsCmdlets.HttpClients +{ + + [HttpClient(typeof(WorkItemTrackingHttpClient))] + partial interface IWorkItemTrackingHttpClient { + + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/TfsCmdlets.csproj b/CSharp/TfsCmdlets/TfsCmdlets.csproj index 4b08b518..87819e31 100644 --- a/CSharp/TfsCmdlets/TfsCmdlets.csproj +++ b/CSharp/TfsCmdlets/TfsCmdlets.csproj @@ -4,7 +4,7 @@ netcoreapp3.1;net471 TfsCmdlets true - 8.0 + 11.0 $(TargetPath)\TfsCmdlets.xml 1591 preview From dee3df2cfc196b4c6117cb82d791f84cad0e5904 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 1 Aug 2024 18:31:13 -0300 Subject: [PATCH 13/90] Convert to new HttpClient model --- .../Cmdlets/WorkItem/Field/GetWorkItemField.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs index affc22af..5a9bbf8b 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs @@ -1,5 +1,6 @@ using Microsoft.TeamFoundation.WorkItemTracking.WebApi; using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; +using TfsCmdlets.HttpClients; namespace TfsCmdlets.Cmdlets.WorkItem.Field { @@ -42,10 +43,11 @@ partial class GetWorkItemField [CmdletController] partial class GetWorkItemFieldController { + [Import] + private IWorkItemTrackingHttpClient Client { get; set; } + protected override IEnumerable Run() { - var client = Data.GetClient(); - string tpName; if(Has_Project) { @@ -63,14 +65,14 @@ protected override IEnumerable Run() (IncludeExtensionFields ? GetFieldsExpand.ExtensionFields : GetFieldsExpand.None) | (IncludeDeleted ? GetFieldsExpand.IncludeDeleted : GetFieldsExpand.None); - yield return client.GetFieldsAsync(expand) + yield return Client.GetFieldsAsync(expand) .GetResult($"Error getting field '{s}'") .Where(field => field.Name.IsLike(s) || field.ReferenceName.IsLike(s)); break; } - case string s: + case string s when !string.IsNullOrEmpty(s): { - yield return client.GetFieldAsync(fieldNameOrRefName: s) + yield return Client.GetFieldAsync(fieldNameOrRefName: s) .GetResult($"Error getting field '{s}'"); break; } From 85dbf2e56c861dd4dd075be4ae89dcab19f99bf4 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 1 Aug 2024 18:40:14 -0300 Subject: [PATCH 14/90] Move files and rename namespace --- .../{HttpClient => HttpClients}/GenericHttpClient.cs | 2 +- .../{HttpClient => HttpClients}/GitExtendedHttpClient.cs | 2 +- .../HttpClients/HttpClientAttribute.cs | 0 .../HttpClients/IVssHttpClient.cs | 0 .../{HttpClient => HttpClients}/TeamAdminHttpClient.cs | 2 +- CSharp/TfsCmdlets.Common/Services/Impl/RestApiServiceImpl.cs | 2 +- 6 files changed, 4 insertions(+), 4 deletions(-) rename CSharp/TfsCmdlets.Common/{HttpClient => HttpClients}/GenericHttpClient.cs (99%) rename CSharp/TfsCmdlets.Common/{HttpClient => HttpClients}/GitExtendedHttpClient.cs (98%) rename CSharp/{TfsCmdlets => TfsCmdlets.Common}/HttpClients/HttpClientAttribute.cs (100%) rename CSharp/{TfsCmdlets => TfsCmdlets.Common}/HttpClients/IVssHttpClient.cs (100%) rename CSharp/TfsCmdlets.Common/{HttpClient => HttpClients}/TeamAdminHttpClient.cs (99%) diff --git a/CSharp/TfsCmdlets.Common/HttpClient/GenericHttpClient.cs b/CSharp/TfsCmdlets.Common/HttpClients/GenericHttpClient.cs similarity index 99% rename from CSharp/TfsCmdlets.Common/HttpClient/GenericHttpClient.cs rename to CSharp/TfsCmdlets.Common/HttpClients/GenericHttpClient.cs index 1b98f5e5..0e19136b 100644 --- a/CSharp/TfsCmdlets.Common/HttpClient/GenericHttpClient.cs +++ b/CSharp/TfsCmdlets.Common/HttpClients/GenericHttpClient.cs @@ -4,7 +4,7 @@ using Microsoft.VisualStudio.Services.Common; using Microsoft.VisualStudio.Services.WebApi; -namespace TfsCmdlets.HttpClient +namespace TfsCmdlets.HttpClients { /// /// Generic HTTP Client, used by the Invoke-TfsRestApi cmdlet diff --git a/CSharp/TfsCmdlets.Common/HttpClient/GitExtendedHttpClient.cs b/CSharp/TfsCmdlets.Common/HttpClients/GitExtendedHttpClient.cs similarity index 98% rename from CSharp/TfsCmdlets.Common/HttpClient/GitExtendedHttpClient.cs rename to CSharp/TfsCmdlets.Common/HttpClients/GitExtendedHttpClient.cs index 9486fa37..b8e7c016 100644 --- a/CSharp/TfsCmdlets.Common/HttpClient/GitExtendedHttpClient.cs +++ b/CSharp/TfsCmdlets.Common/HttpClients/GitExtendedHttpClient.cs @@ -1,7 +1,7 @@ using System.Net.Http; using Microsoft.VisualStudio.Services.Common; -namespace TfsCmdlets.HttpClient +namespace TfsCmdlets.HttpClients { /// /// Custom HTTP Client to handle extended Git repository management diff --git a/CSharp/TfsCmdlets/HttpClients/HttpClientAttribute.cs b/CSharp/TfsCmdlets.Common/HttpClients/HttpClientAttribute.cs similarity index 100% rename from CSharp/TfsCmdlets/HttpClients/HttpClientAttribute.cs rename to CSharp/TfsCmdlets.Common/HttpClients/HttpClientAttribute.cs diff --git a/CSharp/TfsCmdlets/HttpClients/IVssHttpClient.cs b/CSharp/TfsCmdlets.Common/HttpClients/IVssHttpClient.cs similarity index 100% rename from CSharp/TfsCmdlets/HttpClients/IVssHttpClient.cs rename to CSharp/TfsCmdlets.Common/HttpClients/IVssHttpClient.cs diff --git a/CSharp/TfsCmdlets.Common/HttpClient/TeamAdminHttpClient.cs b/CSharp/TfsCmdlets.Common/HttpClients/TeamAdminHttpClient.cs similarity index 99% rename from CSharp/TfsCmdlets.Common/HttpClient/TeamAdminHttpClient.cs rename to CSharp/TfsCmdlets.Common/HttpClients/TeamAdminHttpClient.cs index eaa0265e..75ac929c 100644 --- a/CSharp/TfsCmdlets.Common/HttpClient/TeamAdminHttpClient.cs +++ b/CSharp/TfsCmdlets.Common/HttpClients/TeamAdminHttpClient.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; using Microsoft.VisualStudio.Services.Common; -namespace TfsCmdlets.HttpClient +namespace TfsCmdlets.HttpClients { /// /// Custom HTTP Client to handle team administrator management diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/RestApiServiceImpl.cs b/CSharp/TfsCmdlets.Common/Services/Impl/RestApiServiceImpl.cs index 65f6b7d7..5d69eb71 100644 --- a/CSharp/TfsCmdlets.Common/Services/Impl/RestApiServiceImpl.cs +++ b/CSharp/TfsCmdlets.Common/Services/Impl/RestApiServiceImpl.cs @@ -3,7 +3,7 @@ using Microsoft.VisualStudio.Services.Operations; using Microsoft.VisualStudio.Services.Search.WebApi.Contracts; using Microsoft.VisualStudio.Services.WebApi; -using TfsCmdlets.HttpClient; +using TfsCmdlets.HttpClients; using TfsCmdlets.Models; namespace TfsCmdlets.Services.Impl From 4dc5d91b5ffc3640f88e71150e27ec7a853c33f1 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 2 Aug 2024 02:52:01 -0300 Subject: [PATCH 15/90] Introduce new Client DI model --- CSharp/TfsCmdlets.Common/GlobalUsings.cs | 24 -- .../HttpClients/GitExtendedHttpClient.cs | 84 ------- .../HttpClients/TeamAdminHttpClient.cs | 237 ------------------ .../Cmdlets/GlobalList/ExportGlobalList.cs | 2 +- .../GeneratedMethod.cs | 35 ++- .../Generators/Controllers/ControllerInfo.cs | 5 +- .../Generators/Controllers/TypeProcessor.cs | 8 + .../Generators/HttpClients/HttpClientInfo.cs | 22 +- .../Generators/HttpClients/TypeProcessor.cs | 1 + .../Generators/Models/TypeProcessor.cs | 3 +- .../TfsCmdlets.SourceGenerators.csproj | 1 + CSharp/TfsCmdlets.sln | 6 - .../Attributes.cs | 3 + .../Cmdlets/Artifact/GetArtifact.cs | 11 +- .../Cmdlets/Artifact/GetArtifactFeed.cs | 9 +- .../Cmdlets/Artifact/GetArtifactFeedView.cs | 8 +- .../Cmdlets/Artifact/GetArtifactVersion.cs | 6 +- .../Cmdlets/CmdletBase.cs | 0 .../ExtensionManagement/DisableExtension.cs | 10 +- .../ExtensionManagement/EnableExtension.cs | 10 +- .../ExtensionManagement/GetExtension.cs | 12 +- .../ExtensionManagement/InstallExtension.cs | 9 +- .../ExtensionManagement/UninstallExtension.cs | 6 +- .../Cmdlets/Git/Branch/GetGitBranch.cs | 5 +- .../Cmdlets/Git/Branch/RemoveGitBranch.cs | 6 +- .../Cmdlets/Git/Commit/GetGitCommit.cs | 7 +- .../Cmdlets/Git/DisableGitRepository.cs | 8 +- .../Cmdlets/Git/EnableGitRepository.cs | 8 +- .../Cmdlets/Git/GetGitRepository.cs | 18 +- .../TfsCmdlets/Cmdlets/Git/Item/GetGitItem.cs | 9 +- .../Cmdlets/Git/NewGitRepository.cs | 8 +- .../Cmdlets/Git/Policy/GetGitBranchPolicy.cs | 7 +- .../Cmdlets/Git/Policy/GetGitPolicyType.cs | 8 +- .../Cmdlets/Git/RemoveGitRepository.cs | 6 +- .../Cmdlets/Git/RenameGitRepository.cs | 6 +- .../Cmdlets/Identity/GetIdentity.cs | 10 +- .../Cmdlets/Identity/Group/AddGroupMember.cs | 6 +- .../Cmdlets/Identity/Group/GetGroup.cs | 9 +- .../Cmdlets/Identity/Group/NewGroup.cs | 7 +- .../Cmdlets/Identity/Group/RemoveGroup.cs | 6 +- .../Identity/Group/RemoveGroupMember.cs | 6 +- .../Cmdlets/Identity/User/GetUser.cs | 12 +- .../Cmdlets/Identity/User/RemoveUser.cs | 7 +- .../Definition/DisableBuildDefinition.cs | 5 +- .../Build/Definition/EnableBuildDefinition.cs | 5 +- .../Build/Definition/GetBuildDefinition.cs | 7 +- .../Build/Definition/ResumeBuildDefinition.cs | 5 +- .../Definition/SuspendBuildDefinition.cs | 5 +- .../Build/Folder/GetBuildDefinitionFolder.cs | 8 +- .../Build/Folder/NewBuildDefinitionFolder.cs | 6 +- .../Folder/RemoveBuildDefinitionFolder.cs | 7 +- .../Cmdlets/Pipeline/Build/StartBuild.cs | 6 +- .../ReleaseManagement/GetReleaseDefinition.cs | 8 +- .../GetReleaseDefinitionFolder.cs | 8 +- .../NewReleaseDefinitionFolder.cs | 5 +- .../RemoveReleaseDefinitionFolder.cs | 5 +- .../ProcessTemplate/GetProcessTemplate.cs | 10 +- .../ProcessTemplate/NewProcessTemplate.cs | 6 +- .../ServiceHook/GetServiceHookConsumer.cs | 5 +- .../GetServiceHookNotificationHistory.cs | 5 +- .../ServiceHook/GetServiceHookPublisher.cs | 6 +- .../ServiceHook/GetServiceHookSubscription.cs | 5 +- .../Team/Backlog/GetTeamBacklogLevel.cs | 7 +- .../Cmdlets/Team/Board/GetTeamBoard.cs | 10 +- .../Team/Board/GetTeamBoardCardRule.cs | 5 +- CSharp/TfsCmdlets/Cmdlets/Team/GetTeam.cs | 30 +-- CSharp/TfsCmdlets/Cmdlets/Team/NewTeam.cs | 6 +- CSharp/TfsCmdlets/Cmdlets/Team/RemoveTeam.cs | 6 +- CSharp/TfsCmdlets/Cmdlets/Team/RenameTeam.cs | 6 +- CSharp/TfsCmdlets/Cmdlets/Team/SetTeam.cs | 22 +- .../Cmdlets/Team/TeamAdmin/AddTeamAdmin.cs | 7 +- .../Cmdlets/Team/TeamAdmin/RemoveTeamAdmin.cs | 8 +- .../Cmdlets/Team/TeamMember/AddTeamMember.cs | 2 +- .../Avatar/ImportTeamProjectAvatar.cs | 6 +- .../Avatar/RemoveTeamProjectAvatar.cs | 6 +- .../Cmdlets/TeamProject/GetTeamProject.cs | 18 +- .../Cmdlets/TeamProject/NewTeamProject.cs | 6 +- .../Cmdlets/TeamProject/RemoveTeamProject.cs | 14 +- .../Cmdlets/TeamProject/RenameTeamProject.cs | 7 +- .../Cmdlets/TeamProject/SetTeamProject.cs | 5 +- .../Cmdlets/TestManagement/CopyTestPlan.cs | 16 +- .../Cmdlets/TestManagement/NewTestPlan.cs | 6 +- .../Cmdlets/TestManagement/RemoveTestPlan.cs | 7 +- .../Cmdlets/TestManagement/RenameTestPlan.cs | 6 +- .../Cmdlets/TfsCmdletAttribute.cs | 0 CSharp/TfsCmdlets/Cmdlets/Wiki/GetWiki.cs | 12 +- CSharp/TfsCmdlets/Cmdlets/Wiki/NewWiki.cs | 6 +- CSharp/TfsCmdlets/Cmdlets/Wiki/RemoveWiki.cs | 6 +- .../GetClassificationNodeController.cs | 12 +- .../MoveClassificationNodeController.cs | 9 +- .../NewClassificationNodeController.cs | 8 +- .../RemoveClassificationNodeController.cs | 6 +- .../RenameClassificationNodeController.cs | 10 +- .../WorkItem/AreasIterations/SetIteration.cs | 7 +- .../Cmdlets/WorkItem/GetWorkItem.cs | 57 +++-- .../WorkItem/History/GetWorkItemHistory.cs | 5 +- .../WorkItem/Linking/AddWorkItemLink.cs | 5 +- .../Linking/ExportWorkItemAttachment.cs | 5 +- .../Linking/GetWorkItemLinkEndType.cs | 6 +- .../Cmdlets/WorkItem/MoveWorkItem.cs | 5 +- .../Cmdlets/WorkItem/NewWorkItem.cs | 6 +- .../Query/GetWorkItemQueryItemController.cs | 13 +- .../Query/NewWorkItemQueryItemController.cs | 5 +- .../UndoWorkItemQueryRemovalController.cs | 6 +- .../Cmdlets/WorkItem/RemoveWorkItem.cs | 5 +- .../Cmdlets/WorkItem/SearchWorkItem.cs | 7 +- .../Cmdlets/WorkItem/SetWorkItem.cs | 6 +- .../WorkItem/Tagging/GetWorkItemTag.cs | 7 +- .../WorkItem/Tagging/NewWorkItemTag.cs | 6 +- .../WorkItem/Tagging/RemoveWorkItemTag.cs | 5 +- .../WorkItem/Tagging/RenameWorkItemTag.cs | 5 +- .../Tagging/ToggleWorkItemTagController.cs | 6 +- .../Cmdlets/WorkItem/UndoWorkItemRemoval.cs | 6 +- .../WorkItem/WorkItemType/GetWorkItemType.cs | 7 +- .../Controllers/ControllerBase.cs | 2 - .../Enums.cs | 0 .../Extensions/HashtableExtensions.cs | 0 .../Extensions/JsonExtensions.cs | 0 .../Extensions/ListExtensions.cs | 0 .../Extensions/ObjectExtensions.cs | 0 .../Extensions/PSObjectExtensions.cs | 0 .../Extensions/PipelineExtensions.cs | 0 .../Extensions/StringExtensions.cs | 0 .../Extensions/TaskExtensions.cs | 0 .../Extensions/TeamProjectExtensions.cs | 0 .../Extensions/WorkItemExtensions.cs | 0 .../Extensions/XmlExtensions.cs | 0 CSharp/TfsCmdlets/GlobalUsings.cs | 1 + .../HttpClients/HttpClientAttribute.cs | 0 .../IAccountLicensingHttpClient.cs | 9 + .../HttpClients/IBuildHttpClient.cs | 9 + .../IExtensionManagementHttpClient.cs | 9 + .../TfsCmdlets/HttpClients/IFeedHttpClient.cs | 9 + .../HttpClients/IGenericHttpClient.cs | 7 + .../HttpClients/IGitExtendedHttpClient.cs | 9 + .../TfsCmdlets/HttpClients/IGitHttpClient.cs | 9 + .../HttpClients/IGraphHttpClient.cs | 9 + .../HttpClients/IIdentityHttpClient.cs | 9 + .../HttpClients/IOperationsHttpClient.cs | 9 + .../HttpClients/IPolicyHttpClient.cs | 9 + .../HttpClients/IProcessHttpClient.cs | 9 + .../HttpClients/IProjectHttpClient.cs | 9 + .../HttpClients/IReleaseHttpClient.cs | 9 + .../HttpClients/IReleaseHttpClient2.cs | 9 + .../HttpClients/ISearchHttpClient.cs | 9 + .../IServiceHooksPublisherHttpClient.cs | 9 + .../HttpClients/ITaggingHttpClient.cs | 9 + .../HttpClients/ITeamAdminHttpClient.cs | 136 ++++++++++ .../TfsCmdlets/HttpClients/ITeamHttpClient.cs | 9 + .../HttpClients/ITestPlanHttpClient.cs | 9 + .../HttpClients/IVssHttpClient.cs | 0 .../TfsCmdlets/HttpClients/IWikiHttpClient.cs | 9 + .../TfsCmdlets/HttpClients/IWorkHttpClient.cs | 10 + .../IWorkItemTrackingProcessHttpClient.cs | 9 + .../HttpClients/Impl}/GenericHttpClient.cs | 8 +- .../HttpClients/Impl/GitExtendedHttpClient.cs | 80 ++++++ .../HttpClients/Impl/TeamAdminHttpClient.cs | 108 ++++++++ .../Models/BacklogLevelConfiguration.cs | 0 .../Models/Board.cs | 0 .../Models/CardRule.cs | 0 .../Models/ClassificationNode.cs | 4 +- .../Models/Connection.cs | 0 .../Models/ContributionNodeQuery.cs | 0 .../Models/GitItem.cs | 0 .../Models/GlobalList.cs | 0 .../Models/Identity.cs | 0 .../Models/IdentityRefWrapper.cs | 0 .../Models/ModelBase.cs | 0 .../Models/ServerVersion.cs | 0 .../Models/Team.cs | 0 .../Models/TeamAdmin.cs | 0 .../Models/TeamMember.cs | 0 .../Models/TeamProjectMember.cs | 0 .../Models/TfsInstallationPath.cs | 0 .../Models/WorkItem/Query/QueryItem.cs | 2 +- .../Models/WorkItemHistory.cs | 0 .../Services/IAsyncOperationAwaiter.cs | 0 .../Services/IController.cs | 0 .../Services/ICurrentConnections.cs | 0 .../Services/IDataManager.cs | 2 - .../Services/IDescriptorService.cs | 0 .../Services/IInteractiveAuthentication.cs | 0 .../Services/IKnownWorkItemLinkTypes.cs | 0 .../Services/ILegacyWorkItemService.cs | 0 .../Services/ILogger.cs | 0 .../Services/INodeUtil.cs | 0 .../Services/IPaginator.cs | 0 .../Services/IParameterManager.cs | 0 .../Services/IPowerShellService.cs | 0 .../Services/IProcessUtil.cs | 0 .../Services/IRegistryService.cs | 0 .../Services/IRestApiService.cs | 0 .../Services/IRuntimeUtil.cs | 0 .../Services/ITfsServiceProvider.cs | 0 .../Services/ITfsVersionTable.cs | 0 .../Services/IWorkItemPatchBuilder.cs | 0 .../Impl/AsyncOperationAwaiterImpl.cs | 10 +- .../Services/Impl/CurrentConnectionsImpl.cs | 0 .../Services/Impl/DataManagerImpl.cs | 8 - .../Services/Impl/DescriptorServiceImpl.cs | 13 +- .../Impl/InteractiveAuthenticationImpl.cs | 0 .../Impl/KnownWorkItemLinkTypesImpl.cs | 0 .../Impl/LegacyWorkItemServiceImpl.cs | 0 .../Services/Impl/LoggerImpl.cs | 0 .../Services/Impl/NodeUtilImpl.cs | 0 .../Services/Impl/PaginatorImpl.cs | 0 .../Services/Impl/ParameterManagerImpl.cs | 0 .../Services/Impl/PowerShellServiceImpl.cs | 0 .../Services/Impl/ProcessUtil.cs | 0 .../Services/Impl/RegistryServiceImpl.cs | 0 .../Services/Impl/RestApiServiceImpl.cs | 13 +- .../Services/Impl/RuntimeUtilImpl.cs | 0 .../Services/Impl/TfsVersionTableImpl.cs | 0 .../Services/Impl/WorkItemPatchBuilderImpl.cs | 0 .../Services/ServiceLocator.cs | 0 .../ShellHelper.cs | 0 CSharp/TfsCmdlets/TfsCmdlets.csproj | 3 +- .../Util/ErrorUtil.cs | 0 .../Util/LazyProperty.cs | 0 .../Util/Mru.cs | 0 .../Util/PSJsonConverter.cs | 0 221 files changed, 955 insertions(+), 884 deletions(-) delete mode 100644 CSharp/TfsCmdlets.Common/GlobalUsings.cs delete mode 100644 CSharp/TfsCmdlets.Common/HttpClients/GitExtendedHttpClient.cs delete mode 100644 CSharp/TfsCmdlets.Common/HttpClients/TeamAdminHttpClient.cs rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Attributes.cs (97%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Cmdlets/CmdletBase.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Cmdlets/TfsCmdletAttribute.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Controllers/ControllerBase.cs (96%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Enums.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Extensions/HashtableExtensions.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Extensions/JsonExtensions.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Extensions/ListExtensions.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Extensions/ObjectExtensions.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Extensions/PSObjectExtensions.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Extensions/PipelineExtensions.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Extensions/StringExtensions.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Extensions/TaskExtensions.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Extensions/TeamProjectExtensions.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Extensions/WorkItemExtensions.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Extensions/XmlExtensions.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/HttpClients/HttpClientAttribute.cs (100%) create mode 100644 CSharp/TfsCmdlets/HttpClients/IAccountLicensingHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IBuildHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IExtensionManagementHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IFeedHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IGenericHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IGitExtendedHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IGitHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IGraphHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IIdentityHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IOperationsHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IPolicyHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IProcessHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IProjectHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IReleaseHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IReleaseHttpClient2.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/ISearchHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IServiceHooksPublisherHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/ITaggingHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/ITeamAdminHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/ITeamHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/ITestPlanHttpClient.cs rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/HttpClients/IVssHttpClient.cs (100%) create mode 100644 CSharp/TfsCmdlets/HttpClients/IWikiHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IWorkHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/IWorkItemTrackingProcessHttpClient.cs rename CSharp/{TfsCmdlets.Common/HttpClients => TfsCmdlets/HttpClients/Impl}/GenericHttpClient.cs (98%) create mode 100644 CSharp/TfsCmdlets/HttpClients/Impl/GitExtendedHttpClient.cs create mode 100644 CSharp/TfsCmdlets/HttpClients/Impl/TeamAdminHttpClient.cs rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/BacklogLevelConfiguration.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/Board.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/CardRule.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/ClassificationNode.cs (96%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/Connection.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/ContributionNodeQuery.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/GitItem.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/GlobalList.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/Identity.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/IdentityRefWrapper.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/ModelBase.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/ServerVersion.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/Team.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/TeamAdmin.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/TeamMember.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/TeamProjectMember.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/TfsInstallationPath.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/WorkItem/Query/QueryItem.cs (77%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Models/WorkItemHistory.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IAsyncOperationAwaiter.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IController.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/ICurrentConnections.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IDataManager.cs (96%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IDescriptorService.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IInteractiveAuthentication.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IKnownWorkItemLinkTypes.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/ILegacyWorkItemService.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/ILogger.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/INodeUtil.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IPaginator.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IParameterManager.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IPowerShellService.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IProcessUtil.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IRegistryService.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IRestApiService.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IRuntimeUtil.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/ITfsServiceProvider.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/ITfsVersionTable.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/IWorkItemPatchBuilder.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/AsyncOperationAwaiterImpl.cs (80%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/CurrentConnectionsImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/DataManagerImpl.cs (97%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/DescriptorServiceImpl.cs (53%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/InteractiveAuthenticationImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/KnownWorkItemLinkTypesImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/LegacyWorkItemServiceImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/LoggerImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/NodeUtilImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/PaginatorImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/ParameterManagerImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/PowerShellServiceImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/ProcessUtil.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/RegistryServiceImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/RestApiServiceImpl.cs (96%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/RuntimeUtilImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/TfsVersionTableImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/Impl/WorkItemPatchBuilderImpl.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Services/ServiceLocator.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/ShellHelper.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Util/ErrorUtil.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Util/LazyProperty.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Util/Mru.cs (100%) rename CSharp/{TfsCmdlets.Common => TfsCmdlets}/Util/PSJsonConverter.cs (100%) diff --git a/CSharp/TfsCmdlets.Common/GlobalUsings.cs b/CSharp/TfsCmdlets.Common/GlobalUsings.cs deleted file mode 100644 index 0d6d0311..00000000 --- a/CSharp/TfsCmdlets.Common/GlobalUsings.cs +++ /dev/null @@ -1,24 +0,0 @@ -global using System; -global using System.Collections; -global using System.Collections.Generic; -global using System.Composition; -global using System.IO; -global using System.Linq; -global using System.Management.Automation; -global using System.Text; -global using TfsCmdlets.Extensions; -global using TfsCmdlets.Services; - -global using WebApiWorkItem = Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItem; -global using WebApiTeamProject = Microsoft.TeamFoundation.Core.WebApi.TeamProject; -global using WebApiIdentity = Microsoft.VisualStudio.Services.Identity.Identity; -global using WebApiIdentityRef = Microsoft.VisualStudio.Services.WebApi.IdentityRef; -global using WebApiProcess = Microsoft.TeamFoundation.Core.WebApi.Process; -global using WebApiTeamProjectRef = Microsoft.TeamFoundation.Core.WebApi.TeamProjectReference; -global using WebApiWorkItemType = Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemType; -global using WebApiQueryMembership = Microsoft.VisualStudio.Services.Identity.QueryMembership; -global using WebApiWorkItemRelation = Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemRelation; -global using WebApiBoard = Microsoft.TeamFoundation.Work.WebApi.Board; -global using WebApiTeam = Microsoft.TeamFoundation.Core.WebApi.WebApiTeam; -global using WebApiFeed = Microsoft.VisualStudio.Services.Feed.WebApi.Feed; -global using WebApiPackage = Microsoft.VisualStudio.Services.Feed.WebApi.Package; diff --git a/CSharp/TfsCmdlets.Common/HttpClients/GitExtendedHttpClient.cs b/CSharp/TfsCmdlets.Common/HttpClients/GitExtendedHttpClient.cs deleted file mode 100644 index b8e7c016..00000000 --- a/CSharp/TfsCmdlets.Common/HttpClients/GitExtendedHttpClient.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Net.Http; -using Microsoft.VisualStudio.Services.Common; - -namespace TfsCmdlets.HttpClients -{ - /// - /// Custom HTTP Client to handle extended Git repository management - /// - public class GitExtendedHttpClient : GenericHttpClient - { - /// - /// Enables/disables a Git repository - /// - public void UpdateRepositoryEnabledStatus(Guid projectId, Guid repoId, bool enabled) - { - UpdateRepositoryEnabledStatus(projectId.ToString(), repoId, enabled); - } - - /// - /// Enables/disables a Git repository - /// - public void UpdateRepositoryEnabledStatus(string project, Guid repoId, bool enabled) - { - PostForm( - $"{project}/_api/_versioncontrol/UpdateRepositoryOption", - new Dictionary - { - ["repositoryId"] = repoId.ToString(), - ["option"] = $"{{'key':'IsDisabled', 'value':{(!enabled).ToString().ToLowerInvariant()}}}" - }, - true, - $"/{project}/_settings/repositories?repo={repoId}", - null, - new Dictionary { - ["__v"] = "5", - ["repositoryId"] = repoId.ToString() - } - ); - } - - #region Constructors and fields - - /// - /// Creates a new instance of the GitExtendedHttpClient class - /// - public GitExtendedHttpClient(Uri baseUrl, VssCredentials credentials) : base(baseUrl, credentials) - { - } - - /// - /// Creates a new instance of the GitExtendedHttpClient class - /// - public GitExtendedHttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings) : base( - baseUrl, credentials, settings) - { - } - - /// - /// Creates a new instance of the GitExtendedHttpClient class - /// - public GitExtendedHttpClient(Uri baseUrl, VssCredentials credentials, params DelegatingHandler[] handlers) : base( - baseUrl, credentials, handlers) - { - } - - /// - /// Creates a new instance of the GitExtendedHttpClient class - /// - public GitExtendedHttpClient(Uri baseUrl, HttpMessageHandler pipeline, bool disposeHandler) : base(baseUrl, - pipeline, disposeHandler) - { - } - - /// - /// Creates a new instance of the GitExtendedHttpClient class - /// - public GitExtendedHttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings, - params DelegatingHandler[] handlers) : base(baseUrl, credentials, settings, handlers) - { - } - - #endregion - } -} \ No newline at end of file diff --git a/CSharp/TfsCmdlets.Common/HttpClients/TeamAdminHttpClient.cs b/CSharp/TfsCmdlets.Common/HttpClients/TeamAdminHttpClient.cs deleted file mode 100644 index 75ac929c..00000000 --- a/CSharp/TfsCmdlets.Common/HttpClients/TeamAdminHttpClient.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System.Net.Http; -using System.Runtime.Serialization; -using Microsoft.VisualStudio.Services.Common; - -namespace TfsCmdlets.HttpClients -{ - /// - /// Custom HTTP Client to handle team administrator management - /// - public class TeamAdminHttpClient : GenericHttpClient - { - /// - /// Adds an administrator to a team - /// - public IEnumerable AddTeamAdmin(Guid projectId, Guid teamId, IEnumerable userIds) - { - return AddTeamAdmin(projectId.ToString(), teamId, userIds); - } - - /// - /// Adds an administrator to a team - /// - public IEnumerable AddTeamAdmin(string project, Guid teamId, IEnumerable userIds) - { - var result = Post( - $"/{project}/_api/_identity/AddTeamAdmins", - new AddTeamAdminRequestData - { - Team = teamId, - NewUsers = "[]", - ExistingUsers = $"[{string.Join(", ", userIds.Select(id => "\"" + id + "\""))}]" - }); - - return result.Admins; - } - - /// - /// Removes an administrator from a team - /// - public bool RemoveTeamAdmin(Guid project, Guid teamId, Guid userId) - { - return RemoveTeamAdmin(project.ToString(), teamId, userId); - } - - /// - /// Removes an administrator from a team - /// - public bool RemoveTeamAdmin(string project, Guid teamId, Guid userId) - { - var result = PostForm( - $"{project}/_api/_identity/RemoveTeamAdmin", - new Dictionary - { - ["teamId"] = teamId.ToString(), - ["tfidToRemove"] = userId.ToString() - }, - true, - $"/{project}/_settings/teams?teamId={teamId}", - null, - new Dictionary {["__v"] = "5"}, - "application/json" - ); - - return result.Success; - } - - #region Constructors and fields - - /// - /// Creates a new instance of the TeamAdminHttpClient class - /// - public TeamAdminHttpClient(Uri baseUrl, VssCredentials credentials) : base(baseUrl, credentials) - { - } - - /// - /// Creates a new instance of the TeamAdminHttpClient class - /// - public TeamAdminHttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings) : base( - baseUrl, credentials, settings) - { - } - - /// - /// Creates a new instance of the TeamAdminHttpClient class - /// - public TeamAdminHttpClient(Uri baseUrl, VssCredentials credentials, params DelegatingHandler[] handlers) : base( - baseUrl, credentials, handlers) - { - } - - /// - /// Creates a new instance of the TeamAdminHttpClient class - /// - public TeamAdminHttpClient(Uri baseUrl, HttpMessageHandler pipeline, bool disposeHandler) : base(baseUrl, - pipeline, disposeHandler) - { - } - - /// - /// Creates a new instance of the TeamAdminHttpClient class - /// - public TeamAdminHttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings, - params DelegatingHandler[] handlers) : base(baseUrl, credentials, settings, handlers) - { - } - - #endregion - } - - #region Data Classes - - /// - /// Represents a collection of team administrators - /// - [DataContract] - public class TeamAdmins - { - /// - /// Collection of team administrators - /// - [DataMember(Name = "admins")] - public TeamAdmin[] Admins { get; set; } - } - - /// - /// Represents a team administrator - /// - public class TeamAdmin - { - /// - /// Identity Type - /// - public string IdentityType { get; set; } - - /// - /// Friendly Display Name - /// - public string FriendlyDisplayName { get; set; } - - /// - /// Display Name - /// - public string DisplayName { get; set; } - - /// - /// Sub-header - /// - public string SubHeader { get; set; } - - /// - /// Team Foundation Id - /// - public string TeamFoundationId { get; set; } - - /// - /// Entity Id - /// - public string EntityId { get; set; } - - /// - /// List of Errors - /// - public object[] Errors { get; set; } - - /// - /// List of Warnings - /// - public object[] Warnings { get; set; } - - /// - /// User Domain - /// - public string Domain { get; set; } - - /// - /// User Account Name - /// - public string AccountName { get; set; } - - /// - /// Is Windows User - /// - public bool IsWindowsUser { get; set; } - - /// - /// Email Address - /// - public string MailAddress { get; set; } - - /// - public override string ToString() - { - return $"{FriendlyDisplayName} ({AccountName})"; - } - } - - /// - /// The request body to submit to the "Add Admin" service - /// - [DataContract] - public class AddTeamAdminRequestData - { - /// - /// TeamId - /// - [DataMember(Name = "teamId")] - public Guid Team { get; set; } - - /// - /// List of New Users - /// - [DataMember(Name = "newUsersJson")] - public string NewUsers { get; set; } - - /// - /// List of Existing Users - /// - [DataMember(Name = "existingUsersJson")] - public string ExistingUsers { get; set; } - } - - /// - /// The request body to submit to the "Remove Admin" service - /// - [DataContract] - public class RemoveTeamAdminResult - { - /// - /// Indicates the success of the operation - /// - [DataMember(Name = "success")] - public bool Success { get; set; } - } - - #endregion -} \ No newline at end of file diff --git a/CSharp/TfsCmdlets.Legacy/Cmdlets/GlobalList/ExportGlobalList.cs b/CSharp/TfsCmdlets.Legacy/Cmdlets/GlobalList/ExportGlobalList.cs index 1c55ffe1..8ddc5fdf 100644 --- a/CSharp/TfsCmdlets.Legacy/Cmdlets/GlobalList/ExportGlobalList.cs +++ b/CSharp/TfsCmdlets.Legacy/Cmdlets/GlobalList/ExportGlobalList.cs @@ -31,7 +31,7 @@ partial class ExportGlobalList /// /// Specifies the name of the global list to be exported. Wildcards are supported. /// When omitted, it defaults to all global lists in the supplied team project collection. - /// When using wilcards, a single XML document will be producer containing all matching + /// When using Wildcards, a single XML document will be producer containing all matching /// global lists. /// [Parameter(Position = 0)] diff --git a/CSharp/TfsCmdlets.SourceGenerators/GeneratedMethod.cs b/CSharp/TfsCmdlets.SourceGenerators/GeneratedMethod.cs index 01871cbd..96d6e93b 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/GeneratedMethod.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/GeneratedMethod.cs @@ -12,13 +12,24 @@ public GeneratedMethod(IMethodSymbol method) { var parms1 = new List(); var parms2 = new List(); - - Name = method.Name; + + Name = method.Arity > 0 ? $"{method.Name}<{string.Join(", ", method.TypeArguments.Select(t => t.FullName()))}>" : method.Name; ReturnType = method.ReturnType; foreach (var p in method.Parameters) { - var defaultValue = p.HasExplicitDefaultValue ? $" = {p.ExplicitDefaultValue?? (p.Type.IsValueType? $"default({p.Type.ToDisplayString()})" : "null")}" : string.Empty; + var defaultValue = p.HasExplicitDefaultValue ? + p.ExplicitDefaultValue switch + { + string s => $" = \"{s}\"", + char c => $" = '{c}'", + bool b => $" = {b.ToString().ToLower()}", + decimal d => $" = {d}m", + null => p.Type.IsValueType? $" = default({p.Type.ToDisplayString()})": " = null", + _ => $" = {p.ExplicitDefaultValue}" + } + : string.Empty; + parms1.Add($"{p.Type.FullName()} {p.Name}{defaultValue}"); parms2.Add(p.Name); } @@ -29,17 +40,17 @@ public GeneratedMethod(IMethodSymbol method) public string SignatureNamesOnly { get; set; } - public string Name { get; } - - public string Signature { get; } - + public string Name { get; } + + public string Signature { get; } + public ITypeSymbol ReturnType { get; } public override string ToString() { return ToString(null); } - + public string ToString(string body) { var sb = new StringBuilder(); @@ -48,13 +59,13 @@ public string ToString(string body) if (string.IsNullOrWhiteSpace(body)) return sb.ToString(); var isMethodBody = body.TrimStart().StartsWith("=>"); - + sb.AppendLine(); - - if(!isMethodBody) sb.AppendLine("{"); + + if (!isMethodBody) sb.AppendLine("{"); sb.AppendLine(body); if (!isMethodBody) sb.AppendLine("}"); - + return sb.ToString(); } }; diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs index dc3a8b88..0f2fd9af 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs @@ -14,6 +14,8 @@ public class ControllerInfo : GeneratorState public string GenericArg { get; } internal string Verb { get; } public INamedTypeSymbol DataType { get; } + public INamedTypeSymbol Client { get; } + public INamedTypeSymbol BaseClass { get; } public string CmdletName { get; } public INamedTypeSymbol Cmdlet { get; } @@ -44,7 +46,8 @@ internal ControllerInfo(INamedTypeSymbol controller, GeneratorExecutionContext c if (Cmdlet == null) throw new ArgumentException($"Unable to find cmdlet class '{CmdletName}'"); BaseClass = customBaseClass ?? context.Compilation.GetTypeByMetadataName("TfsCmdlets.Controllers.ControllerBase"); ; - DataType = controller.GetAttributeConstructorValue("CmdletControllerAttribute"); ; + DataType = controller.GetAttributeConstructorValue("CmdletControllerAttribute"); + Client = controller.GetAttributeNamedValue("CmdletControllerAttribute", "Client"); GenericArg = DataType == null ? string.Empty : $"<{DataType}>"; Verb = Cmdlet.Name.Substring(0, Cmdlet.Name.FindIndex(char.IsUpper, 1)); Noun = Cmdlet.Name.Substring(Verb.Length); diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs index ca1b1213..aef36784 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs @@ -26,7 +26,13 @@ public override string GenerateCode() var props = new StringBuilder(); var cacheProps = new StringBuilder(); var controller = _controller; + var clientProp = string.Empty; + if(controller.Client != null) + { + clientProp = $"[Import] private {controller.Client.FullName()} Client {{ get; set; }}"; + } + foreach (var prop in controller.GeneratedProperties.Values) { props.Append(prop.ToString()); @@ -51,6 +57,8 @@ namespace {controller.Namespace} {{ internal partial class {controller.Name}: {controller.BaseClassName} {{ + {clientProp} + {props} protected override void CacheParameters() diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/HttpClientInfo.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/HttpClientInfo.cs index cbec53de..1fb9dcb6 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/HttpClientInfo.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/HttpClientInfo.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; @@ -12,22 +13,23 @@ internal HttpClientInfo(INamedTypeSymbol symbol, GeneratorExecutionContext conte : base(symbol, logger) { OriginalType = symbol.GetAttributeConstructorValue("HttpClientAttribute"); + Methods = OriginalType + .GetMembersRecursively(SymbolKind.Method, "Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase") + .Cast() + .Where(m => + m.MethodKind == MethodKind.Ordinary && + m.DeclaredAccessibility == Accessibility.Public && + !m.IsOverride && + !m.HasAttribute("ObsoleteAttribute")) + .ToList(); } public INamedTypeSymbol OriginalType { get; } + public IEnumerable Methods { get; } internal IEnumerable GenerateMethods() { - var methods = OriginalType - .GetMembersRecursively(SymbolKind.Method, "Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase") - .Cast() - .Where(m => - m.MethodKind == MethodKind.Ordinary && - m.DeclaredAccessibility == Accessibility.Public && - !m.HasAttribute("ObsoleteAttribute")) - .ToList(); - - foreach (var method in methods) + foreach (var method in Methods) { yield return new GeneratedMethod(method); } diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/TypeProcessor.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/TypeProcessor.cs index b1bcfad1..e173811d 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/TypeProcessor.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/HttpClients/TypeProcessor.cs @@ -61,6 +61,7 @@ public partial interface {{client.Name}}: IVssHttpClient } [Export(typeof({{client.Name}}))] + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] internal class {{client.Name}}Impl: {{client.Name}} { private {{client.OriginalType}} _client; diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/Models/TypeProcessor.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/Models/TypeProcessor.cs index a7b44c3e..ad904ba8 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/Models/TypeProcessor.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/Models/TypeProcessor.cs @@ -9,7 +9,7 @@ public class TypeProcessor : BaseTypeProcessor protected override void OnInitialize() { - DataType = Type.GetAttributeConstructorValue("ModeleAttribute"); ; + DataType = Type.GetAttributeConstructorValue("ModelAttribute"); ; } public override string GenerateCode() @@ -21,6 +21,7 @@ public override string GenerateCode() */ public partial class {Type.Name}: ModelBase<{DataType}> {{ + public {Type.Name}({DataType} obj): base(obj) {{ }} public static implicit operator {Type}({DataType} obj) => new {Type}(obj); public static implicit operator {DataType}({Type} obj) => obj.InnerObject; }} diff --git a/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj b/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj index 31e76d6a..0bf0656f 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj +++ b/CSharp/TfsCmdlets.SourceGenerators/TfsCmdlets.SourceGenerators.csproj @@ -10,6 +10,7 @@ + diff --git a/CSharp/TfsCmdlets.sln b/CSharp/TfsCmdlets.sln index 1ddf2fda..8d858da2 100644 --- a/CSharp/TfsCmdlets.sln +++ b/CSharp/TfsCmdlets.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets", "TfsCmdlets\Tf EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets.SourceGenerators", "TfsCmdlets.SourceGenerators\TfsCmdlets.SourceGenerators.csproj", "{1045CB54-F17F-42EA-9497-5D60815BDC64}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets.Common", "TfsCmdlets.Common\TfsCmdlets.Common.csproj", "{CD6FA384-22B2-42E8-8D4B-69BBF66DC792}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TfsCmdlets.SourceGenerators.UnitTests", "TfsCmdlets.SourceGenerators.UnitTests\TfsCmdlets.SourceGenerators.UnitTests.csproj", "{76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}" EndProject Global @@ -33,10 +31,6 @@ Global {1045CB54-F17F-42EA-9497-5D60815BDC64}.Debug|Any CPU.Build.0 = Debug|Any CPU {1045CB54-F17F-42EA-9497-5D60815BDC64}.Release|Any CPU.ActiveCfg = Release|Any CPU {1045CB54-F17F-42EA-9497-5D60815BDC64}.Release|Any CPU.Build.0 = Release|Any CPU - {CD6FA384-22B2-42E8-8D4B-69BBF66DC792}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD6FA384-22B2-42E8-8D4B-69BBF66DC792}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD6FA384-22B2-42E8-8D4B-69BBF66DC792}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CD6FA384-22B2-42E8-8D4B-69BBF66DC792}.Release|Any CPU.Build.0 = Release|Any CPU {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}.Debug|Any CPU.Build.0 = Debug|Any CPU {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/CSharp/TfsCmdlets.Common/Attributes.cs b/CSharp/TfsCmdlets/Attributes.cs similarity index 97% rename from CSharp/TfsCmdlets.Common/Attributes.cs rename to CSharp/TfsCmdlets/Attributes.cs index a91ee765..a056887d 100644 --- a/CSharp/TfsCmdlets.Common/Attributes.cs +++ b/CSharp/TfsCmdlets/Attributes.cs @@ -14,6 +14,9 @@ public class CmdletControllerAttribute: ExportAttribute public string[] CustomNouns { get; set; } public Type CustomBaseClass { get; set; } + + public Type Client { get; set; } + public CmdletControllerAttribute() : base(typeof(IController)) { diff --git a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifact.cs b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifact.cs index c276db5f..f13a5eaf 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifact.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifact.cs @@ -61,10 +61,11 @@ partial class GetArtifact [CmdletController(typeof(WebApiPackage))] partial class GetArtifactController { + [Import] + private IFeedHttpClient Client { get; set; } + protected override IEnumerable Run() { - var client = GetClient(); - foreach (var input in Artifact) { var artifact = input switch @@ -90,7 +91,7 @@ string s when s.IsGuid() => Guid.Parse(s), { case Guid g: { - yield return client.GetPackageAsync( + yield return Client.GetPackageAsync( packageId: g.ToString(), feedId: feedId, project: projectId) @@ -99,7 +100,7 @@ string s when s.IsGuid() => Guid.Parse(s), } case string s when s.IsWildcard(): { - yield return client.GetPackagesAsync( + yield return Client.GetPackagesAsync( project: projectId, feedId: feedId, protocolType: ProtocolType, @@ -115,7 +116,7 @@ string s when s.IsGuid() => Guid.Parse(s), } case string s: { - yield return client.GetPackagesAsync( + yield return Client.GetPackagesAsync( project: projectId, feedId: feedId, protocolType: ProtocolType, diff --git a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeed.cs b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeed.cs index 120d3c0c..eeacd8ed 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeed.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeed.cs @@ -36,10 +36,11 @@ partial class GetArtifactFeed [CmdletController(typeof(Feed))] partial class GetArtifactFeedController { + [Import] + private IFeedHttpClient Client { get; set; } + protected override IEnumerable Run() { - var client = GetClient(); - foreach (var input in Feed) { var feed = input switch @@ -59,14 +60,14 @@ string s when s.IsGuid() => Guid.Parse(s), } case Guid g: { - yield return client.GetFeedsAsync(Role) + yield return Client.GetFeedsAsync(Role, false, false) .GetResult($"Error getting artifact feed(s) '{g}'") .Where(f => f.Id == g); break; } case string s when !string.IsNullOrEmpty(s): { - yield return client.GetFeedsAsync(Role) + yield return Client.GetFeedsAsync(Role, false, false) .GetResult($"Error getting artifact feed(s) '{s}'") .Where(f1 => f1.Name.IsLike(s) && ( (string.IsNullOrEmpty(f1.Project?.Name) && ((Scope & ProjectOrCollectionScope.Collection) > 0)) || diff --git a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeedView.cs b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeedView.cs index 05d74961..c56fd25a 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeedView.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeedView.cs @@ -41,13 +41,11 @@ partial class GetArtifactFeedView public Microsoft.VisualStudio.Services.Feed.WebApi.FeedRole Role { get; set; } = Microsoft.VisualStudio.Services.Feed.WebApi.FeedRole.Administrator; } - [CmdletController(typeof(FeedView))] + [CmdletController(typeof(FeedView), Client=typeof(IFeedHttpClient))] partial class GetArtifactFeedViewController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var input in View) { var view = input switch @@ -66,14 +64,14 @@ protected override IEnumerable Run() } case string s when !string.IsNullOrEmpty(s) && feed.Project == null: { - yield return client.GetFeedViewsAsync(feed.Id.ToString()) + yield return Client.GetFeedViewsAsync(feed.Id.ToString()) .GetResult($"Error getting artifact feed view(s) '{s}'") .Where(fv => fv.Name.IsLike(s)); break; } case string s when !string.IsNullOrEmpty(s): { - yield return client.GetFeedViewsAsync(feed.Project.Id, feed.Id.ToString()) + yield return Client.GetFeedViewsAsync(feed.Project.Id, feed.Id.ToString()) .GetResult($"Error getting artifact feed view(s) '{s}'") .Where(fv => fv.Name.IsLike(s)); break; diff --git a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactVersion.cs b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactVersion.cs index 088fb00f..6852b325 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactVersion.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactVersion.cs @@ -51,13 +51,11 @@ partial class GetArtifactVersion public string ProtocolType { get; set; } } - [CmdletController(typeof(PackageVersion))] + [CmdletController(typeof(PackageVersion), Client=typeof(IFeedHttpClient))] partial class GetArtifactVersionController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var input in Version) { var version = input switch @@ -93,7 +91,7 @@ protected override IEnumerable Run() } case string s when s.IsWildcard(): { - yield return client.GetPackageVersionsAsync( + yield return Client.GetPackageVersionsAsync( project: projectId, feedId: feedId, packageId: package.Id.ToString(), diff --git a/CSharp/TfsCmdlets.Common/Cmdlets/CmdletBase.cs b/CSharp/TfsCmdlets/Cmdlets/CmdletBase.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Cmdlets/CmdletBase.cs rename to CSharp/TfsCmdlets/Cmdlets/CmdletBase.cs diff --git a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/DisableExtension.cs b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/DisableExtension.cs index fa1b96ba..e50f19b8 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/DisableExtension.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/DisableExtension.cs @@ -10,27 +10,25 @@ namespace TfsCmdlets.Cmdlets.ExtensionManagement partial class DisableExtension { /// - /// Specifies the ID or the name of the extensions. Wilcards are supported. + /// Specifies the ID or the name of the extensions. Wildcards are supported. /// [Parameter(Position = 0, ValueFromPipeline = true)] [SupportsWildcards] public object Extension { get; set; } /// - /// Specifies the ID or the name of the publisher. Wilcards are supported. + /// Specifies the ID or the name of the publisher. Wildcards are supported. /// [Parameter(Position = 1)] [SupportsWildcards] public string Publisher { get; set; } } - [CmdletController(typeof(InstalledExtension))] + [CmdletController(typeof(InstalledExtension), Client=typeof(IExtensionManagementHttpClient))] partial class DisableExtensionController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var item in Items) { if ((item.InstallState.Flags & ExtensionStateFlags.Disabled) != 0) @@ -48,7 +46,7 @@ protected override IEnumerable Run() try { - result = client.UpdateInstalledExtensionAsync(item) + result = Client.UpdateInstalledExtensionAsync(item) .GetResult("Error updating extension."); } catch (Exception ex) diff --git a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/EnableExtension.cs b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/EnableExtension.cs index 83140808..66c5d754 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/EnableExtension.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/EnableExtension.cs @@ -10,27 +10,25 @@ namespace TfsCmdlets.Cmdlets.ExtensionManagement partial class EnableExtension { /// - /// Specifies the ID or the name of the extensions. Wilcards are supported. + /// Specifies the ID or the name of the extensions. Wildcards are supported. /// [Parameter(Position = 0)] [SupportsWildcards] public object Extension { get; set; } /// - /// Specifies the ID or the name of the publisher. Wilcards are supported. + /// Specifies the ID or the name of the publisher. Wildcards are supported. /// [Parameter(Position = 1)] [SupportsWildcards] public string Publisher { get; set; } } - [CmdletController(typeof(InstalledExtension))] + [CmdletController(typeof(InstalledExtension), Client=typeof(IExtensionManagementHttpClient))] partial class EnableExtensionController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var item in GetItems(new { IncludeDisabledExtensions = true })) { if ((item.InstallState.Flags & ExtensionStateFlags.Disabled) == ExtensionStateFlags.None) @@ -48,7 +46,7 @@ protected override IEnumerable Run() try { - result = client.UpdateInstalledExtensionAsync(item) + result = Client.UpdateInstalledExtensionAsync(item) .GetResult("Error updating extension."); } catch (Exception ex) diff --git a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/GetExtension.cs b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/GetExtension.cs index 8aa16191..8765a72f 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/GetExtension.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/GetExtension.cs @@ -10,7 +10,7 @@ namespace TfsCmdlets.Cmdlets.ExtensionManagement partial class GetExtension { /// - /// Specifies the ID or the name of the extensions. Wilcards are supported. + /// Specifies the ID or the name of the extensions. Wildcards are supported. /// When omitted, returns all extensions installed in the specified organization/collection. /// [Parameter(Position = 0)] @@ -18,7 +18,7 @@ partial class GetExtension public object Extension { get; set; } = "*"; /// - /// Specifies the ID or the name of the publisher. Wilcards are supported. + /// Specifies the ID or the name of the publisher. Wildcards are supported. /// When omitted, returns all extensions installed in the specified organization/collection. /// [Parameter(Position = 1)] @@ -38,13 +38,11 @@ partial class GetExtension public SwitchParameter IncludeInstallationIssues { get; set; } } - [CmdletController(typeof(InstalledExtension))] + [CmdletController(typeof(InstalledExtension), Client=typeof(IExtensionManagementHttpClient))] partial class GetExtensionController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var input in Extension) { object extension = input; @@ -69,7 +67,7 @@ protected override IEnumerable Run() { Logger.Log($"Getting extensions matching name '{s}' with publisher '{publisher}'"); - foreach (var result in client.GetInstalledExtensionsAsync( + foreach (var result in Client.GetInstalledExtensionsAsync( includeDisabledExtensions: IncludeDisabledExtensions, includeErrors: IncludeErrors, includeInstallationIssues: IncludeInstallationIssues) @@ -83,7 +81,7 @@ protected override IEnumerable Run() } case string s: { - yield return client.GetInstalledExtensionByNameAsync(publisher, s) + yield return Client.GetInstalledExtensionByNameAsync(publisher, s) .GetResult("Error getting installed extension."); break; } diff --git a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/InstallExtension.cs b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/InstallExtension.cs index 92df1b76..1b2d18e2 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/InstallExtension.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/InstallExtension.cs @@ -30,7 +30,7 @@ partial class InstallExtension public string Version { get; set; } } - [CmdletController(typeof(InstalledExtension))] + [CmdletController(typeof(InstalledExtension), Client=typeof(IExtensionManagementHttpClient))] partial class InstallExtensionController { protected override IEnumerable Run() @@ -39,24 +39,21 @@ protected override IEnumerable Run() { case string s when !string.IsNullOrEmpty(s) && s.Contains("."): { - var client = GetClient(); var (publisher, extension, _) = Extension.Split('.'); if(!PowerShell.ShouldProcess(Collection, $"Install extension '{extension}' by '{publisher}'")) yield break; - yield return client.InstallExtensionByNameAsync(publisher, extension, Version) + yield return Client.InstallExtensionByNameAsync(publisher, extension, Version) .GetResult("Error installing extension."); break; } case string s when !string.IsNullOrEmpty(s): { - var client = GetClient(); - if(!PowerShell.ShouldProcess(Collection, $"Install extension '{Extension}' by '{Publisher}'")) yield break; - yield return client.InstallExtensionByNameAsync(Publisher, Extension, Version) + yield return Client.InstallExtensionByNameAsync(Publisher, Extension, Version) .GetResult("Error installing extension."); break; } diff --git a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/UninstallExtension.cs b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/UninstallExtension.cs index 73fb9a0e..03121889 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/UninstallExtension.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/UninstallExtension.cs @@ -22,13 +22,11 @@ partial class UninstallExtension public string Publisher { get; set; } } - [CmdletController(typeof(InstalledExtension))] + [CmdletController(typeof(InstalledExtension), Client = typeof(IExtensionManagementHttpClient))] partial class UninstallExtensionController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var item in Items) { if (!PowerShell.ShouldProcess(Collection, $"Uninstall extension '{item.ExtensionDisplayName}' by '{item.PublisherDisplayName}' ({item.ExtensionName}.{item.PublisherName})")) @@ -36,7 +34,7 @@ protected override IEnumerable Run() try { - client.UninstallExtensionByNameAsync(item.PublisherName, item.ExtensionName) + Client.UninstallExtensionByNameAsync(item.PublisherName, item.ExtensionName) .Wait("Error uninstalling extension."); } catch (Exception ex) diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs index ea438389..b1f80764 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs @@ -33,7 +33,7 @@ partial class GetGitBranch public object Repository { get; set; } } - [CmdletController(typeof(GitBranchStats))] + [CmdletController(typeof(GitBranchStats), Client=typeof(IGitHttpClient))] partial class GetGitBranchController { protected override IEnumerable Run() @@ -46,7 +46,6 @@ protected override IEnumerable Run() yield break; } - var client = GetClient(); string branchName = null; foreach(var branch in Branch) @@ -85,7 +84,7 @@ protected override IEnumerable Run() try { - result = client.GetBranchesAsync(repo.ProjectReference.Name, repo.Id) + result = Client.GetBranchesAsync(repo.ProjectReference.Name, repo.Id) .GetResult($"Error retrieving branch(es) '{branch}' from repository '{repo.Name}'") .Where(b => b.Name.IsLike(branchName)); } diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Branch/RemoveGitBranch.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Branch/RemoveGitBranch.cs index 89b60d1a..94125718 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Branch/RemoveGitBranch.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Branch/RemoveGitBranch.cs @@ -25,13 +25,11 @@ partial class RemoveGitBranch public object Repository { get; set; } } - [CmdletController(typeof(GitBranchStats))] + [CmdletController(typeof(GitBranchStats), Client=typeof(IGitHttpClient))] partial class RemoveGitBranchController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var branch in Items) { var commitUrl = new Uri(branch.Commit.Url); @@ -43,7 +41,7 @@ protected override IEnumerable Run() try { - client.UpdateRefsAsync(new[]{new GitRefUpdate() + Client.UpdateRefsAsync(new[]{new GitRefUpdate() { Name = $"refs/heads/{branch.Name}", OldObjectId = branch.Commit.CommitId, diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs index 35eda66a..77730961 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs @@ -125,12 +125,11 @@ partial class GetGitCommit public object Repository { get; set; } } - [CmdletController(typeof(GitCommitRef))] + [CmdletController(typeof(GitCommitRef), Client=typeof(IGitHttpClient))] partial class GetGitCommitController { protected override IEnumerable Run() { - var client = GetClient(); var repository = GetItem(new { Repository = Has_Repository? Repository: Project.Name }); string commitSha; @@ -159,7 +158,7 @@ protected override IEnumerable Run() } } - yield return client.GetCommitAsync(repository.ProjectReference.Id.ToString(), commitSha, repository.Id.ToString()) + yield return Client.GetCommitAsync(repository.ProjectReference.Id.ToString(), commitSha, repository.Id.ToString()) .GetResult($"Error getting commit '{commitSha}' in repository '{repository.Name}'"); } yield break; @@ -220,7 +219,7 @@ protected override IEnumerable Run() ToDate = Has_ToDate ? this.ToDate.ToString("yyyy-MM-ddTHH:mm:ssK") : null, }; - var result = client.GetCommitsBatchAsync(criteria, repository.Id) + var result = Client.GetCommitsBatchAsync(criteria, repository.Id) .GetResult("Error getting Git commits"); foreach (var commit in result) diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/DisableGitRepository.cs b/CSharp/TfsCmdlets/Cmdlets/Git/DisableGitRepository.cs index 00a3130e..0b498fe2 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/DisableGitRepository.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/DisableGitRepository.cs @@ -1,6 +1,6 @@ using System.Management.Automation; using Microsoft.TeamFoundation.SourceControl.WebApi; -using TfsCmdlets.HttpClient; +using TfsCmdlets.HttpClients; namespace TfsCmdlets.Cmdlets.Git { @@ -24,18 +24,16 @@ partial class DisableGitRepository public object Repository { get; set; } } - [CmdletController(typeof(GitRepository))] + [CmdletController(typeof(GitRepository), Client=typeof(IGitExtendedHttpClient))] partial class DisableGitRepositoryController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var repo in Items) { if (!PowerShell.ShouldProcess(Project, $"Disable Git repository '{repo.Name}'")) continue; - client.UpdateRepositoryEnabledStatus(Project.Name, repo.Id, false); + Client.UpdateRepositoryEnabledStatus(Project.Name, repo.Id, false); yield return GetItem(); } diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/EnableGitRepository.cs b/CSharp/TfsCmdlets/Cmdlets/Git/EnableGitRepository.cs index a6883778..bdc99d1f 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/EnableGitRepository.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/EnableGitRepository.cs @@ -1,6 +1,6 @@ using System.Management.Automation; using Microsoft.TeamFoundation.SourceControl.WebApi; -using TfsCmdlets.HttpClient; +using TfsCmdlets.HttpClients; namespace TfsCmdlets.Cmdlets.Git { @@ -25,18 +25,16 @@ partial class EnableGitRepository public object Repository { get; set; } } - [CmdletController(typeof(GitRepository))] + [CmdletController(typeof(GitRepository), Client=typeof(IGitExtendedHttpClient))] partial class EnableGitRepositoryController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var repo in Items) { if (!PowerShell.ShouldProcess(Project, $"Disable Git repository '{repo.Name}'")) continue; - client.UpdateRepositoryEnabledStatus(Project.Name, repo.Id, true); + Client.UpdateRepositoryEnabledStatus(Project.Name, repo.Id, true); yield return GetItem(); } diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/GetGitRepository.cs b/CSharp/TfsCmdlets/Cmdlets/Git/GetGitRepository.cs index 1f3f68a0..0e9c6e8a 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/GetGitRepository.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/GetGitRepository.cs @@ -32,13 +32,11 @@ partial class GetGitRepository public SwitchParameter IncludeParent { get; set; } } - [CmdletController(typeof(GitRepository))] + [CmdletController(typeof(GitRepository), Client=typeof(IGitHttpClient))] partial class GetGitRepositoryController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var input in Repository) { var repository = input switch @@ -58,14 +56,14 @@ string s when string.IsNullOrEmpty(s) => Project.Name, } case Guid guid: { - yield return client + yield return Client .GetRepositoryAsync(Project.Name, guid, includeParent: IncludeParent) .GetResult($"Error getting repository with ID {guid}"); break; } case { } when Default: { - yield return client + yield return Client .GetRepositoryAsync(Project.Name, Project.Name, includeParent: IncludeParent) .GetResult($"Error getting repository '{Project.Name}'"); break; @@ -76,21 +74,21 @@ string s when string.IsNullOrEmpty(s) => Project.Name, try { - result = client + result = Client .GetRepositoryAsync(Project.Name, s, includeParent: IncludeParent) .GetResult($"Error getting repository '{s}'"); } catch { // Workaround to retrieve disabled repositories - result = client + result = Client .GetRepositoriesAsync(Project.Name, includeLinks: true, includeHidden: true) .GetResult($"Error getting repository(ies) '{s}'") .First(r => r.Name.Equals(s, StringComparison.OrdinalIgnoreCase)); if (IncludeParent) { - result = client + result = Client .GetRepositoryAsync(Project.Name, result.Id, includeParent: true) .GetResult($"Error getting repository(ies) '{s}'"); } @@ -100,14 +98,14 @@ string s when string.IsNullOrEmpty(s) => Project.Name, } case string s: { - foreach (var repo in client + foreach (var repo in Client .GetRepositoriesAsync(Project.Name, includeLinks: true) .GetResult($"Error getting repository(ies) '{s}'") .Where(r => r.Name.IsLike(s))) { if (IncludeParent) { - yield return client + yield return Client .GetRepositoryAsync(Project.Name, repo.Id, includeParent: true) .GetResult($"Error getting repository(ies) '{s}'"); } diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Item/GetGitItem.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Item/GetGitItem.cs index a60159ee..85cea232 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Item/GetGitItem.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Item/GetGitItem.cs @@ -41,7 +41,7 @@ partial class GetGitItem public object Repository { get; set; } } - [CmdletController(typeof(GitItem))] + [CmdletController(typeof(GitItem), Client=typeof(IGitHttpClient))] partial class GetGitItemController { [Import] @@ -49,7 +49,6 @@ partial class GetGitItemController protected override IEnumerable Run() { - var client = GetClient(); var repo = GetItem(new { Default = !Has_Repository }); if(repo.Size == 0) @@ -95,7 +94,7 @@ protected override IEnumerable Run() var path = NodeUtil.NormalizeNodePath(s, includeLeadingSeparator: true, separator: '/'); - yield return new Models.GitItem(client.GetItemAsync(projectName, repo.Id, s, + yield return new Models.GitItem(Client.GetItemAsync(projectName, repo.Id, s, recursionLevel: VersionControlRecursionType.None, includeContentMetadata: IncludeMetadata, includeContent: IncludeContent, @@ -111,7 +110,7 @@ protected override IEnumerable Run() Logger.Log($"Retrieving item(s) matching '{path}' under folder '{rootPath}' in repository '{repo.Name}'"); - var result = client.GetItemsAsync(projectName, repo.Id, rootPath, + var result = Client.GetItemsAsync(projectName, repo.Id, rootPath, recursionLevel: shouldRecurse ? VersionControlRecursionType.Full : VersionControlRecursionType.OneLevel, includeContentMetadata: IncludeMetadata, includeLinks: true, @@ -131,7 +130,7 @@ protected override IEnumerable Run() { Logger.Log($"Retrieving item '{i.Path}' (including contents) in repository '{repo.Name}'"); - yield return new Models.GitItem(client.GetItemAsync(projectName, repo.Id, i.Path, + yield return new Models.GitItem(Client.GetItemAsync(projectName, repo.Id, i.Path, recursionLevel: VersionControlRecursionType.None, includeContentMetadata: IncludeMetadata, includeContent: IncludeContent, diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/NewGitRepository.cs b/CSharp/TfsCmdlets/Cmdlets/Git/NewGitRepository.cs index 4782f7f7..8407c753 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/NewGitRepository.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/NewGitRepository.cs @@ -32,15 +32,13 @@ partial class NewGitRepository public string SourceBranch { get; set; } } - [CmdletController(typeof(GitRepository))] + [CmdletController(typeof(GitRepository), Client=typeof(IGitHttpClient))] partial class NewGitRepositoryController { protected override IEnumerable Run() { if (!PowerShell.ShouldProcess(Project, $"Create Git repository '{Repository}'")) yield break; - var client = GetClient(); - var tpRef = new TeamProjectReference { Id = Project.Id, @@ -96,7 +94,7 @@ protected override IEnumerable Run() ParentRepository = parentRepoRef }; - yield return client.CreateRepositoryAsync(createOptions, Project.Name, sourceRef: sourceRef) + yield return Client.CreateRepositoryAsync(createOptions, Project.Name, sourceRef: sourceRef) .GetResult("Error forking Git repository"); yield break; @@ -108,7 +106,7 @@ protected override IEnumerable Run() ProjectReference = tpRef }; - yield return client.CreateRepositoryAsync(repoToCreate, Project.Name) + yield return Client.CreateRepositoryAsync(repoToCreate, Project.Name) .GetResult("Error creating Git repository"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitBranchPolicy.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitBranchPolicy.cs index 3a0ab9a8..cad964ee 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitBranchPolicy.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitBranchPolicy.cs @@ -32,7 +32,7 @@ partial class GetGitBranchPolicy public object Repository { get; set; } } - [CmdletController(typeof(PolicyConfiguration))] + [CmdletController(typeof(PolicyConfiguration), Client=typeof(IGitHttpClient))] partial class GetGitBranchPolicyController { protected override IEnumerable Run() @@ -42,7 +42,6 @@ protected override IEnumerable Run() var repoId = url.Segments[url.Segments.Length - 3].TrimEnd('/'); var projectId = url.Segments[url.Segments.Length - 7].TrimEnd('/'); var repo = GetItem(new{Repository = repoId, Project = projectId}); - var client = GetClient(); var branch = $"refs/heads/{b.Name}"; @@ -80,8 +79,8 @@ protected override IEnumerable Run() bool getById = (policyTypeId != Guid.Empty); var policies = (getById ? - client.GetPolicyConfigurationsAsync(repo.ProjectReference.Name, repo.Id, branch, policyTypeId): - client.GetPolicyConfigurationsAsync(repo.ProjectReference.Name, repo.Id, branch)) + Client.GetPolicyConfigurationsAsync(repo.ProjectReference.Name, repo.Id, branch, policyTypeId): + Client.GetPolicyConfigurationsAsync(repo.ProjectReference.Name, repo.Id, branch)) .GetResult($"Error getting policy definitions from branch {branch} in repository {repo.Name}") .PolicyConfigurations; diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitPolicyType.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitPolicyType.cs index 6a4bfe52..45a7e0bf 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitPolicyType.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitPolicyType.cs @@ -19,13 +19,11 @@ partial class GetGitPolicyType public object PolicyType { get; set; } = "*"; } - [CmdletController(typeof(PolicyType))] + [CmdletController(typeof(PolicyType), Client=typeof(IPolicyHttpClient))] partial class GetGitPolicyTypeController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var input in PolicyType) { var policyType = input switch @@ -43,13 +41,13 @@ protected override IEnumerable Run() } case Guid g: { - yield return client.GetPolicyTypeAsync(Project.Name, g) + yield return Client.GetPolicyTypeAsync(Project.Name, g) .GetResult("Error retrieving policy types"); break; } case string s: { - foreach (var pt in client.GetPolicyTypesAsync(Project.Name) + foreach (var pt in Client.GetPolicyTypesAsync(Project.Name) .GetResult("Error retrieving policy types") .Where(p => p.DisplayName.IsLike(s))) { diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/RemoveGitRepository.cs b/CSharp/TfsCmdlets/Cmdlets/Git/RemoveGitRepository.cs index fa6b6c37..046f52c7 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/RemoveGitRepository.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/RemoveGitRepository.cs @@ -26,20 +26,18 @@ partial class RemoveGitRepository public SwitchParameter Force { get; set; } } - [CmdletController(typeof(GitRepository))] + [CmdletController(typeof(GitRepository), Client=typeof(IGitHttpClient))] partial class RemoveGitRepositoryController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var repo in Items) { if (!PowerShell.ShouldProcess($"[Project: {repo.ProjectReference.Name}]/[Repository: {repo.Name}]", $"Delete repository")) continue; if (!(repo.DefaultBranch == null || Force) && !PowerShell.ShouldContinue($"Are you sure you want to delete Git repository '{repo.Name}'?")) continue; - client.DeleteRepositoryAsync(repo.Id).Wait(); + Client.DeleteRepositoryAsync(repo.Id).Wait(); } return null; diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/RenameGitRepository.cs b/CSharp/TfsCmdlets/Cmdlets/Git/RenameGitRepository.cs index 2f815075..f06e29af 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/RenameGitRepository.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/RenameGitRepository.cs @@ -18,7 +18,7 @@ partial class RenameGitRepository public object Repository { get; set; } } - [CmdletController(typeof(GitRepository))] + [CmdletController(typeof(GitRepository), Client=typeof(IGitHttpClient))] partial class RenameGitRepositoryController { protected override IEnumerable Run() @@ -34,9 +34,7 @@ protected override IEnumerable Run() if (!PowerShell.ShouldProcess(Project, $"Rename repository '{repoToRename.Name}' to '{NewName}'")) yield break; - var client = GetClient(); - - yield return client.RenameRepositoryAsync(repoToRename, NewName) + yield return Client.RenameRepositoryAsync(repoToRename, NewName) .GetResult("Error renaming repository"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/Identity/GetIdentity.cs b/CSharp/TfsCmdlets/Cmdlets/Identity/GetIdentity.cs index 36db6713..593184f4 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Identity/GetIdentity.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Identity/GetIdentity.cs @@ -38,13 +38,11 @@ partial class GetIdentity public SwitchParameter Current { get; set; } } - [CmdletController(typeof(Models.Identity))] + [CmdletController(typeof(Models.Identity), Client=typeof(IIdentityHttpClient))] partial class GetIdentityController { protected override IEnumerable Run() { - var client = Data.GetClient(); // ClientScope.Server - foreach (var input in Identity) { var identity = input switch @@ -69,7 +67,7 @@ protected override IEnumerable Run() } case WebApiIdentityRef ir: { - yield return new Models.Identity(client.ReadIdentityAsync(ir.Id, QueryMembership) + yield return new Models.Identity(Client.ReadIdentityAsync(ir.Id, QueryMembership) .GetResult($"Error retrieving information from identity [{identity}]")); break; } @@ -77,7 +75,7 @@ protected override IEnumerable Run() { Logger.Log($"Finding identity with ID [{g}] and QueryMembership={QueryMembership}"); - yield return new Models.Identity(client.ReadIdentityAsync(g, QueryMembership) + yield return new Models.Identity(Client.ReadIdentityAsync(g, QueryMembership) .GetResult($"Error retrieving information from identity [{identity}]")); break; } @@ -85,7 +83,7 @@ protected override IEnumerable Run() { Logger.Log($"Finding identity with account name [{identity}] and QueryMembership={QueryMembership}"); - foreach (var id in client.ReadIdentitiesAsync(IdentitySearchFilter.AccountName, s, ReadIdentitiesOptions.None, QueryMembership) + foreach (var id in Client.ReadIdentitiesAsync(IdentitySearchFilter.AccountName, s, ReadIdentitiesOptions.None, QueryMembership) .GetResult($"Error retrieving information from identity [{identity}]") .Select(i => new Models.Identity(i))) { diff --git a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/AddGroupMember.cs b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/AddGroupMember.cs index 9b510aca..964c48b5 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/AddGroupMember.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/AddGroupMember.cs @@ -24,7 +24,7 @@ partial class AddGroupMember public object Group { get; set; } } - [CmdletController] + [CmdletController(Client=typeof(IIdentityHttpClient))] partial class AddGroupMemberController { protected override IEnumerable Run() @@ -42,14 +42,12 @@ protected override IEnumerable Run() Identity = group }); - var client = Data.GetClient(); - foreach (var m in identities) { if (!PowerShell.ShouldProcess($"Group '{g.DisplayName}'", $"Add member '{m.DisplayName} ({m.UniqueName})'")) continue; - client.AddMemberToGroupAsync( + Client.AddMemberToGroupAsync( (IdentityDescriptor)g.Descriptor, (IdentityDescriptor)m.Descriptor) .GetResult($"Error adding member '{m.DisplayName}' to group '{g.DisplayName}'"); diff --git a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/GetGroup.cs b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/GetGroup.cs index 0fa456df..ca271da1 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/GetGroup.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/GetGroup.cs @@ -31,7 +31,7 @@ partial class GetGroup public SwitchParameter Recurse { get; set; } } - [CmdletController(typeof(GraphGroup))] + [CmdletController(typeof(GraphGroup), Client=typeof(IGraphHttpClient))] partial class GetGroupController { [Import] @@ -42,7 +42,6 @@ protected override IEnumerable Run() var group = Parameters.Get(nameof(GetGroup.Group)); var scope = Parameters.Get(nameof(GetGroup.Scope)); var recurse = Parameters.Get(nameof(GetGroup.Recurse)); - var client = Data.GetClient(); PagedGraphGroups result = null; string groupName; @@ -72,7 +71,7 @@ protected override IEnumerable Run() { do { - result = client.ListGroupsAsync(continuationToken: result?.ContinuationToken.FirstOrDefault()) + result = Client.ListGroupsAsync(continuationToken: result?.ContinuationToken.FirstOrDefault()) .GetResult("Error getting groups in collection"); foreach (var g in result.GraphGroups @@ -92,7 +91,7 @@ protected override IEnumerable Run() do { - result = client.ListGroupsAsync(continuationToken: result?.ContinuationToken.FirstOrDefault()) + result = Client.ListGroupsAsync(continuationToken: result?.ContinuationToken.FirstOrDefault()) .GetResult("Error getting groups in collection"); foreach (var g in result.GraphGroups @@ -113,7 +112,7 @@ protected override IEnumerable Run() do { - result = client.ListGroupsAsync(scopeDescriptor: descriptor.Value, continuationToken: result?.ContinuationToken.FirstOrDefault()) + result = Client.ListGroupsAsync(scopeDescriptor: descriptor.Value, continuationToken: result?.ContinuationToken.FirstOrDefault()) .GetResult($"Error getting groups in team project {tp.Name}"); foreach (var g in result.GraphGroups.Where(g => g.PrincipalName.IsLike(groupName) || g.DisplayName.IsLike(groupName))) diff --git a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs index ca9844a7..c74c7417 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs @@ -29,7 +29,7 @@ partial class NewGroup public GroupScope Scope { get; set; } = GroupScope.Collection; } - [CmdletController(typeof(GraphGroup))] + [CmdletController(typeof(GraphGroup), Client=typeof(GraphHttpClient))] partial class NewGroupController { [Import] @@ -40,7 +40,6 @@ protected override IEnumerable Run() var group = Parameters.Get(nameof(NewGroup.Group)); var description = Parameters.Get(nameof(NewGroup.Description)); var scope = Parameters.Get(nameof(NewGroup.Scope)); - var client = Data.GetClient(); switch (scope) { @@ -57,7 +56,7 @@ protected override IEnumerable Run() { if(!PowerShell.ShouldProcess(Data.GetCollection(), $"Create group '{group}'")) yield break; - yield return client.CreateGroupAsync(new GraphGroupVstsCreationContext() { + yield return Client.CreateGroupAsync(new GraphGroupVstsCreationContext() { DisplayName = group, Description = description }).GetResult($"Error creating group '{group}' in collection"); @@ -71,7 +70,7 @@ protected override IEnumerable Run() if(!PowerShell.ShouldProcess(tp, $"Create group {group}")) yield break; - yield return client.CreateGroupAsync(new GraphGroupVstsCreationContext() { + yield return Client.CreateGroupAsync(new GraphGroupVstsCreationContext() { DisplayName = group, Description = description }, scopeDescriptor: descriptor.Value).GetResult($"Error creating group '{group}' in project '{tp.Name}'"); diff --git a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/RemoveGroup.cs b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/RemoveGroup.cs index fe1e5fc3..d1a04cd6 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/RemoveGroup.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/RemoveGroup.cs @@ -24,18 +24,16 @@ partial class RemoveGroup public GroupScope Scope { get; set; } = GroupScope.Collection; } - [CmdletController(typeof(GraphGroup))] + [CmdletController(typeof(GraphGroup), Client=typeof(IGraphHttpClient))] partial class RemoveGroupController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var group in Items) { if (!PowerShell.ShouldProcess(group.PrincipalName, "Remove group")) continue; - client.DeleteGroupAsync(group.Descriptor) + Client.DeleteGroupAsync(group.Descriptor) .Wait($"Error removing group '{group.PrincipalName}'"); } diff --git a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/RemoveGroupMember.cs b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/RemoveGroupMember.cs index a137ddf8..d3d14c09 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/RemoveGroupMember.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/RemoveGroupMember.cs @@ -22,7 +22,7 @@ partial class RemoveGroupMember public object Group { get; set; } } - [CmdletController] + [CmdletController(Client=typeof(IIdentityHttpClient))] partial class RemoveGroupMemberController { protected override IEnumerable Run() @@ -40,15 +40,13 @@ protected override IEnumerable Run() Identity = group }); - var client = Data.GetClient(); - Logger.Log($"Adding {m.IdentityType} '{m.DisplayName} ({m.UniqueName})' to group '{g.DisplayName}'"); if (!PowerShell.ShouldProcess($"[Group: {g.DisplayName}]/[Member: '{m.DisplayName} ({m.UniqueName})']", "Remove member")) return null; Logger.Log($"Removing '{m.DisplayName} ({m.UniqueName})' from group '{g.DisplayName}'"); - client.RemoveMemberFromGroupAsync(g.Descriptor, m.Descriptor) + Client.RemoveMemberFromGroupAsync(g.Descriptor, m.Descriptor) .GetResult($"Error removing '{m.DisplayName} ({m.UniqueName}))' from group '{g.DisplayName}'"); return null; diff --git a/CSharp/TfsCmdlets/Cmdlets/Identity/User/GetUser.cs b/CSharp/TfsCmdlets/Cmdlets/Identity/User/GetUser.cs index 1881710c..beba7530 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Identity/User/GetUser.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Identity/User/GetUser.cs @@ -28,13 +28,11 @@ partial class GetUser public SwitchParameter Current { get; set; } } - [CmdletController(typeof(AccountEntitlement))] + [CmdletController(typeof(AccountEntitlement), Client=typeof(HttpClients.IAccountLicensingHttpClient))] partial class GetUserController { protected override IEnumerable Run() { - var client = GetClient(); - if (Current) { var user = Data.GetItem(new { Identity = User }); @@ -60,22 +58,22 @@ protected override IEnumerable Run() } case WebApiIdentity i: { - yield return client.GetAccountEntitlementAsync(i.Id).GetResult("Error getting account entitlement"); + yield return Client.GetAccountEntitlementAsync(i.Id).GetResult("Error getting account entitlement"); break; } case WebApiIdentityRef ir: { - yield return client.GetAccountEntitlementAsync(ir.Id).GetResult("Error getting account entitlement"); + yield return Client.GetAccountEntitlementAsync(ir.Id).GetResult("Error getting account entitlement"); break; } case Guid g: { - yield return client.GetAccountEntitlementAsync(g).GetResult("Error getting account entitlement"); + yield return Client.GetAccountEntitlementAsync(g).GetResult("Error getting account entitlement"); break; } case string s: { - foreach (var u in client.GetAccountEntitlementsAsync() + foreach (var u in Client.GetAccountEntitlementsAsync() .GetResult("Error getting account entitlements") .Where(u => u.User.DisplayName.IsLike(s) || u.User.UniqueName.IsLike(s))) { diff --git a/CSharp/TfsCmdlets/Cmdlets/Identity/User/RemoveUser.cs b/CSharp/TfsCmdlets/Cmdlets/Identity/User/RemoveUser.cs index 3270f5d3..62db82bc 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Identity/User/RemoveUser.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Identity/User/RemoveUser.cs @@ -1,6 +1,7 @@ using System.Management.Automation; using Microsoft.VisualStudio.Services.Licensing; using Microsoft.VisualStudio.Services.Licensing.Client; +using IAccountLicensingHttpClient = TfsCmdlets.HttpClients.IAccountLicensingHttpClient; namespace TfsCmdlets.Cmdlets.Identity.User { @@ -24,20 +25,18 @@ partial class RemoveUser namespace TfsCmdlets.Controllers.Identity.User { - [CmdletController(typeof(AccountEntitlement))] + [CmdletController(typeof(AccountEntitlement), Client=typeof(IAccountLicensingHttpClient))] partial class RemoveUserController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var user in Items) { if (!PowerShell.ShouldProcess(Collection, $"Delete user '{user.User.UniqueName}' ('{user.User.DisplayName}')")) continue; try { - client.DeleteEntitlementAsync(Guid.Parse(user.User.Id)).GetAwaiter().GetResult(); + Client.DeleteEntitlementAsync(Guid.Parse(user.User.Id)).GetAwaiter().GetResult(); } catch (Exception ex) { diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/DisableBuildDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/DisableBuildDefinition.cs index f1a534a8..7b064b18 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/DisableBuildDefinition.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/DisableBuildDefinition.cs @@ -17,13 +17,12 @@ partial class DisableBuildDefinition public object Definition { get; set; } } - [CmdletController(typeof(BuildDefinitionReference))] + [CmdletController(typeof(BuildDefinitionReference), Client=typeof(IBuildHttpClient))] partial class DisableBuildDefinitionController { protected override IEnumerable Run() { var def = Data.GetItem(); - var client = Data.GetClient(); if (def.QueueStatus == DefinitionQueueStatus.Disabled) { @@ -44,7 +43,7 @@ protected override IEnumerable Run() Name = def.Name, }; - yield return client.UpdateDefinitionAsync(patch) + yield return Client.UpdateDefinitionAsync(patch) .GetResult($"Error updating build definition {def.GetFullPath()}"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/EnableBuildDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/EnableBuildDefinition.cs index c24c9128..d278dbd4 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/EnableBuildDefinition.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/EnableBuildDefinition.cs @@ -17,13 +17,12 @@ partial class EnableBuildDefinition public object Definition { get; set; } } - [CmdletController(typeof(BuildDefinitionReference))] + [CmdletController(typeof(BuildDefinitionReference), Client=typeof(IBuildHttpClient))] partial class EnableBuildDefinitionController { protected override IEnumerable Run() { var def = Data.GetItem(); - var client = Data.GetClient(); if (def.QueueStatus == DefinitionQueueStatus.Enabled) { @@ -50,7 +49,7 @@ protected override IEnumerable Run() Name = def.Name, }; - yield return client.UpdateDefinitionAsync(patch) + yield return Client.UpdateDefinitionAsync(patch) .GetResult($"Error updating build definition {def.GetFullPath()}"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/GetBuildDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/GetBuildDefinition.cs index b706ffbd..35173038 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/GetBuildDefinition.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/GetBuildDefinition.cs @@ -25,7 +25,7 @@ partial class GetBuildDefinition public DefinitionQueryOrder QueryOrder { get; set; } } - [CmdletController(typeof(BuildDefinitionReference))] + [CmdletController(typeof(BuildDefinitionReference), Client=typeof(IBuildHttpClient))] partial class GetBuildDefinitionController { [Import] @@ -33,7 +33,6 @@ partial class GetBuildDefinitionController protected override IEnumerable Run() { - var client = Data.GetClient(); var ids = new List(); foreach (var input in Definition) @@ -58,7 +57,7 @@ protected override IEnumerable Run() case string s: { var path = NodeUtil.NormalizeNodePath(s, includeLeadingSeparator: true); - var defs = client.GetDefinitionsAsync(project: Project.Name) + var defs = Client.GetDefinitionsAsync(project: (string) Project.Name, yamlFilename: null) .GetResult($"Error getting pipeline definitions matching '{s}'"); ids.AddRange(defs.Where(bd => bd.GetFullPath().IsLike(path) || bd.Name.IsLike(s)).Select(bd => bd.Id)); break; @@ -73,7 +72,7 @@ protected override IEnumerable Run() if(ids.Count == 0) yield break; - foreach (var def in client.GetFullDefinitionsAsync(project: Project.Name, definitionIds: ids, queryOrder: QueryOrder) + foreach (var def in Client.GetFullDefinitionsAsync(project: Project.Name, definitionIds: ids, queryOrder: QueryOrder, yamlFilename: null) .GetResult($"Error getting pipeline definitions")) { yield return def; diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/ResumeBuildDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/ResumeBuildDefinition.cs index 02831541..fe03de42 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/ResumeBuildDefinition.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/ResumeBuildDefinition.cs @@ -17,14 +17,13 @@ partial class ResumeBuildDefinition public object Definition { get; set; } } - [CmdletController(typeof(BuildDefinitionReference))] + [CmdletController(typeof(BuildDefinitionReference), Client=typeof(IBuildHttpClient))] partial class ResumeBuildDefinitionController { protected override IEnumerable Run() { var def = Data.GetItem(); - var client = Data.GetClient(); if (def.QueueStatus == DefinitionQueueStatus.Enabled) { @@ -51,7 +50,7 @@ protected override IEnumerable Run() Name = def.Name, }; - yield return client.UpdateDefinitionAsync(patch) + yield return Client.UpdateDefinitionAsync(patch) .GetResult($"Error updating build definition {def.GetFullPath()}"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/SuspendBuildDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/SuspendBuildDefinition.cs index eb71393e..ebfce7b7 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/SuspendBuildDefinition.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/SuspendBuildDefinition.cs @@ -17,13 +17,12 @@ partial class SuspendBuildDefinition public object Definition { get; set; } } - [CmdletController(typeof(BuildDefinitionReference))] + [CmdletController(typeof(BuildDefinitionReference), Client=typeof(IBuildHttpClient))] partial class SuspendBuildDefinitionController { protected override IEnumerable Run() { var def = Data.GetItem(); - var client = Data.GetClient(); if (def.QueueStatus == DefinitionQueueStatus.Paused) { @@ -50,7 +49,7 @@ protected override IEnumerable Run() Name = def.Name, }; - yield return client.UpdateDefinitionAsync(patch) + yield return Client.UpdateDefinitionAsync(patch) .GetResult($"Error updating build definition {def.GetFullPath()}"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/GetBuildDefinitionFolder.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/GetBuildDefinitionFolder.cs index 69dbacbb..65d97472 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/GetBuildDefinitionFolder.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/GetBuildDefinitionFolder.cs @@ -26,7 +26,7 @@ partial class GetBuildDefinitionFolder public Microsoft.TeamFoundation.Build.WebApi.FolderQueryOrder QueryOrder {get;set;} } - [CmdletController(typeof(WebApiFolder))] + [CmdletController(typeof(WebApiFolder), Client=typeof(IBuildHttpClient))] partial class GetBuildDefinitionFolderController { protected override IEnumerable Run() @@ -43,9 +43,8 @@ protected override IEnumerable Run() } case string s when s.IsWildcard(): { - var client = Data.GetClient(); var tp = Data.GetProject(); - var folders = client.GetFoldersAsync(tp.Name, null, queryOrder) + var folders = Client.GetFoldersAsync(tp.Name, null, queryOrder) .GetResult($"Error getting folders matching {s}"); foreach (var i in folders.Where(f => f.Path.IsLike(s) || GetFolderName(f).IsLike(s))) yield return i; @@ -54,9 +53,8 @@ protected override IEnumerable Run() } case string s: { - var client = Data.GetClient(); var tp = Data.GetProject(); - var f = client.GetFoldersAsync(tp.Name, $@"\{s.Trim('\\')}", queryOrder) + var f = Client.GetFoldersAsync(tp.Name, $@"\{s.Trim('\\')}", queryOrder) .GetResult($"Error getting folders matching {s}").FirstOrDefault(); if (f != null) yield return f; diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/NewBuildDefinitionFolder.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/NewBuildDefinitionFolder.cs index 47aeead4..1ad9d147 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/NewBuildDefinitionFolder.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/NewBuildDefinitionFolder.cs @@ -29,7 +29,7 @@ partial class NewBuildDefinitionFolder public string Description { get; set; } } - [CmdletController(typeof(WebApiFolder))] + [CmdletController(typeof(WebApiFolder), Client=typeof(IBuildHttpClient))] partial class NewBuildDefinitionFolderController { protected override IEnumerable Run() @@ -49,14 +49,12 @@ protected override IEnumerable Run() yield break; } - var client = Data.GetClient(); - var newFolder = new WebApiFolder() { Description = description }; - var result = client.CreateFolderAsync(newFolder, tp.Name, $@"\{folder}") + var result = Client.CreateFolderAsync(newFolder, tp.Name, $@"\{folder}") .GetResult($"Error creating folder '{folder}'"); yield return result; diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/RemoveBuildDefinitionFolder.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/RemoveBuildDefinitionFolder.cs index 2ba36159..a8235d68 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/RemoveBuildDefinitionFolder.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Folder/RemoveBuildDefinitionFolder.cs @@ -33,7 +33,7 @@ partial class RemoveBuildDefinitionFolder public SwitchParameter Force { get; set; } } - [CmdletController(typeof(WebApiFolder))] + [CmdletController(typeof(WebApiFolder), Client=typeof(IBuildHttpClient))] partial class RemoveBuildDefinitionFolderController { protected override IEnumerable Run() @@ -66,13 +66,12 @@ protected override IEnumerable Run() } var tp = Data.GetProject(); - var client = Data.GetClient(); if (!force) { Logger.Log($"Force argument not set. Check if folder '{f.Path}' has build definitions"); - var result = client.GetDefinitionsAsync2(tp.Name, null, null, null, DefinitionQueryOrder.None, null, null, null, null, f.Path) + var result = Client.GetDefinitionsAsync2(project: tp.Name, queryOrder: DefinitionQueryOrder.None, path: f.Path, yamlFilename: null) .GetResult($"Error fetching build definitions in folder '{f.Path}'").ToList(); if (result.Count > 0) @@ -81,7 +80,7 @@ protected override IEnumerable Run() } } - client.DeleteFolderAsync(tp.Name, f.Path).Wait(); + Client.DeleteFolderAsync(tp.Name, f.Path).Wait(); } return null; diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/StartBuild.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/StartBuild.cs index cfd2c89e..cf419e32 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/StartBuild.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/StartBuild.cs @@ -16,16 +16,14 @@ partial class StartBuild public object Definition { get; set; } } - [CmdletController(typeof(WebApiBuild))] + [CmdletController(typeof(WebApiBuild), Client = typeof(IBuildHttpClient))] partial class StartBuildController { protected override IEnumerable Run() { var definition = GetItem(); - var client = GetClient(); - - yield return client.QueueBuildAsync(project: Project.Name, build: new WebApiBuild + yield return Client.QueueBuildAsync(project: Project.Name, definitionId: null, build: new WebApiBuild { Definition = definition }).GetResult($"Error queuing build '{definition}'"); diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinition.cs index ad3322ee..14cae846 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinition.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinition.cs @@ -16,13 +16,11 @@ partial class GetReleaseDefinition public object Definition { get; set; } = "*"; } - [CmdletController(typeof(ReleaseDefinition))] + [CmdletController(typeof(ReleaseDefinition), Client = typeof(IReleaseHttpClient2))] partial class GetReleaseDefinitionController { protected override IEnumerable Run() { - var client = GetClient(); - foreach(var input in Definition) { var definition = input switch { @@ -35,12 +33,12 @@ protected override IEnumerable Run() yield return rd; break; case string s when s.IsWildcard(): - yield return client.GetReleaseDefinitionsAsync(Project.Name) + yield return Client.GetReleaseDefinitionsAsync(Project.Name) .GetResult($"Error getting release definition(s) '{s}'") .Where(r => r.Name.IsLike(s)); break; case string s: - yield return client.GetReleaseDefinitionsAsync(Project.Name, searchText: s) + yield return Client.GetReleaseDefinitionsAsync(Project.Name, searchText: s) .GetResult($"Error getting release definition '{s}'"); break; } diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinitionFolder.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinitionFolder.cs index 55088d57..3b84cc07 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinitionFolder.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinitionFolder.cs @@ -27,7 +27,7 @@ partial class GetReleaseDefinitionFolder public Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.FolderPathQueryOrder QueryOrder { get; set; } } - [CmdletController(typeof(WebApiFolder))] + [CmdletController(typeof(WebApiFolder), Client = typeof(IReleaseHttpClient))] partial class GetReleaseDefinitionFolderController { [Import] @@ -47,10 +47,9 @@ protected override IEnumerable Run() } case string s when s.IsWildcard(): { - var client = Data.GetClient(); var tp = Data.GetProject(); s = NodeUtil.NormalizeNodePath(s, tp.Name); - var folders = client.GetFoldersAsync(tp.Name, null, queryOrder) + var folders = Client.GetFoldersAsync(tp.Name, null, queryOrder) .GetResult($"Error getting folders matching {s}"); foreach (var i in folders @@ -63,9 +62,8 @@ protected override IEnumerable Run() } case string s: { - var client = Data.GetClient(); var tp = Data.GetProject(); - var f = client.GetFoldersAsync(tp.Name, NodeUtil.NormalizeNodePath(s, tp.Name), queryOrder) + var f = Client.GetFoldersAsync(tp.Name, NodeUtil.NormalizeNodePath(s, tp.Name), queryOrder) .GetResult($"Error getting folders matching {s}").FirstOrDefault(); if (f != null) yield return f; diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/NewReleaseDefinitionFolder.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/NewReleaseDefinitionFolder.cs index 6e1a3883..c495a549 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/NewReleaseDefinitionFolder.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/NewReleaseDefinitionFolder.cs @@ -25,7 +25,7 @@ partial class NewReleaseDefinitionFolder public string Description { get; set; } } - [CmdletController(typeof(WebApiFolder))] + [CmdletController(typeof(WebApiFolder), Client=typeof(IReleaseHttpClient))] partial class NewReleaseDefinitionFolderController { [Import] @@ -39,14 +39,13 @@ protected override IEnumerable Run() if (!PowerShell.ShouldProcess(tp, $"Create release folder '{folder}'")) yield break; - var client = Data.GetClient(); var newFolder = new WebApiFolder() { Description = description, Path = NodeUtil.NormalizeNodePath(folder, tp.Name) }; - yield return client.CreateFolderAsync(newFolder, tp.Name) + yield return Client.CreateFolderAsync(newFolder, tp.Name) .GetResult($"Error creating folder '{folder}'"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/RemoveReleaseDefinitionFolder.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/RemoveReleaseDefinitionFolder.cs index 47a1ae2a..f0720d99 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/RemoveReleaseDefinitionFolder.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/RemoveReleaseDefinitionFolder.cs @@ -32,7 +32,7 @@ partial class RemoveReleaseDefinitionFolder public SwitchParameter Force { get; set; } } - [CmdletController(typeof(WebApiFolder))] + [CmdletController(typeof(WebApiFolder), Client=typeof(IReleaseHttpClient))] partial class RemoveReleaseDefinitionFolderController { protected override IEnumerable Run() @@ -63,8 +63,7 @@ protected override IEnumerable Run() if (!force && !PowerShell.ShouldContinue($"Are you sure you want to delete folder '{f.Path}' and all of its contents?")) continue; - Data.GetClient() - .DeleteFolderAsync(tp.Name, f.Path) + Client.DeleteFolderAsync(tp.Name, f.Path) .Wait(); } diff --git a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/GetProcessTemplate.cs b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/GetProcessTemplate.cs index 25b1fb9e..dbf8d5d7 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/GetProcessTemplate.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/GetProcessTemplate.cs @@ -25,13 +25,11 @@ partial class GetProcessTemplate public SwitchParameter Default { get; set; } } - [CmdletController(typeof(WebApiProcess))] + [CmdletController(typeof(WebApiProcess), Client=typeof(IProcessHttpClient))] partial class GetProcessTemplateController { protected override IEnumerable Run() { - var client = Data.GetClient(); - foreach (var pt in ProcessTemplate) { var process = pt switch @@ -49,14 +47,14 @@ protected override IEnumerable Run() } case Guid g: { - yield return TaskExtensions.GetResult(client.GetProcessByIdAsync(g), $"Error getting process template '{process}'"); + yield return TaskExtensions.GetResult(Client.GetProcessByIdAsync(g), $"Error getting process template '{process}'"); yield break; } case null when Default: case { } when Default: { - foreach (var proc in TaskExtensions.GetResult>(client.GetProcessesAsync(), $"Error getting process templates") + foreach (var proc in TaskExtensions.GetResult>(Client.GetProcessesAsync(), $"Error getting process templates") .Where(p => p.IsDefault)) { yield return proc; @@ -66,7 +64,7 @@ protected override IEnumerable Run() } case string s: { - foreach (var proc in TaskExtensions.GetResult>(client.GetProcessesAsync(), $"Error getting process template '{process}'") + foreach (var proc in TaskExtensions.GetResult>(Client.GetProcessesAsync(), $"Error getting process template '{process}'") .Where(p => p.Name.IsLike(s))) { yield return proc; diff --git a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/NewProcessTemplate.cs b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/NewProcessTemplate.cs index 42a3eef5..f24e3d93 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/NewProcessTemplate.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/NewProcessTemplate.cs @@ -43,7 +43,7 @@ partial class NewProcessTemplate public SwitchParameter Force { get; set; } } - [CmdletController(typeof(WebApiProcess))] + [CmdletController(typeof(WebApiProcess), Client=typeof(IWorkItemTrackingProcessHttpClient))] partial class NewProcessTemplateController { protected override IEnumerable Run() @@ -55,11 +55,9 @@ protected override IEnumerable Run() if (exists && !(Force || PowerShell.ShouldContinue($"Are you sure you want to overwrite existing process '{ProcessTemplate}'?"))) yield break; - var client = Data.GetClient(); - var tmpProcessName = exists ? $"{ProcessTemplate}_{new Random().Next():X}" : ProcessTemplate; - client.CreateNewProcessAsync(new CreateProcessModel() { + Client.CreateNewProcessAsync(new CreateProcessModel() { Name = tmpProcessName, Description = Description, ParentProcessTypeId = parent.Id, diff --git a/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookConsumer.cs b/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookConsumer.cs index 044610d1..3a62ae19 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookConsumer.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookConsumer.cs @@ -27,7 +27,7 @@ partial class GetServiceHookConsumer public string Consumer { get; set; } = "*"; } - [CmdletController(typeof(WebApiConsumer))] + [CmdletController(typeof(WebApiConsumer), Client=typeof(ServiceHooksPublisherHttpClient))] partial class GetServiceHookConsumerController { protected override IEnumerable Run() @@ -43,8 +43,7 @@ protected override IEnumerable Run() } case string s: { - var client = GetClient(); - var result = client.GetConsumersAsync() + var result = Client.GetConsumersAsync() .GetResult("Error getting service hook consumers"); foreach (var shc in result.Where(c => c.Name.IsLike(s) || c.Id.IsLike(s))) diff --git a/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookNotificationHistory.cs b/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookNotificationHistory.cs index e58ffe0f..b7604295 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookNotificationHistory.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookNotificationHistory.cs @@ -36,13 +36,12 @@ partial class GetServiceHookNotificationHistory public NotificationStatus Status { get; set; } } - [CmdletController(typeof(Notification))] + [CmdletController(typeof(Notification), Client=typeof(IServiceHooksPublisherHttpClient))] partial class GetServiceHookNotificationHistoryController { protected override IEnumerable Run() { var ids = GetItems().Select(i => i.Id).ToArray(); - var client = Data.GetClient(); var query = new NotificationsQuery() { @@ -52,7 +51,7 @@ protected override IEnumerable Run() Status = Status }; - var result = client.QueryNotificationsAsync(query) + var result = Client.QueryNotificationsAsync(query) .GetResult("Error getting service hook notifications") .Results; diff --git a/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookPublisher.cs b/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookPublisher.cs index 48ab6d62..c2fe1f66 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookPublisher.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookPublisher.cs @@ -26,13 +26,11 @@ partial class GetServiceHookPublisher public object Publisher { get; set; } = "*"; } - [CmdletController(typeof(WebApiPublisher))] + [CmdletController(typeof(WebApiPublisher), Client=typeof(IServiceHooksPublisherHttpClient))] partial class GetServiceHookPublisherController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var publisher in Publisher) { switch (publisher) @@ -44,7 +42,7 @@ protected override IEnumerable Run() } case string s: { - var result = client.GetPublishersAsync() + var result = Client.GetPublishersAsync() .GetResult("Error getting service hook publishers") .Where(p => p.Name.IsLike(s) || p.Id.IsLike(s)); diff --git a/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookSubscription.cs b/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookSubscription.cs index 4cb9d537..12af27b0 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookSubscription.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookSubscription.cs @@ -43,12 +43,11 @@ partial class GetServiceHookSubscription public string EventType { get; set; } } - [CmdletController(typeof(WebApiSubscription))] + [CmdletController(typeof(WebApiSubscription), Client=typeof(IServiceHooksPublisherHttpClient))] partial class GetServiceHookSubscriptionController { protected override IEnumerable Run() { - var client = GetClient(); var publisher = Has_Publisher ? GetItem() : null; var consumer = Has_Consumer ? GetItem() : null; @@ -70,7 +69,7 @@ protected override IEnumerable Run() EventType = EventType }; - var result = client.QuerySubscriptionsAsync(query) + var result = Client.QuerySubscriptionsAsync(query) .GetResult("Error getting service hook subscriptions") .Results .Where(sub => sub.ActionDescription.IsLike(s)); diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/Backlog/GetTeamBacklogLevel.cs b/CSharp/TfsCmdlets/Cmdlets/Team/Backlog/GetTeamBacklogLevel.cs index b13d3bb9..6d3a24b0 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/Backlog/GetTeamBacklogLevel.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/Backlog/GetTeamBacklogLevel.cs @@ -14,7 +14,7 @@ partial class GetTeamBacklogLevel /// /// Specifies one or more backlog level configurations to be returned. Valid values /// are the name (e.g. "Stories") or the ID (e.g. "Microsoft.RequirementCategory") of the - /// backlog level to return. Wilcards are supported. When omitted, returns all backlogs + /// backlog level to return. Wildcards are supported. When omitted, returns all backlogs /// levels of the given team. /// [Parameter(Position = 0)] @@ -23,7 +23,7 @@ partial class GetTeamBacklogLevel public object Backlog { get; set; } = "*"; } - [CmdletController(typeof(Models.BacklogLevelConfiguration))] + [CmdletController(typeof(Models.BacklogLevelConfiguration), Client=typeof(IWorkHttpClient))] partial class GetTeamBacklogLevelController { protected override IEnumerable Run() @@ -41,10 +41,9 @@ protected override IEnumerable Run() } case string s: { - var client = Data.GetClient(); var ctx = new TeamContext(tp.Name, t.Name); - var result = client.GetBacklogsAsync(ctx) + var result = Client.GetBacklogsAsync(ctx) .GetResult($"Error getting backlogs") .Where(b => b.Name.IsLike(s) || b.Id.IsLike(s)) .OrderByDescending(b => b.Rank); diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/Board/GetTeamBoard.cs b/CSharp/TfsCmdlets/Cmdlets/Team/Board/GetTeamBoard.cs index 423f60d2..ae1e72e9 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/Board/GetTeamBoard.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/Board/GetTeamBoard.cs @@ -20,7 +20,7 @@ partial class GetTeamBoard public object Board { get; set; } = "*"; } - [CmdletController(typeof(Models.Board))] + [CmdletController(typeof(Models.Board), Client=typeof(IWorkHttpClient))] partial class GetTeamBoardController { protected override IEnumerable Run() @@ -42,12 +42,11 @@ protected override IEnumerable Run() case string s when !s.IsGuid(): { var ctx = new TeamContext(tp.Name, t.Name); - var client = Data.GetClient(); - foreach (var b in TaskExtensions.GetResult>(client.GetBoardsAsync(ctx), "Error getting team boards") + foreach (var b in TaskExtensions.GetResult>(Client.GetBoardsAsync(ctx), "Error getting team boards") .Where(b => b.Name.IsLike(s))) { - yield return new Models.Board(TaskExtensions.GetResult(client.GetBoardAsync(ctx, b.Id.ToString()), $"Error getting board '{b.Name}'"), tp.Name, t.Name); + yield return new Models.Board(TaskExtensions.GetResult(Client.GetBoardAsync(ctx, b.Id.ToString()), $"Error getting board '{b.Name}'"), tp.Name, t.Name); } yield break; @@ -55,9 +54,8 @@ protected override IEnumerable Run() case string s: { var ctx = new TeamContext(tp.Name, t.Name); - var client = Data.GetClient(); - yield return new Models.Board(TaskExtensions.GetResult(client.GetBoardAsync(ctx, s), $"Error getting board 's'"), tp.Name, t.Name); + yield return new Models.Board(TaskExtensions.GetResult(Client.GetBoardAsync(ctx, s), $"Error getting board 's'"), tp.Name, t.Name); yield break; } diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/Board/GetTeamBoardCardRule.cs b/CSharp/TfsCmdlets/Cmdlets/Team/Board/GetTeamBoardCardRule.cs index f8e820f2..9368ce70 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/Board/GetTeamBoardCardRule.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/Board/GetTeamBoardCardRule.cs @@ -33,7 +33,7 @@ partial class GetTeamBoardCardRule public object Board { get; set; } } - [CmdletController(typeof(Models.CardRule))] + [CmdletController(typeof(Models.CardRule), Client=typeof(IWorkHttpClient))] partial class GetTeamBoardCardRuleController { protected override IEnumerable Run() @@ -47,9 +47,8 @@ protected override IEnumerable Run() var ruleType = Parameters.Get(nameof(GetTeamBoardCardRule.RuleType)); var ctx = new TeamContext(tp.Name, t.Name); - var client = Data.GetClient(); - var rules = TaskExtensions.GetResult(client.GetBoardCardRuleSettingsAsync(ctx, board.Name), "Error getting board card rules") + var rules = TaskExtensions.GetResult(Client.GetBoardCardRuleSettingsAsync(ctx, board.Name), "Error getting board card rules") .rules; // Card rules diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/GetTeam.cs b/CSharp/TfsCmdlets/Cmdlets/Team/GetTeam.cs index b0d802a5..a60ef8b0 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/GetTeam.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/GetTeam.cs @@ -66,7 +66,7 @@ partial class GetTeam public SwitchParameter Default { get; set; } } - [CmdletController(typeof(Models.Team))] + [CmdletController(typeof(Models.Team), Client=typeof(ITeamHttpClient))] partial class GetTeamController { [Import] @@ -75,10 +75,14 @@ partial class GetTeamController [Import] private IPaginator Paginator { get; } + [Import] + private IProjectHttpClient ProjectClient { get; set; } + + [Import] + private IWorkHttpClient WorkClient { get; set; } + protected override IEnumerable Run() { - var client = Data.GetClient(); - foreach (var input in Team) { var team = input switch @@ -92,8 +96,7 @@ protected override IEnumerable Run() { Logger.Log("Get default team"); - var projectClient = Data.GetClient(); - var props = projectClient + var props = ProjectClient .GetProjectPropertiesAsync(Project.Id) .GetResult("Error retrieving project's default team"); team = new Guid((string)props.Where(p => p.Name.Equals("System.Microsoft.TeamFoundation.Team.Default")) @@ -126,14 +129,14 @@ protected override IEnumerable Run() } case Guid g: { - var result = client.GetTeamAsync(Project.Name, g.ToString()) + var result = Client.GetTeamAsync(Project.Name, g.ToString()) .GetResult($"Error getting team '{g}'"); yield return CreateTeamObject(result); yield break; } case string s when !s.IsWildcard(): { - var result = client.GetTeamAsync(Project.Name, s) + var result = Client.GetTeamAsync(Project.Name, s) .GetResult($"Error getting team '{s}'"); yield return CreateTeamObject(result); yield break; @@ -142,7 +145,7 @@ protected override IEnumerable Run() { foreach (var result in Paginator.Paginate( - (top, skip) => client.GetTeamsAsync(Project.Name, top: top, skip: skip).GetResult($"Error getting team(s) '{s}'")) + (top, skip) => Client.GetTeamsAsync(Project.Name, top: top, skip: skip).GetResult($"Error getting team(s) '{s}'")) .Where(t => t.Name.IsLike(s))) { yield return CreateTeamObject(result); @@ -164,28 +167,25 @@ private Models.Team CreateTeamObject(WebApiTeam innerTeam) if (IncludeMembers) { - var client = GetClient(); - Logger.Log($"Retrieving team membership information for team '{team.Name}'"); - team.TeamMembers = client.GetTeamMembersWithExtendedPropertiesAsync(team.ProjectName, team.Name) + team.TeamMembers = Client.GetTeamMembersWithExtendedPropertiesAsync(team.ProjectName, team.Name) .GetResult($"Error retrieving membership information for team {team.Name}"); } if (IncludeSettings) { - var workClient = GetClient(); var ctx = new TeamContext(team.ProjectName, team.Name); Logger.Log($"Retrieving team settings for team '{team.Name}'"); - team.Settings = workClient.GetTeamSettingsAsync(ctx) + team.Settings = WorkClient.GetTeamSettingsAsync(ctx) .GetResult($"Error retrieving settings for team {team.Name}"); Logger.Log($"Retrieving default team field (area path) for team '{team.Name}'"); - team.TeamField = workClient.GetTeamFieldValuesAsync(ctx) + team.TeamField = WorkClient.GetTeamFieldValuesAsync(ctx) .GetResult($"Error retrieving team field values for team {team.Name}"); Logger.Log($"Retrieving iterations for team '{team.Name}'"); - team.IterationPaths = workClient.GetTeamIterationsAsync(ctx) + team.IterationPaths = WorkClient.GetTeamIterationsAsync(ctx) .GetResult("Error getting team's current iterations"); } diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/NewTeam.cs b/CSharp/TfsCmdlets/Cmdlets/Team/NewTeam.cs index d68338da..9bc440f2 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/NewTeam.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/NewTeam.cs @@ -78,16 +78,14 @@ partial class NewTeam public string Description { get; set; } } - [CmdletController(typeof(Models.Team))] + [CmdletController(typeof(Models.Team), Client=typeof(ITeamHttpClient))] partial class NewTeamController { protected override IEnumerable Run() { if (!PowerShell.ShouldProcess(Project, $"Create team {Team}")) yield break; - var client = GetClient(); - - var t = client.CreateTeamAsync(new WebApiTeam() + var t = Client.CreateTeamAsync(new WebApiTeam() { Name = Team, Description = Description, diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/RemoveTeam.cs b/CSharp/TfsCmdlets/Cmdlets/Team/RemoveTeam.cs index b734ac7b..c18fbd57 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/RemoveTeam.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/RemoveTeam.cs @@ -18,18 +18,16 @@ partial class RemoveTeam public object Team { get; set; } } - [CmdletController(typeof(Models.Team))] + [CmdletController(typeof(Models.Team), Client=typeof(TeamHttpClient))] partial class RemoveTeamController { protected override IEnumerable Run() { - var client = Data.GetClient(); - foreach (var team in Items) { if (!PowerShell.ShouldProcess($"[Project: {team.ProjectName}]/[Team: {team.Name}]", $"Delete team")) continue; - client.DeleteTeamAsync(team.ProjectName, team.Name) + Client.DeleteTeamAsync(team.ProjectName, team.Name) .Wait($"Error deleting team {team.Name}"); } diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/RenameTeam.cs b/CSharp/TfsCmdlets/Cmdlets/Team/RenameTeam.cs index 40d1b453..0b956a3c 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/RenameTeam.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/RenameTeam.cs @@ -17,18 +17,16 @@ partial class RenameTeam public object Team { get; set; } } - [CmdletController(typeof(Models.Team))] + [CmdletController(typeof(Models.Team), Client=typeof(ITeamHttpClient))] partial class RenameTeamController { protected override IEnumerable Run() { - var client = Data.GetClient(); - var team = Data.GetItem(); if (!PowerShell.ShouldProcess(Project, $"Rename team '{team.Name}' to '{NewName}'")) yield break; - yield return client.UpdateTeamAsync(new WebApiTeam() { Name = NewName }, Project.Id.ToString(), team.Id.ToString()) + yield return Client.UpdateTeamAsync(new WebApiTeam() { Name = NewName }, Project.Id.ToString(), team.Id.ToString()) .GetResult($"Error renaming team '{team.Name}' to '{NewName}'"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/SetTeam.cs b/CSharp/TfsCmdlets/Cmdlets/Team/SetTeam.cs index 3975e5b6..60a6eb01 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/SetTeam.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/SetTeam.cs @@ -110,7 +110,7 @@ partial class SetTeam public SwitchParameter Force { get; set; } } - [CmdletController(typeof(Models.Team))] + [CmdletController(typeof(Models.Team), Client=typeof(ITeamHttpClient))] partial class SetTeamController { [Import] @@ -119,17 +119,15 @@ partial class SetTeamController [Import] private INodeUtil NodeUtil { get; set; } + [Import] + private IWorkHttpClient WorkClient { get; set; } + protected override IEnumerable Run() { var t = Data.GetTeam(includeSettings: true); var backlogVisibilities = (BacklogVisibilities ?? new Hashtable()).ToDictionary(); var workingDays = (WorkingDays ?? Enumerable.Empty()).ToList(); - - var teamClient = GetClient(); - var projectClient = GetClient(); - var workClient = GetClient(); - var ctx = new TeamContext(Project.Name, t.Name); var teamFieldPatch = new TeamFieldValuesPatch(); @@ -167,7 +165,7 @@ protected override IEnumerable Run() if (Has_Description && !t.Description.Equals(Description) && PowerShell.ShouldProcess(t, $"Set team's description to '{Description}'")) { - t = teamClient.UpdateTeamAsync(new WebApiTeam() + t = Client.UpdateTeamAsync(new WebApiTeam() { Description = Description ?? string.Empty }, Project.Id.ToString(), t.Id.ToString()) @@ -287,7 +285,7 @@ protected override IEnumerable Run() if (isDirty) { - workClient.UpdateTeamFieldValuesAsync(teamFieldPatch, ctx) + WorkClient.UpdateTeamFieldValuesAsync(teamFieldPatch, ctx) .GetResult("Error applying team field value and/or area path settings"); } @@ -347,7 +345,7 @@ protected override IEnumerable Run() if (isDirty) { - workClient.UpdateTeamSettingsAsync(patch, ctx) + WorkClient.UpdateTeamSettingsAsync(patch, ctx) .GetResult("Error applying iteration settings"); } @@ -355,7 +353,7 @@ protected override IEnumerable Run() if (Parameters.HasParameter(nameof(SetTeam.IterationPaths)) && PowerShell.ShouldProcess(t, $"Set the team's iteration paths to '{IterationPaths.ToJsonString()}'")) { - var currentIterations = workClient.GetTeamIterationsAsync(ctx) + var currentIterations = WorkClient.GetTeamIterationsAsync(ctx) .GetResult("Error getting team's current iterations"); var newIterations = IterationPaths @@ -366,7 +364,7 @@ protected override IEnumerable Run() foreach (var iteration in newIterations) { Logger.Log($"Adding iteration '{iteration.Name}'"); - workClient.PostTeamIterationAsync(iteration, ctx) + WorkClient.PostTeamIterationAsync(iteration, ctx) .GetResult($"Error setting iteration {iteration.Id} as a sprint iteration"); } @@ -374,7 +372,7 @@ protected override IEnumerable Run() { foreach (var iteration in currentIterations.Where(i => !newIterations.Any(ni => ni.Id == i.Id))) { - workClient.DeleteTeamIterationAsync(ctx, iteration.Id) + WorkClient.DeleteTeamIterationAsync(ctx, iteration.Id) .Wait($"Error removing iteration '{iteration.Id}' from the team's sprint iterations"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/AddTeamAdmin.cs b/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/AddTeamAdmin.cs index 1929bd63..9dcd602a 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/AddTeamAdmin.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/AddTeamAdmin.cs @@ -1,5 +1,5 @@ using System.Management.Automation; -using TfsCmdlets.HttpClient; +using TfsCmdlets.HttpClients; namespace TfsCmdlets.Cmdlets.Team.TeamAdmin { @@ -17,7 +17,7 @@ partial class AddTeamAdmin public object Admin { get; set; } } - [CmdletController(typeof(Models.TeamAdmin))] + [CmdletController(typeof(Models.TeamAdmin), Client=typeof(ITeamAdminHttpClient))] partial class AddTeamAdminController { protected override IEnumerable Run() @@ -39,8 +39,7 @@ protected override IEnumerable Run() if (!PowerShell.ShouldProcess(team, $"Add team administrator(s) {string.Join(", ", uniqueNames)}")) yield break; - var client = Data.GetClient(); - var result = client.AddTeamAdmin(team.ProjectId, team.Id, ids); + var result = Client.AddTeamAdmin(team.ProjectId, team.Id, ids); foreach (var addedAdmin in result) { diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/RemoveTeamAdmin.cs b/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/RemoveTeamAdmin.cs index 5f628a6e..85933d07 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/RemoveTeamAdmin.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/RemoveTeamAdmin.cs @@ -1,5 +1,5 @@ using System.Management.Automation; -using TfsCmdlets.HttpClient; +using TfsCmdlets.HttpClients; namespace TfsCmdlets.Cmdlets.Team.TeamAdmin { @@ -17,7 +17,7 @@ partial class RemoveTeamAdmin public object Admin { get; set; } } - [CmdletController(typeof(Models.TeamAdmin))] + [CmdletController(typeof(Models.TeamAdmin), Client=typeof(TeamAdminHttpClient))] partial class RemoveTeamAdminController { protected override IEnumerable Run() @@ -33,9 +33,7 @@ protected override IEnumerable Run() if (!PowerShell.ShouldProcess($"Team '{t.Name}'", $"Remove administrator '{adminIdentity.DisplayName} ({adminIdentity.UniqueName})'")) return null; - var client = Data.GetClient(); - - if (!client.RemoveTeamAdmin(t.ProjectName, t.Id, adminIdentity.Id)) + if (!Client.RemoveTeamAdmin(t.ProjectName, t.Id, adminIdentity.Id)) { throw new Exception($"Error removing team administrator '{admin}'"); } diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/TeamMember/AddTeamMember.cs b/CSharp/TfsCmdlets/Cmdlets/Team/TeamMember/AddTeamMember.cs index 0d6bead9..b15a7c5d 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/TeamMember/AddTeamMember.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/TeamMember/AddTeamMember.cs @@ -1,5 +1,5 @@ using System.Management.Automation; -using TfsCmdlets.HttpClient; +using TfsCmdlets.HttpClients; namespace TfsCmdlets.Cmdlets.Team.TeamMember { diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProject/Avatar/ImportTeamProjectAvatar.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProject/Avatar/ImportTeamProjectAvatar.cs index 3721bd25..cfb4c344 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProject/Avatar/ImportTeamProjectAvatar.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProject/Avatar/ImportTeamProjectAvatar.cs @@ -16,7 +16,7 @@ partial class ImportTeamProjectAvatar public string Path { get; set; } } - [CmdletController] + [CmdletController(Client=typeof(IProjectHttpClient))] partial class ImportTeamProjectAvatarController { protected override IEnumerable Run() @@ -31,8 +31,6 @@ protected override IEnumerable Run() if (!File.Exists(path)){ throw new ArgumentException($"Invalid avatar image path '{path}'");} - var client = Data.GetClient(); - var projectAvatar = new ProjectAvatar { Image = File.ReadAllBytes(path) @@ -40,7 +38,7 @@ protected override IEnumerable Run() Logger.Log($"Importing '{path}' and using it as avatar image for team project '{tp.Name}'"); - client.SetProjectAvatarAsync(projectAvatar, tp.Name) + Client.SetProjectAvatarAsync(projectAvatar, tp.Name) .Wait("Error import team project avatar image"); return null; diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProject/Avatar/RemoveTeamProjectAvatar.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProject/Avatar/RemoveTeamProjectAvatar.cs index 230fca51..91c69c3d 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProject/Avatar/RemoveTeamProjectAvatar.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProject/Avatar/RemoveTeamProjectAvatar.cs @@ -15,20 +15,18 @@ partial class RemoveTeamProjectAvatar public object Project { get; set; } } - [CmdletController] + [CmdletController(Client=typeof(IProjectHttpClient))] partial class RemoveTeamProjectAvatarController { protected override IEnumerable Run() { - var client = Data.GetClient(); - foreach (var tp in Data.GetItems(new { Project = Parameters.Get(nameof(Project)) })) { if (!PowerShell.ShouldProcess($"[Project: {tp.Name}]", $"Remove custom team project avatar")) continue; Logger.Log($"Resetting team project avatar image to default"); - client.RemoveProjectAvatarAsync(tp.Name) + Client.RemoveProjectAvatarAsync(tp.Name) .Wait("Error removing project avatar"); } return null; diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProject/GetTeamProject.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProject/GetTeamProject.cs index cf7e5bf6..21847c41 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProject/GetTeamProject.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProject/GetTeamProject.cs @@ -60,7 +60,7 @@ partial class GetTeamProject public SwitchParameter Current { get; set; } } - [CmdletController(typeof(WebApiTeamProject))] + [CmdletController(typeof(WebApiTeamProject), Client = typeof(IProjectHttpClient))] partial class GetTeamProjectController { [Import] @@ -71,14 +71,12 @@ partial class GetTeamProjectController protected override IEnumerable Run() { - var client = GetClient(); - if (Parameters.Get("Project") == null || Current) { Logger.Log("Get currently connected team project"); yield return IncludeDetails ? - FetchProject(CurrentConnections.Project.Name, client, true) : + FetchProject(CurrentConnections.Project.Name, Client, true) : CurrentConnections.Project; yield break; @@ -97,7 +95,7 @@ protected override IEnumerable Run() } case Guid g: { - var p = FetchProject(g.ToString(), client, includeDetails); + var p = FetchProject(g.ToString(), Client, includeDetails); if(Has_Process && !Process.Any(process => p.Capabilities["processTemplate"]["templateName"].IsLike(process))) continue; @@ -107,7 +105,7 @@ protected override IEnumerable Run() } case string s when !s.IsWildcard() && !Deleted: { - var p = FetchProject(s, client, includeDetails); + var p = FetchProject(s, Client, includeDetails); if(Has_Process && !Process.Any(process => p.Capabilities["processTemplate"]["templateName"].IsLike(process))) continue; @@ -118,13 +116,13 @@ protected override IEnumerable Run() case string s: { var stateFilter = Deleted ? ProjectState.Deleted : ProjectState.All; - var tpRefs = FetchProjects(stateFilter, client); + var tpRefs = FetchProjects(stateFilter, Client); foreach (var tpRef in tpRefs.Where(r => r.Name.IsLike(s))) { var p = Deleted || !includeDetails? new WebApiTeamProject(tpRef) : - FetchProject(tpRef.Id.ToString(), client, true); + FetchProject(tpRef.Id.ToString(), Client, true); if (p == null) continue; @@ -143,10 +141,10 @@ protected override IEnumerable Run() } } - private WebApiTeamProject FetchProject(string project, ProjectHttpClient client, bool includeDetails = true) + private WebApiTeamProject FetchProject(string project, IProjectHttpClient client, bool includeDetails = true) => client.GetProject(project, includeDetails).GetResult($"Error getting team project '{project}'"); - private IEnumerable FetchProjects(ProjectState stateFilter, ProjectHttpClient client) + private IEnumerable FetchProjects(ProjectState stateFilter, IProjectHttpClient client) => Paginator.Paginate((top, skip) => client.GetProjects(stateFilter, top: top, skip: skip).GetResult($"Error getting team project(s)")); } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProject/NewTeamProject.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProject/NewTeamProject.cs index 1614b53f..c19c587d 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProject/NewTeamProject.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProject/NewTeamProject.cs @@ -41,7 +41,7 @@ partial class NewTeamProject public object ProcessTemplate { get; set; } } - [CmdletController(typeof(WebApiTeamProject))] + [CmdletController(typeof(WebApiTeamProject), Client=typeof(IProjectHttpClient))] partial class NewTeamProjectController { [Import] @@ -49,8 +49,6 @@ partial class NewTeamProjectController protected override IEnumerable Run() { - var client = GetClient(); - foreach (var project in Project) { if (!PowerShell.ShouldProcess(Collection, $"Create team project '{project}'")) continue; @@ -82,7 +80,7 @@ protected override IEnumerable Run() // Trigger the project creation - var result = AsyncAwaiter.Wait(client.QueueCreateProject(tpInfo), "Error queueing project creation"); + var result = AsyncAwaiter.Wait(Client.QueueCreateProject(tpInfo), "Error queueing project creation"); if (result.Status != OperationStatus.Succeeded) { diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProject/RemoveTeamProject.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProject/RemoveTeamProject.cs index 55731560..02ea4c34 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProject/RemoveTeamProject.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProject/RemoveTeamProject.cs @@ -32,9 +32,12 @@ partial class RemoveTeamProject public SwitchParameter Force { get; set; } } - [CmdletController(typeof(WebApiTeamProject))] + [CmdletController(typeof(WebApiTeamProject), Client=typeof(IProjectHttpClient))] partial class RemoveTeamProjectController { + [Import] + private IOperationsHttpClient OperationsClient { get; set; } + protected override IEnumerable Run() { var tpc = Data.GetCollection(); @@ -42,8 +45,6 @@ protected override IEnumerable Run() var hard = Parameters.Get(nameof(RemoveTeamProject.Hard)); var force = Parameters.Get(nameof(RemoveTeamProject.Force)); - var client = Data.GetClient(); - foreach (var tp in tps) { if (!PowerShell.ShouldProcess($"[Organization: {tpc.DisplayName}]/[Project: {tp.Name}]", "Delete team project")) continue; @@ -58,12 +59,11 @@ protected override IEnumerable Run() Logger.Log($"{method}-deleting team project {tp.Name}"); - var token = client.QueueDeleteProject(tp.Id, hard).GetResult($"Error queueing team project deletion"); + var token = Client.QueueDeleteProject(tp.Id, hard).GetResult($"Error queueing team project deletion"); // Wait for the operation to complete - var opsClient = Data.GetClient(); - var opsToken = opsClient.GetOperation(token.Id).GetResult("Error getting operation status"); + var opsToken = OperationsClient.GetOperation(token.Id).GetResult("Error getting operation status"); while ( (opsToken.Status != OperationStatus.Succeeded) && @@ -71,7 +71,7 @@ protected override IEnumerable Run() (opsToken.Status != OperationStatus.Cancelled)) { Thread.Sleep(2); - opsToken = opsClient.GetOperation(token.Id).GetResult("Error getting operation status"); + opsToken = OperationsClient.GetOperation(token.Id).GetResult("Error getting operation status"); } if (opsToken.Status != OperationStatus.Succeeded) diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProject/RenameTeamProject.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProject/RenameTeamProject.cs index d6d38dd4..9793a9d4 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProject/RenameTeamProject.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProject/RenameTeamProject.cs @@ -24,7 +24,7 @@ partial class RenameTeamProject public SwitchParameter Force { get; set; } } - [CmdletController(typeof(WebApiTeamProject))] + [CmdletController(typeof(WebApiTeamProject), Client=typeof(IOperationsHttpClient))] partial class RenameTeamProjectController { @@ -55,8 +55,7 @@ protected override IEnumerable Run() // Wait for the operation to complete - var opsClient = Data.GetClient(); - var opsToken = opsClient.GetOperation(token.Id) + var opsToken = Client.GetOperation(token.Id) .GetResult("Error getting operation status"); while ( @@ -65,7 +64,7 @@ protected override IEnumerable Run() (opsToken.Status != OperationStatus.Cancelled)) { Thread.Sleep(2); - opsToken = opsClient.GetOperation(token.Id) + opsToken = Client.GetOperation(token.Id) .GetResult("Error getting operation status"); } diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProject/SetTeamProject.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProject/SetTeamProject.cs index 735c50a6..1e562317 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProject/SetTeamProject.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProject/SetTeamProject.cs @@ -31,7 +31,7 @@ partial class SetTeamProject public string AvatarImage { get; set; } } - [CmdletController(typeof(WebApiTeamProject))] + [CmdletController(typeof(WebApiTeamProject), Client=typeof(IProjectHttpClient))] partial class SetTeamProjectController { [Import] @@ -58,11 +58,10 @@ protected override IEnumerable Run() if (Has_Description) { - var client = GetClient(); var tp = Data.GetProject(); var tpInfo = new WebApiTeamProject() { Description = Description }; - var result = AsyncAwaiter.Wait(client.UpdateProject(tp.Id, tpInfo), "Error updating team project description"); + var result = AsyncAwaiter.Wait(Client.UpdateProject(tp.Id, tpInfo), "Error updating team project description"); if (result.Status != OperationStatus.Succeeded) { diff --git a/CSharp/TfsCmdlets/Cmdlets/TestManagement/CopyTestPlan.cs b/CSharp/TfsCmdlets/Cmdlets/TestManagement/CopyTestPlan.cs index f4a7b27b..0ee81544 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TestManagement/CopyTestPlan.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TestManagement/CopyTestPlan.cs @@ -118,7 +118,7 @@ partial class CopyTestPlan public string Passthru { get; set; } = "None"; } - [CmdletController(typeof(TestPlan))] + [CmdletController(typeof(TestPlan), Client=typeof(ITestPlanHttpClient))] partial class CopyTestPlanController { protected override IEnumerable Run() @@ -138,8 +138,6 @@ protected override IEnumerable Run() var destinationWorkItemType = Parameters.Get(nameof(CopyTestPlan.DestinationWorkItemType)); var cloneRequirements = Parameters.Get(nameof(CopyTestPlan.CloneRequirements)); - var client = Data.GetClient(); - var cloneParams = new CloneTestPlanParams() { sourceTestPlan = new SourceTestPlanInfo() @@ -168,7 +166,7 @@ protected override IEnumerable Run() } }; - var result = client.CloneTestPlanAsync(cloneParams, tp.Name, deepClone) + var result = Client.CloneTestPlanAsync(cloneParams, tp.Name, deepClone) .GetResult($"Error cloning test plan '{plan.Name}' to '{destTp.Name}'"); var opInfo = result; @@ -176,7 +174,7 @@ protected override IEnumerable Run() do { Thread.Sleep(5000); - opInfo = client.GetCloneInformationAsync(tp.Name, opInfo.cloneOperationResponse.opId) + opInfo = Client.GetCloneInformationAsync(tp.Name, opInfo.cloneOperationResponse.opId) .GetResult($"Error getting operation status"); } while (opInfo.cloneOperationResponse.state.Equals("Queued") || opInfo.cloneOperationResponse.state.Equals("InProgress")); @@ -191,7 +189,7 @@ protected override IEnumerable Run() } } - [CmdletController(typeof(TestPlan))] + [CmdletController(typeof(TestPlan), Client=typeof(ITestPlanHttpClient))] partial class GetTestPlanController { protected override IEnumerable Run() @@ -211,16 +209,14 @@ protected override IEnumerable Run() case int i: { var tp = Data.GetProject(); - var client = Data.GetClient(); - yield return client.GetTestPlanByIdAsync(tp.Id, i) + yield return Client.GetTestPlanByIdAsync(tp.Id, i) .GetResult($"Error getting test plan '{i}'"); yield break; } case string s: { var tp = Data.GetProject(); - var client = Data.GetClient(); - foreach (var plan in client.GetTestPlansAsync(tp.Id, owner, null, planDetails, active) + foreach (var plan in Client.GetTestPlansAsync(tp.Id, owner, null, planDetails, active) .GetResult($"Error getting test plans '{testPlan}'") .Where(plan => plan.Name.IsLike(s))) { diff --git a/CSharp/TfsCmdlets/Cmdlets/TestManagement/NewTestPlan.cs b/CSharp/TfsCmdlets/Cmdlets/TestManagement/NewTestPlan.cs index 45edebce..7f900bf3 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TestManagement/NewTestPlan.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TestManagement/NewTestPlan.cs @@ -47,7 +47,7 @@ partial class NewTestPlan public object Owner { get; set; } } - [CmdletController(typeof(TestPlan))] + [CmdletController(typeof(TestPlan), Client=typeof(ITestPlanHttpClient))] partial class NewTestPlanController { [Import] @@ -73,9 +73,7 @@ protected override IEnumerable Run() var tp = Data.GetProject(); - var client = Data.GetClient(); - - yield return client.CreateTestPlanAsync(new TestPlanCreateParams() + yield return Client.CreateTestPlanAsync(new TestPlanCreateParams() { AreaPath = NodeUtil.NormalizeNodePath(areaPath, tp.Name, "Areas", includeTeamProject: true), Iteration = NodeUtil.NormalizeNodePath(iterationPath, tp.Name, "Iterations", includeTeamProject: true), diff --git a/CSharp/TfsCmdlets/Cmdlets/TestManagement/RemoveTestPlan.cs b/CSharp/TfsCmdlets/Cmdlets/TestManagement/RemoveTestPlan.cs index 59081b19..682b51c4 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TestManagement/RemoveTestPlan.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TestManagement/RemoveTestPlan.cs @@ -25,27 +25,26 @@ partial class RemoveTestPlan public SwitchParameter Force { get; set; } } - [CmdletController(typeof(TestPlan))] + [CmdletController(typeof(TestPlan), Client=typeof(ITestPlanHttpClient))] partial class RemoveTestPlanController { protected override IEnumerable Run() { var plans = Data.GetItems(); var force = Parameters.Get(nameof(RemoveTestPlan.Force)); - var client = Data.GetClient(); foreach (var plan in plans) { if (!PowerShell.ShouldProcess($"[Project: {plan.Project.Name}]/[Plan: #{plan.Id} ({plan.Name})]", $"Delete test plan")) continue; - var suites = client.GetTestSuitesForPlanAsync(plan.Project.Name, plan.Id, SuiteExpand.Children) + var suites = Client.GetTestSuitesForPlanAsync(plan.Project.Name, plan.Id, SuiteExpand.Children) .GetResult($"Error retrieving test suites for test plan '{plan.Name}'"); var hasChildren = (suites.Count > 1 || suites[0].Children?.Count > 0); if (hasChildren && !force & !PowerShell.ShouldContinue($"Are you sure you want to delete test plan '{plan.Name}' and all of its contents?")) continue; - client.DeleteTestPlanAsync(plan.Project.Name, plan.Id) + Client.DeleteTestPlanAsync(plan.Project.Name, plan.Id) .Wait($"Error deleting test plan '{plan.Name}'"); } return null; diff --git a/CSharp/TfsCmdlets/Cmdlets/TestManagement/RenameTestPlan.cs b/CSharp/TfsCmdlets/Cmdlets/TestManagement/RenameTestPlan.cs index f4e0fe55..e09e8b6e 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TestManagement/RenameTestPlan.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TestManagement/RenameTestPlan.cs @@ -17,7 +17,7 @@ partial class RenameTestPlan public object TestPlan { get; set; } } - [CmdletController(typeof(TestPlan))] + [CmdletController(typeof(TestPlan), Client=typeof(ITestPlanHttpClient))] partial class RenameTestPlanController { protected override IEnumerable Run() @@ -29,9 +29,7 @@ protected override IEnumerable Run() if (!PowerShell.ShouldProcess(tp, $"Rename test plan '{plan.Name}' to '{newName}'")) yield break; - var client = Data.GetClient(); - - yield return client.UpdateTestPlanAsync(new TestPlanUpdateParams() { Name = newName }, tp.Name, plan.Id) + yield return Client.UpdateTestPlanAsync(new TestPlanUpdateParams() { Name = newName }, tp.Name, plan.Id) .GetResult($"Error renaming test plan '{plan.Name}'"); } } diff --git a/CSharp/TfsCmdlets.Common/Cmdlets/TfsCmdletAttribute.cs b/CSharp/TfsCmdlets/Cmdlets/TfsCmdletAttribute.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Cmdlets/TfsCmdletAttribute.cs rename to CSharp/TfsCmdlets/Cmdlets/TfsCmdletAttribute.cs diff --git a/CSharp/TfsCmdlets/Cmdlets/Wiki/GetWiki.cs b/CSharp/TfsCmdlets/Cmdlets/Wiki/GetWiki.cs index 1639dcef..44835ad3 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Wiki/GetWiki.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Wiki/GetWiki.cs @@ -26,13 +26,11 @@ partial class GetWiki public SwitchParameter ProjectWiki { get; set; } } - [CmdletController(typeof(WikiV2))] + [CmdletController(typeof(WikiV2), Client=typeof(IWikiHttpClient))] partial class GetWikiController { protected override IEnumerable Run() { - var client = GetClient(); - foreach(var input in Wiki) { var wiki = input switch { @@ -50,7 +48,7 @@ protected override IEnumerable Run() case null when ProjectWiki: case {} when ProjectWiki: { - foreach (var w in client.GetAllWikisAsync(Project.Name) + foreach (var w in Client.GetAllWikisAsync(Project.Name) .GetResult($"Error getting project wiki") .Where(r => r.Type == WikiType.ProjectWiki)) { @@ -60,19 +58,19 @@ protected override IEnumerable Run() } case Guid guid: { - yield return client.GetWikiAsync(Project.Name, guid) + yield return Client.GetWikiAsync(Project.Name, guid) .GetResult($"Error getting Wiki with ID {guid}"); break; } case string s when !s.IsWildcard(): { - yield return client.GetWikiAsync(Project.Name, s) + yield return Client.GetWikiAsync(Project.Name, s) .GetResult($"Error getting Wiki '{s}'"); break; } case string s: { - foreach (var w in client.GetAllWikisAsync(Project.Name) + foreach (var w in Client.GetAllWikisAsync(Project.Name) .GetResult($"Error getting wiki(s) '{s}'") .Where(r => r.Name.IsLike(s))) { diff --git a/CSharp/TfsCmdlets/Cmdlets/Wiki/NewWiki.cs b/CSharp/TfsCmdlets/Cmdlets/Wiki/NewWiki.cs index 2fbe1a7a..4f2061fa 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Wiki/NewWiki.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Wiki/NewWiki.cs @@ -42,7 +42,7 @@ partial class NewWiki public SwitchParameter ProjectWiki { get; set; } } - [CmdletController(typeof(WikiV2))] + [CmdletController(typeof(WikiV2), Client=typeof(IWikiHttpClient))] partial class NewWikiController { protected override IEnumerable Run() @@ -66,9 +66,7 @@ protected override IEnumerable Run() createParams.RepositoryId = repo.Id; } - var client = Data.GetClient(); - - yield return TaskExtensions.GetResult(client.CreateWikiAsync(createParams), "Error creating Wiki repository"); + yield return TaskExtensions.GetResult(Client.CreateWikiAsync(createParams), "Error creating Wiki repository"); } } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Cmdlets/Wiki/RemoveWiki.cs b/CSharp/TfsCmdlets/Cmdlets/Wiki/RemoveWiki.cs index aeac19b1..dd620ed5 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Wiki/RemoveWiki.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Wiki/RemoveWiki.cs @@ -25,13 +25,11 @@ partial class RemoveWiki public SwitchParameter ProjectWiki { get; set; } } -[CmdletController(typeof(WikiV2))] +[CmdletController(typeof(WikiV2), Client=typeof(IWikiHttpClient))] partial class RemoveWikiController { protected override IEnumerable Run() { - var client = Data.GetClient(); - var wikis = Data.GetItems(); var force = Parameters.Get("Force"); @@ -54,7 +52,7 @@ protected override IEnumerable Run() } else { - client.DeleteWikiAsync(tp.Name, w.Id).Wait(); + Client.DeleteWikiAsync(tp.Name, w.Id).Wait(); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/GetClassificationNodeController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/GetClassificationNodeController.cs index 04f54c40..487be4e2 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/GetClassificationNodeController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/GetClassificationNodeController.cs @@ -7,7 +7,10 @@ namespace TfsCmdlets.Cmdlets.WorkItem.AreasIterations internal abstract class GetClassificationNodeController: ControllerBase { [Import] - private INodeUtil NodeUtil { get; } + private INodeUtil NodeUtil { get; set; } + + [Import] + private IWorkItemTrackingHttpClient Client { get; set; } protected override IEnumerable Run() { @@ -54,7 +57,6 @@ protected override IEnumerable Run() } } - var client = Data.GetClient(); var depth = 2; if (path.IsWildcard()) @@ -62,9 +64,9 @@ protected override IEnumerable Run() depth = 4; Logger.Log($"Preparing to recursively search for pattern '{path}'"); - var root = new ClassificationNode(client.GetClassificationNodeAsync(tp.Name, structureGroup, "\\", depth) + var root = new ClassificationNode(Client.GetClassificationNodeAsync(tp.Name, structureGroup, "\\", depth) .GetResult($"Error retrieving {structureGroup} from path '{path}'"), - tp.Name, client); + tp.Name, Client); foreach (var n in root.GetChildren(path)) { @@ -76,7 +78,7 @@ protected override IEnumerable Run() Logger.Log($"Getting {structureGroup} under path '{path}'"); - yield return new ClassificationNode(client.GetClassificationNodeAsync(tp.Name, structureGroup, path, depth) + yield return new ClassificationNode(Client.GetClassificationNodeAsync(tp.Name, structureGroup, path, depth) .GetResult($"Error retrieving {structureGroup} from path '{path}'"), tp.Name, null); } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/MoveClassificationNodeController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/MoveClassificationNodeController.cs index 61b37c6f..35829079 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/MoveClassificationNodeController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/MoveClassificationNodeController.cs @@ -7,6 +7,9 @@ namespace TfsCmdlets.Cmdlets.WorkItem.AreasIterations { internal abstract class MoveClassificationNodeController : ControllerBase { + [Import] + private IWorkItemTrackingHttpClient Client { get; set; } + protected override IEnumerable Run() { var tp = Data.GetProject(); @@ -49,12 +52,10 @@ protected override IEnumerable Run() Id = sourceNode.Id }; - var client = Data.GetClient(); - - var result = client.CreateOrUpdateClassificationNodeAsync(patch, tp.Name, structureGroup, destinationNode.RelativePath) + var result = Client.CreateOrUpdateClassificationNodeAsync(patch, tp.Name, structureGroup, destinationNode.RelativePath) .GetResult($"Error moving node '{sourceNode.RelativePath}' to '{destinationNode.RelativePath}'"); - yield return new ClassificationNode(result, projectName, client); + yield return new ClassificationNode(result, projectName, Client); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/NewClassificationNodeController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/NewClassificationNodeController.cs index 8e781c6b..2a9b43df 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/NewClassificationNodeController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/NewClassificationNodeController.cs @@ -9,6 +9,9 @@ internal abstract class NewClassificationNodeController: ControllerBase [Import] private INodeUtil NodeUtil { get; } + [Import] + private IWorkItemTrackingHttpClient Client { get; set; } + protected override IEnumerable Run() { var tp = Data.GetProject(); @@ -18,7 +21,6 @@ protected override IEnumerable Run() var nodeType = structureGroup.ToString().TrimEnd('s'); var nodePath = NodeUtil.NormalizeNodePath(node, tp.Name, scope: nodeType, includeTeamProject: false); - var client = Data.GetClient(); var parentPath = Path.GetDirectoryName(nodePath); var nodeName = Path.GetFileName(nodePath); @@ -60,10 +62,10 @@ protected override IEnumerable Run() }; } - var result = client.CreateOrUpdateClassificationNodeAsync(patch, tp.Name, structureGroup, parentPath) + var result = Client.CreateOrUpdateClassificationNodeAsync(patch, tp.Name, structureGroup, parentPath) .GetResult($"Error creating node {nodePath}"); - yield return new ClassificationNode(result, tp.Name, client); + yield return new ClassificationNode(result, tp.Name, Client); } [ImportingConstructor] diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RemoveClassificationNodeController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RemoveClassificationNodeController.cs index ee9a902a..0a94c480 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RemoveClassificationNodeController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RemoveClassificationNodeController.cs @@ -6,6 +6,9 @@ namespace TfsCmdlets.Cmdlets.WorkItem.AreasIterations { internal abstract class RemoveClassificationNodeController: ControllerBase { + [Import] + private IWorkItemTrackingHttpClient Client { get; set; } + protected override IEnumerable Run() { var nodes = Data.GetItems().OrderByDescending(n => n.Path).ToList(); @@ -24,7 +27,6 @@ protected override IEnumerable Run() Logger.Log($"Remove nodes and move orphaned work items to node '{moveToNode.FullPath}'"); var tp = Data.GetProject(); - var client = Data.GetClient(); foreach (var node in nodes) { @@ -37,7 +39,7 @@ protected override IEnumerable Run() continue; } - client.DeleteClassificationNodeAsync(node.TeamProject, structureGroup, node.RelativePath, moveToNode.Id) + Client.DeleteClassificationNodeAsync(node.TeamProject, structureGroup, node.RelativePath, moveToNode.Id) .Wait($"Error removing node '{node.FullPath}'"); } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RenameClassificationNodeController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RenameClassificationNodeController.cs index 4adcf9f2..292490f7 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RenameClassificationNodeController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RenameClassificationNodeController.cs @@ -9,12 +9,14 @@ namespace TfsCmdlets.Cmdlets.WorkItem.AreasIterations public abstract class RenameClassificationNodeController: ControllerBase { [Import] - private INodeUtil NodeUtil { get; } + private INodeUtil NodeUtil { get; set; } + + [Import] + private IWorkItemTrackingHttpClient Client { get; set; } protected override IEnumerable Run() { var tp = Data.GetProject(); - var client = Data.GetClient(); var nodeToRename = Data.GetItem(); var structureGroup = Parameters.Get("StructureGroup"); var structureGroupName = structureGroup.ToString().TrimEnd('s'); @@ -36,8 +38,8 @@ protected override IEnumerable Run() Attributes = nodeToRename.Attributes }; - yield return new ClassificationNode(client.UpdateClassificationNodeAsync(patch, tp.Name, structureGroup, nodeToRename.RelativePath) - .GetResult($"Error renaming node '{nodeToRename.RelativePath}'"), tp.Name, client); + yield return new ClassificationNode(Client.UpdateClassificationNodeAsync(patch, tp.Name, structureGroup, nodeToRename.RelativePath) + .GetResult($"Error renaming node '{nodeToRename.RelativePath}'"), tp.Name, Client); } [ImportingConstructor] diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/SetIteration.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/SetIteration.cs index f9c09fdc..695e033a 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/SetIteration.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/SetIteration.cs @@ -44,7 +44,7 @@ partial class SetIteration public int Length { get; set; } = 0; } - [CmdletController(typeof(ClassificationNode))] + [CmdletController(typeof(ClassificationNode), Client=typeof(IWorkItemTrackingHttpClient))] partial class SetIterationController { protected override IEnumerable Run() @@ -57,7 +57,6 @@ protected override IEnumerable Run() ErrorUtil.ThrowIfNotFound(nodeToSet, "Node", Parameters.Get("Node")); var tp = Data.GetProject(); - var client = Data.GetClient(); if (!PowerShell.ShouldProcess(tp, $"Set dates on iteration '{nodeToSet.RelativePath}'")) yield break; @@ -70,10 +69,10 @@ protected override IEnumerable Run() } }; - var result = client.UpdateClassificationNodeAsync(patch, tp.Name, structureGroup, nodeToSet.RelativePath.Substring(1)) + var result = Client.UpdateClassificationNodeAsync(patch, tp.Name, structureGroup, nodeToSet.RelativePath.Substring(1)) .GetResult($"Error setting dates on iteration '{nodeToSet.FullPath}'"); - yield return new ClassificationNode(result, tp.Name, client); + yield return new ClassificationNode(result, tp.Name, Client); } } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/GetWorkItem.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/GetWorkItem.cs index a6784c84..26fb193f 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/GetWorkItem.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/GetWorkItem.cs @@ -243,7 +243,7 @@ partial class GetWorkItem public SwitchParameter IncludeLinks { get; set; } } - [CmdletController(typeof(WebApiWorkItem))] + [CmdletController(typeof(WebApiWorkItem), Client=typeof(IWorkItemTrackingHttpClient))] partial class GetWorkItemController { [Import] @@ -256,7 +256,6 @@ partial class GetWorkItemController protected override IEnumerable Run() { - var client = GetClient(); var expand = IncludeLinks ? WorkItemExpand.All : WorkItemExpand.Fields; IEnumerable fields = null; @@ -296,7 +295,7 @@ string s when int.TryParse(s, out var id) => id, { case WebApiWorkItem wi when Deleted: { - yield return GetWorkItemById((int)wi.Id, WorkItemExpand.None, null, client); + yield return GetWorkItemById((int)wi.Id, WorkItemExpand.None, null, Client); break; } case WebApiWorkItem wi: @@ -306,7 +305,7 @@ string s when int.TryParse(s, out var id) => id, } case int id when Deleted: { - yield return GetWorkItemById(id, WorkItemExpand.None, null, client); + yield return GetWorkItemById(id, WorkItemExpand.None, null, Client); break; } case int id: @@ -316,7 +315,7 @@ string s when int.TryParse(s, out var id) => id, ids.Add(id); continue; } - yield return GetWorkItemById(id, expand, fields, client); + yield return GetWorkItemById(id, expand, fields, Client); break; } case Uri url: @@ -332,7 +331,7 @@ string s when int.TryParse(s, out var id) => id, ids.Add(id); continue; } - yield return GetWorkItemById(id, expand, fields, client); + yield return GetWorkItemById(id, expand, fields, Client); break; } case null when !string.IsNullOrEmpty(Where): @@ -340,13 +339,13 @@ string s when int.TryParse(s, out var id) => id, var fieldList = expand == WorkItemExpand.None ? string.Join(",", fields) : "*"; var wiql = $"SELECT {fieldList} FROM WorkItems WHERE {Where}"; - foreach (var wi in GetWorkItemsByWiql(wiql, expand, client)) yield return wi; + foreach (var wi in GetWorkItemsByWiql(wiql, expand, Client)) yield return wi; break; } case null when !string.IsNullOrEmpty(Wiql): { - foreach (var wi in GetWorkItemsByWiql(Wiql, expand, client)) yield return wi; + foreach (var wi in GetWorkItemsByWiql(Wiql, expand, Client)) yield return wi; break; } @@ -354,13 +353,13 @@ string s when int.TryParse(s, out var id) => id, { var wiql = GetItem(new { Query = SavedQuery, ItemType = "Query" }).Wiql; - foreach (var wi in GetWorkItemsByWiql(wiql, expand, client)) yield return wi; + foreach (var wi in GetWorkItemsByWiql(wiql, expand, Client)) yield return wi; break; } case null when Deleted: { - foreach (var wi in GetDeletedWorkItems(null, client)) + foreach (var wi in GetDeletedWorkItems(null, Client)) { yield return wi; } @@ -375,7 +374,7 @@ string s when int.TryParse(s, out var id) => id, var wiql = BuildSimpleQuery(fields); - foreach (var wi in GetWorkItemsByWiql(wiql, expand, client)) yield return wi; + foreach (var wi in GetWorkItemsByWiql(wiql, expand, Client)) yield return wi; break; } @@ -384,7 +383,7 @@ string s when int.TryParse(s, out var id) => id, if (ids.Count == 0) yield break; - foreach (var wi in GetWorkItemsById(ids, Has_AsOf ? AsOf : null, expand, expand != WorkItemExpand.None ? null : fields, client)) + foreach (var wi in GetWorkItemsById(ids, Has_AsOf ? AsOf : null, expand, expand != WorkItemExpand.None ? null : fields, Client)) { if (ShowWindow) { @@ -397,7 +396,7 @@ string s when int.TryParse(s, out var id) => id, } - private WebApiWorkItem GetWorkItemById(int id, WorkItemExpand expand, IEnumerable fields, WorkItemTrackingHttpClient client) + private WebApiWorkItem GetWorkItemById(int id, WorkItemExpand expand, IEnumerable fields, IWorkItemTrackingHttpClient Client) { WebApiWorkItem wi = null; @@ -405,21 +404,21 @@ private WebApiWorkItem GetWorkItemById(int id, WorkItemExpand expand, IEnumerabl { if (Deleted) { - return GetDeletedWorkItems(new[] { id }, client).FirstOrDefault(); + return GetDeletedWorkItems(new[] { id }, Client).FirstOrDefault(); } else if (Has_Revision) { - wi = client.GetRevisionAsync(id, Revision, expand) + wi = Client.GetRevisionAsync(id, Revision, expand) .GetResult($"Error getting work item '{id}'"); } else if (Has_AsOf) { - wi = client.GetWorkItemAsync(id, fields, AsOf, expand) + wi = Client.GetWorkItemAsync(id, fields, AsOf, expand) .GetResult($"Error getting work item '{id}'"); } else { - wi = client.GetWorkItemAsync(id, fields, null, expand) + wi = Client.GetWorkItemAsync(id, fields, null, expand) .GetResult($"Error getting work item '{id}'"); } @@ -437,7 +436,7 @@ private WebApiWorkItem GetWorkItemById(int id, WorkItemExpand expand, IEnumerabl return wi; } - private IEnumerable GetWorkItemsById(IEnumerable ids, DateTime? asOf, WorkItemExpand expand, IEnumerable fields, WorkItemTrackingHttpClient client) + private IEnumerable GetWorkItemsById(IEnumerable ids, DateTime? asOf, WorkItemExpand expand, IEnumerable fields, IWorkItemTrackingHttpClient Client) { IList idList; @@ -448,7 +447,7 @@ private IEnumerable GetWorkItemsById(IEnumerable ids, DateT if (idList.Count <= MAX_WORKITEMS) { - var wis = client.GetWorkItemsAsync(idList, fields, asOf, expand, WorkItemErrorPolicy.Fail) + var wis = Client.GetWorkItemsAsync(idList, fields, asOf, expand, WorkItemErrorPolicy.Fail) .GetResult("Error getting work items"); foreach (var wi in wis) yield return wi; @@ -460,17 +459,17 @@ private IEnumerable GetWorkItemsById(IEnumerable ids, DateT foreach (var id in idList) { - yield return GetWorkItemById(id, expand, fields, client); + yield return GetWorkItemById(id, expand, fields, Client); } } - private IEnumerable GetDeletedWorkItems(IEnumerable ids, WorkItemTrackingHttpClient client) + private IEnumerable GetDeletedWorkItems(IEnumerable ids, IWorkItemTrackingHttpClient Client) { IEnumerable result; if (ids != null) { - result = client.GetDeletedWorkItemsAsync(ids) + result = Client.GetDeletedWorkItemsAsync(ids) .GetResult($"Error getting deleted work item {ids}"); } else @@ -478,12 +477,12 @@ private IEnumerable GetDeletedWorkItems(IEnumerable ids, Wo IList refs; var projectName = GetItem(new { Deleted = false }).Name; - refs = client.GetDeletedWorkItemShallowReferencesAsync(projectName) + refs = Client.GetDeletedWorkItemShallowReferencesAsync(projectName) .GetResult($"Error getting references for deleted work items"); if (refs.Count == 0) yield break; - result = client.GetDeletedWorkItemsAsync(refs.Select(r => (int)r.Id)) + result = Client.GetDeletedWorkItemsAsync(refs.Select(r => (int)r.Id)) .GetResult($"Error getting deleted work items"); } @@ -509,7 +508,7 @@ private IEnumerable GetDeletedWorkItems(IEnumerable ids, Wo } } - private IEnumerable GetWorkItemsByWiql(string query, WorkItemExpand expand, WorkItemTrackingHttpClient client) + private IEnumerable GetWorkItemsByWiql(string query, WorkItemExpand expand, IWorkItemTrackingHttpClient Client) { TeamContext tc = null; ProjectReference pr = null; @@ -523,21 +522,21 @@ private IEnumerable GetWorkItemsByWiql(string query, WorkItemExp if (tc != null) { - result = client.QueryByWiqlAsync(wiql, tc, TimePrecision) + result = Client.QueryByWiqlAsync(wiql, tc, TimePrecision) .GetResult($"Error querying work items"); } else if (pr != null) { - result = client.QueryByWiqlAsync(wiql, pr.Id, TimePrecision) + result = Client.QueryByWiqlAsync(wiql, pr.Id, TimePrecision) .GetResult($"Error querying work items"); } else { - result = client.QueryByWiqlAsync(wiql, TimePrecision) + result = Client.QueryByWiqlAsync(wiql, TimePrecision) .GetResult($"Error querying work items"); } - return GetWorkItemsById(result.WorkItems.Select(w => w.Id), result.AsOf, expand, expand != WorkItemExpand.None ? null : result.Columns.Select(f => f.ReferenceName).ToList(), client); + return GetWorkItemsById(result.WorkItems.Select(w => w.Id), result.AsOf, expand, expand != WorkItemExpand.None ? null : result.Columns.Select(f => f.ReferenceName).ToList(), Client); } private IEnumerable FixWellKnownFields(IEnumerable fields) diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/History/GetWorkItemHistory.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/History/GetWorkItemHistory.cs index 98760c68..b91cd5e3 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/History/GetWorkItemHistory.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/History/GetWorkItemHistory.cs @@ -16,7 +16,7 @@ partial class GetWorkItemHistory public object WorkItem { get; set; } } - [CmdletController(typeof(Models.WorkItemHistoryEntry))] + [CmdletController(typeof(Models.WorkItemHistoryEntry), Client=typeof(IWorkItemTrackingHttpClient))] partial class GetWorkItemHistoryController { private static readonly string[] _IgnoredFields = new[] { @@ -26,12 +26,11 @@ partial class GetWorkItemHistoryController protected override IEnumerable Run() { - var client = Data.GetClient(); var workItems = GetItems(); foreach (var workItem in workItems) { - var revisions = client.GetRevisionsAsync((int)workItem.Id, (int)workItem.Rev, expand: WorkItemExpand.All) + var revisions = Client.GetRevisionsAsync((int)workItem.Id, (int)workItem.Rev, expand: WorkItemExpand.All) .GetResult("Error retrieving work item revisions"); WebApiWorkItem previous = null; diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/AddWorkItemLink.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/AddWorkItemLink.cs index bc944a59..d0baf2ce 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/AddWorkItemLink.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/AddWorkItemLink.cs @@ -42,7 +42,7 @@ partial class AddWorkItemLink public string Comment { get; set; } } - [CmdletController(typeof(WebApiWorkItemRelation))] + [CmdletController(typeof(WebApiWorkItemRelation), Client=typeof(IWorkItemTrackingHttpClient))] partial class AddWorkItemLinkController { [Import] @@ -75,8 +75,7 @@ protected override IEnumerable Run() } }; - var client = Data.GetClient(); - var result = client.UpdateWorkItemAsync(patch, (int)sourceWi.Id, + var result = Client.UpdateWorkItemAsync(patch, (int)sourceWi.Id, bypassRules: BypassRules, suppressNotifications: SuppressNotifications) .GetResult("Error updating target work item"); diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/ExportWorkItemAttachment.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/ExportWorkItemAttachment.cs index 5de8a6dd..e36a63ac 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/ExportWorkItemAttachment.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/ExportWorkItemAttachment.cs @@ -41,7 +41,7 @@ partial class ExportWorkItemAttachment : CmdletBase public SwitchParameter Force { get; set; } } - [CmdletController(typeof(WorkItemRelation))] + [CmdletController(typeof(WorkItemRelation), Client = typeof(IWorkItemTrackingHttpClient))] partial class ExportWorkItemAttachmentController { protected override IEnumerable Run() @@ -72,7 +72,6 @@ protected override IEnumerable Run() } // var tp = Data.GetProject(); - var client = Data.GetClient(); var outputDir = PowerShell.ResolvePath(destination); Logger.Log($"Downloading attachments to output directory '{outputDir}'"); @@ -102,7 +101,7 @@ protected override IEnumerable Run() var uri = new Uri(link.Url); var id = Guid.Parse(uri.Segments[uri.Segments.Length - 1]); - var stream = client.GetAttachmentContentAsync(id, name).SyncResult(); + var stream = Client.GetAttachmentContentAsync(id, name).SyncResult(); if (exists && !force && !PowerShell.ShouldContinue($"Are you sure you want to overwrite existing file '{outputPath}'?")) continue; diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/GetWorkItemLinkEndType.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/GetWorkItemLinkEndType.cs index fc935002..ec62b38a 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/GetWorkItemLinkEndType.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/GetWorkItemLinkEndType.cs @@ -16,7 +16,7 @@ partial class GetWorkItemLinkType public object LinkType { get; set; } = "*"; } - [CmdletController(typeof(WorkItemRelationType))] + [CmdletController(typeof(WorkItemRelationType), Client=typeof(IWorkItemTrackingHttpClient))] partial class GetWorkItemLinkTypeController { protected override IEnumerable Run() @@ -31,9 +31,7 @@ protected override IEnumerable Run() } case string s: { - var client = Data.GetClient(); - - return client.GetRelationTypesAsync() + return Client.GetRelationTypesAsync() .GetResult("Error getting work item link types") .Where(wirt => wirt.Name.IsLike(s) || wirt.ReferenceName.IsLike(s)); } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/MoveWorkItem.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/MoveWorkItem.cs index 3f8dcceb..62f5e62e 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/MoveWorkItem.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/MoveWorkItem.cs @@ -63,7 +63,7 @@ partial class MoveWorkItem public SwitchParameter Passthru { get; set; } } - [CmdletController(typeof(WebApiWorkItem))] + [CmdletController(typeof(WebApiWorkItem), Client=typeof(IWorkItemTrackingHttpClient))] partial class MoveWorkItemController { protected override IEnumerable Run() @@ -169,8 +169,7 @@ protected override IEnumerable Run() }); } - var client = Data.GetClient(); - var result = client.UpdateWorkItemAsync(patch, (int)wi.Id) + var result = Client.UpdateWorkItemAsync(patch, (int)wi.Id) .GetResult("Error moving work item"); ids.Add((int)result.Id); diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/NewWorkItem.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/NewWorkItem.cs index 075bd6d3..46abb3ae 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/NewWorkItem.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/NewWorkItem.cs @@ -131,7 +131,7 @@ partial class NewWorkItem public SwitchParameter SuppressNotifications { get; set; } } - [CmdletController(typeof(WebApiWorkItem))] + [CmdletController(typeof(WebApiWorkItem), Client=typeof(IWorkItemTrackingHttpClient))] partial class NewWorkItemController { [Import] @@ -147,9 +147,7 @@ protected override IEnumerable Run() wi.Fields["System.TeamProject"] = Project.Name; wi.Fields["System.WorkItemType"] = type.Name; - var client = Data.GetClient(); - - var result = client.CreateWorkItemAsync(Builder.GetJson(wi), Project.Name, type.Name, false, BypassRules, SuppressNotifications) + var result = Client.CreateWorkItemAsync(Builder.GetJson(wi), Project.Name, type.Name, false, BypassRules, SuppressNotifications) .GetResult("Error creating work item"); yield return result; diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/GetWorkItemQueryItemController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/GetWorkItemQueryItemController.cs index ac48c701..d2af0df2 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/GetWorkItemQueryItemController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/GetWorkItemQueryItemController.cs @@ -4,7 +4,7 @@ namespace TfsCmdlets.Controllers.WorkItem.Query { - [CmdletController(typeof(QueryHierarchyItem))] + [CmdletController(typeof(QueryHierarchyItem), Client=typeof(IWorkItemTrackingHttpClient))] partial class GetWorkItemQueryController { [Import] @@ -14,9 +14,8 @@ protected override IEnumerable Run() { var isFolder = ItemType.Equals("Folder", System.StringComparison.OrdinalIgnoreCase); var item = Parameters.Get(ItemType); - var client = GetClient(); - var (myQueriesFolder, sharedQueriesFolder) = GetRootFolders(Project.Name, Scope, client, 0, QueryExpand.Minimal); + var (myQueriesFolder, sharedQueriesFolder) = GetRootFolders(Project.Name, Scope, Client, 0, QueryExpand.Minimal); switch (item) { @@ -56,7 +55,7 @@ protected override IEnumerable Run() var rootFolders = new List(); - (myQueriesFolder, sharedQueriesFolder) = GetRootFolders(Project.Name, Scope, client, 2, QueryExpand.All); + (myQueriesFolder, sharedQueriesFolder) = GetRootFolders(Project.Name, Scope, Client, 2, QueryExpand.All); if ((Scope & QueryItemScope.Personal) == QueryItemScope.Personal) { @@ -70,7 +69,7 @@ protected override IEnumerable Run() foreach (var rootFolder in rootFolders) { - foreach (var c in GetItemsRecursively(rootFolder, path, Project.Name, !isFolder, Deleted, client)) + foreach (var c in GetItemsRecursively(rootFolder, path, Project.Name, !isFolder, Deleted, Client)) { yield return c; } @@ -86,7 +85,7 @@ protected override IEnumerable Run() } } - private (QueryHierarchyItem personal, QueryHierarchyItem shared) GetRootFolders(string projectName, QueryItemScope scope, WorkItemTrackingHttpClient client, int depth = 2, QueryExpand expand = QueryExpand.All) + private (QueryHierarchyItem personal, QueryHierarchyItem shared) GetRootFolders(string projectName, QueryItemScope scope, IWorkItemTrackingHttpClient client, int depth = 2, QueryExpand expand = QueryExpand.All) { var result = client.GetQueriesAsync(projectName, expand, depth, true) .GetResult("Error getting work item query root folders") @@ -95,7 +94,7 @@ protected override IEnumerable Run() return (result.First(q => !(bool)q.IsPublic), result.First(q => (bool)q.IsPublic)); } - private IEnumerable GetItemsRecursively(QueryHierarchyItem item, string pattern, string projectName, bool queriesOnly, bool deletedOnly, WorkItemTrackingHttpClient client) + private IEnumerable GetItemsRecursively(QueryHierarchyItem item, string pattern, string projectName, bool queriesOnly, bool deletedOnly, IWorkItemTrackingHttpClient client) { if ((item.HasChildren ?? false) && (item.Children == null || item.Children.ToList().Count == 0) && !item.IsDeleted) { diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/NewWorkItemQueryItemController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/NewWorkItemQueryItemController.cs index fe4198b4..9c7683b8 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/NewWorkItemQueryItemController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/NewWorkItemQueryItemController.cs @@ -4,7 +4,7 @@ namespace TfsCmdlets.Controllers.WorkItem.Query { - [CmdletController(typeof(QueryHierarchyItem))] + [CmdletController(typeof(QueryHierarchyItem), Client=typeof(IWorkItemTrackingHttpClient))] partial class NewWorkItemQueryController { [Import] @@ -16,7 +16,6 @@ protected override IEnumerable Run() var isFolder = itemType.Equals("Folder", System.StringComparison.OrdinalIgnoreCase); var item = Parameters.Get(itemType); var tp = Data.GetProject(); - var client = GetClient(); var root = GetItem(new { Folder = @"\" }); var fullPath = NodeUtil.NormalizeNodePath(item, tp.Name, root.Name, includeScope: false, separator: '/'); @@ -56,7 +55,7 @@ protected override IEnumerable Run() Logger.Log($"Creating query '{queryName}' in folder '{parentPath}'"); - var result = client.CreateQueryAsync(newItem, tp.Name, parentFolder.Id.ToString()) + var result = Client.CreateQueryAsync(newItem, tp.Name, parentFolder.Id.ToString()) .GetResult($"Error creating new work item {itemType} '{fullPath}'"); yield return result; diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/UndoWorkItemQueryRemovalController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/UndoWorkItemQueryRemovalController.cs index fe356950..39cf7d0e 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/UndoWorkItemQueryRemovalController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Query/UndoWorkItemQueryRemovalController.cs @@ -5,18 +5,16 @@ namespace TfsCmdlets.Controllers.WorkItem.Query { - [CmdletController(typeof(QueryHierarchyItem))] + [CmdletController(typeof(QueryHierarchyItem), Client=typeof(IWorkItemTrackingHttpClient))] partial class UndoWorkItemQueryRemovalController { protected override IEnumerable Run() { - var client = GetClient(); - foreach(var item in Items) { if(!PowerShell.ShouldProcess(Project, $"Restore {ItemType} '{item.Path}'")) continue; - yield return client.UpdateQueryAsync(new QueryHierarchyItem(){IsDeleted=false}, Project.Id, item.Id.ToString(), undeleteDescendants: Recursive) + yield return Client.UpdateQueryAsync(new QueryHierarchyItem(){IsDeleted=false}, Project.Id, item.Id.ToString(), undeleteDescendants: Recursive) .GetResult($"Error restoring {ItemType} '{item.Path}'"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/RemoveWorkItem.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/RemoveWorkItem.cs index e3c94a91..82ecbb9f 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/RemoveWorkItem.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/RemoveWorkItem.cs @@ -33,7 +33,7 @@ partial class RemoveWorkItem public SwitchParameter Force { get; set; } } - [CmdletController(typeof(WebApiWorkItem))] + [CmdletController(typeof(WebApiWorkItem), Client=typeof(IWorkItemTrackingHttpClient))] partial class RemoveWorkItemController { protected override IEnumerable Run() @@ -42,7 +42,6 @@ protected override IEnumerable Run() var tpc = Data.GetCollection(); var destroy = Parameters.Get(nameof(RemoveWorkItem.Destroy)); var force = Parameters.Get(nameof(RemoveWorkItem.Force)); - var client = Data.GetClient(); foreach (var wi in wis) { @@ -50,7 +49,7 @@ protected override IEnumerable Run() if (destroy && !(force || PowerShell.ShouldContinue($"Are you sure you want to destroy work item {wi.Id}?"))) continue; - client.DeleteWorkItemAsync((int)wi.Id, destroy) + Client.DeleteWorkItemAsync((int)wi.Id, destroy) .GetResult($"Error {(destroy ? "destroying" : "deleting")} work item {wi.Id}"); } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/SearchWorkItem.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/SearchWorkItem.cs index 603e4800..8a7c8221 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/SearchWorkItem.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/SearchWorkItem.cs @@ -28,14 +28,13 @@ partial class SearchWorkItem : CmdletBase public int Results { get; set; } = 100; } - [CmdletController(typeof(WebApiWorkItem))] + [CmdletController(typeof(WebApiWorkItem), Client=typeof(ISearchHttpClient))] partial class SearchWorkItemController { protected override IEnumerable Run() { var text = Parameters.Get(nameof(SearchWorkItem.Query)); var results = Parameters.Get(nameof(SearchWorkItem.Results)); - var client = Data.GetClient(); var req = new WorkItemSearchRequest() { @@ -50,12 +49,12 @@ protected override IEnumerable Run() { var tp = Data.GetProject(); - resp = client.FetchWorkItemSearchResultsAsync(req, tp.Name) + resp = Client.FetchWorkItemSearchResultsAsync(req, tp.Name) .GetResult("Error getting search results"); } else { - resp = client.FetchWorkItemSearchResultsAsync(req) + resp = Client.FetchWorkItemSearchResultsAsync(req) .GetResult("Error getting search results"); } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/SetWorkItem.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/SetWorkItem.cs index 68d88075..a9452b1e 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/SetWorkItem.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/SetWorkItem.cs @@ -144,7 +144,7 @@ partial class SetWorkItem public SwitchParameter SuppressNotifications { get; set; } } - [CmdletController(typeof(WebApiWorkItem))] + [CmdletController(typeof(WebApiWorkItem), Client=typeof(IWorkItemTrackingHttpClient))] partial class SetWorkItemController { [Import] @@ -152,11 +152,9 @@ partial class SetWorkItemController protected override IEnumerable Run() { - var client = Data.GetClient(); - foreach (var wi in Items) { - var result = client.UpdateWorkItemAsync(Builder.GetJson(wi), (int)wi.Id, false, BypassRules, SuppressNotifications) + var result = Client.UpdateWorkItemAsync(Builder.GetJson(wi), (int)wi.Id, false, BypassRules, SuppressNotifications) .GetResult("Error updating work item"); yield return result; diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/GetWorkItemTag.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/GetWorkItemTag.cs index 38bba391..74d22233 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/GetWorkItemTag.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/GetWorkItemTag.cs @@ -26,7 +26,7 @@ partial class GetWorkItemTag public SwitchParameter IncludeInactive { get; set; } } - [CmdletController(typeof(WebApiTagDefinition))] + [CmdletController(typeof(WebApiTagDefinition), Client=typeof(ITaggingHttpClient))] partial class GetWorkItemTagController { protected override IEnumerable Run() @@ -35,7 +35,6 @@ protected override IEnumerable Run() var includeInactive = Parameters.Get(nameof(GetWorkItemTag.IncludeInactive)); var tp = Data.GetProject(); - var client = Data.GetClient(); switch (tag) { @@ -45,13 +44,13 @@ protected override IEnumerable Run() } case string s: { - return client.GetTagsAsync(tp.Id, includeInactive) + return Client.GetTagsAsync(tp.Id, includeInactive) .GetResult($"Error getting work item tag(s) '{s}'") .Where(t => t.Name.IsLike(s)); } case IEnumerable tags: { - return client.GetTagsAsync(tp.Id, includeInactive) + return Client.GetTagsAsync(tp.Id, includeInactive) .GetResult($"Error getting work item tag(s) '{string.Join(", ", tags)}'") .Where(t => tags.Any(tag => t.Name.IsLike(tag))); } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/NewWorkItemTag.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/NewWorkItemTag.cs index 08030b76..b45786e9 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/NewWorkItemTag.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/NewWorkItemTag.cs @@ -19,7 +19,7 @@ partial class NewWorkItemTag public string Tag { get; set; } } - [CmdletController(typeof(WebApiTagDefinition))] + [CmdletController(typeof(WebApiTagDefinition), Client=typeof(ITaggingHttpClient))] partial class NewWorkItemTagController { protected override IEnumerable Run() @@ -29,9 +29,7 @@ protected override IEnumerable Run() if (!PowerShell.ShouldProcess(tp, $"Create work item tag '{tag}'")) yield break; - var client = Data.GetClient(); - - yield return client.CreateTagAsync(tp.Id, tag) + yield return Client.CreateTagAsync(tp.Id, tag) .GetResult($"Error creating work item tag '{tag}'"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/RemoveWorkItemTag.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/RemoveWorkItemTag.cs index 017e7eae..660166b3 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/RemoveWorkItemTag.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/RemoveWorkItemTag.cs @@ -25,7 +25,7 @@ partial class RemoveWorkItemTag public SwitchParameter Force { get; set; } } - [CmdletController(typeof(WebApiTagDefinition))] + [CmdletController(typeof(WebApiTagDefinition), Client=typeof(ITaggingHttpClient))] partial class RemoveWorkItemTagController { protected override IEnumerable Run() @@ -34,7 +34,6 @@ protected override IEnumerable Run() var force = Parameters.Get(nameof(RemoveWorkItemTag.Force)); var tp = Data.GetProject(); - var client = Data.GetClient(); foreach (var t in tags) { @@ -46,7 +45,7 @@ protected override IEnumerable Run() continue; } - client.DeleteTagAsync(tp.Id, t.Id) + Client.DeleteTagAsync(tp.Id, t.Id) .Wait($"Error deleting tag '{t.Name}'"); } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/RenameWorkItemTag.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/RenameWorkItemTag.cs index 5b11cf25..564b357a 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/RenameWorkItemTag.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/RenameWorkItemTag.cs @@ -18,7 +18,7 @@ partial class RenameWorkItemTag public object Tag { get; set; } } - [CmdletController(typeof(WebApiTagDefinition))] + [CmdletController(typeof(WebApiTagDefinition), Client=typeof(ITaggingHttpClient))] partial class RenameWorkItemTagController { protected override IEnumerable Run() @@ -27,11 +27,10 @@ protected override IEnumerable Run() var newName = Parameters.Get(nameof(RenameWorkItemTag.NewName)); var tp = Data.GetProject(); - var client = Data.GetClient(); if (!PowerShell.ShouldProcess(tp, $"Rename work item tag '{tag.Name}' to '{newName}'")) yield break; - yield return client.UpdateTagAsync(tp.Id, tag.Id, newName, tag.Active) + yield return Client.UpdateTagAsync(tp.Id, tag.Id, newName, tag.Active) .GetResult($"Error renaming work item tag '{tag.Name}'"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/ToggleWorkItemTagController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/ToggleWorkItemTagController.cs index a028ed77..9582559c 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/ToggleWorkItemTagController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/ToggleWorkItemTagController.cs @@ -4,18 +4,20 @@ namespace TfsCmdlets.Cmdlets.WorkItem.Tagging { internal abstract class ToggleWorkItemTagController: ControllerBase { + [Import] + private ITaggingHttpClient Client { get; set; } + protected override IEnumerable Run() { var tags = Data.GetItems(); var tp = Data.GetProject(); var enabled = Parameters.Get("Enabled"); - var client = Data.GetClient(); foreach (var tag in tags) { if (!PowerShell.ShouldProcess(tp, $"{(enabled? "Enable": "Disable")} work item tag '{tag.Name}'")) continue; - yield return client.UpdateTagAsync(tp.Id, tag.Id, tag.Name, enabled) + yield return Client.UpdateTagAsync(tp.Id, tag.Id, tag.Name, enabled) .GetResult($"Error renaming work item tag '{tag.Name}'"); } } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/UndoWorkItemRemoval.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/UndoWorkItemRemoval.cs index d1125be0..1c972170 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/UndoWorkItemRemoval.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/UndoWorkItemRemoval.cs @@ -18,18 +18,16 @@ partial class UndoWorkItemRemoval public object WorkItem { get; set; } } - [CmdletController(typeof(WebApiWorkItem))] + [CmdletController(typeof(WebApiWorkItem), Client=typeof(IWorkItemTrackingHttpClient))] partial class UndoWorkItemRemovalController { protected override IEnumerable Run() { - var client = GetClient(); - foreach (var wi in GetItems(new { Deleted = true })) { if (!PowerShell.ShouldProcess($"[Organization: {Collection.DisplayName}]/[Work Item: {wi.Id}]", $"Restore work item")) continue; - client.RestoreWorkItemAsync(new Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemDeleteUpdate() + Client.RestoreWorkItemAsync(new Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemDeleteUpdate() { IsDeleted = false }, (int) wi.Id).GetResult($"Error restoring work item {wi.Id}"); diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/GetWorkItemType.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/GetWorkItemType.cs index d04e8a0d..33def061 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/GetWorkItemType.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/GetWorkItemType.cs @@ -25,7 +25,7 @@ partial class GetWorkItemType public object WorkItem { get; set; } } - [CmdletController(typeof(WebApiWorkItemType))] + [CmdletController(typeof(WebApiWorkItemType), Client=typeof(IWorkItemTrackingHttpClient))] partial class GetWorkItemTypeController { protected override IEnumerable Run() @@ -41,7 +41,6 @@ protected override IEnumerable Run() type = workItem.Fields["System.WorkItemType"]; } - var client = Data.GetClient(); var tp = Data.GetProject(); switch (type) @@ -52,13 +51,13 @@ protected override IEnumerable Run() } case string t: { - return client.GetWorkItemTypesAsync(tp.Id) + return Client.GetWorkItemTypesAsync(tp.Id) .GetResult($"Error getting type(s) '{t}'") .Where(t1 => t1.Name.IsLike(t)); } case IEnumerable types: { - return client.GetWorkItemTypesAsync(tp.Id) + return Client.GetWorkItemTypesAsync(tp.Id) .GetResult($"Error getting type(s) '{string.Join(", ", types)}'") .Where(t1 => types.Any(t2 => t1.Name.IsLike(t2))); } diff --git a/CSharp/TfsCmdlets.Common/Controllers/ControllerBase.cs b/CSharp/TfsCmdlets/Controllers/ControllerBase.cs similarity index 96% rename from CSharp/TfsCmdlets.Common/Controllers/ControllerBase.cs rename to CSharp/TfsCmdlets/Controllers/ControllerBase.cs index f7052617..45eb268a 100644 --- a/CSharp/TfsCmdlets.Common/Controllers/ControllerBase.cs +++ b/CSharp/TfsCmdlets/Controllers/ControllerBase.cs @@ -12,8 +12,6 @@ public abstract class ControllerBase : IController public virtual Type DataType => GetType(); - protected T GetClient() => Data.GetClient(); - protected T GetItem(object overridingParameters = null) => Data.GetItem(overridingParameters); protected bool TestItem(object overridingParameters = null) => Data.TestItem(overridingParameters); diff --git a/CSharp/TfsCmdlets.Common/Enums.cs b/CSharp/TfsCmdlets/Enums.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Enums.cs rename to CSharp/TfsCmdlets/Enums.cs diff --git a/CSharp/TfsCmdlets.Common/Extensions/HashtableExtensions.cs b/CSharp/TfsCmdlets/Extensions/HashtableExtensions.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Extensions/HashtableExtensions.cs rename to CSharp/TfsCmdlets/Extensions/HashtableExtensions.cs diff --git a/CSharp/TfsCmdlets.Common/Extensions/JsonExtensions.cs b/CSharp/TfsCmdlets/Extensions/JsonExtensions.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Extensions/JsonExtensions.cs rename to CSharp/TfsCmdlets/Extensions/JsonExtensions.cs diff --git a/CSharp/TfsCmdlets.Common/Extensions/ListExtensions.cs b/CSharp/TfsCmdlets/Extensions/ListExtensions.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Extensions/ListExtensions.cs rename to CSharp/TfsCmdlets/Extensions/ListExtensions.cs diff --git a/CSharp/TfsCmdlets.Common/Extensions/ObjectExtensions.cs b/CSharp/TfsCmdlets/Extensions/ObjectExtensions.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Extensions/ObjectExtensions.cs rename to CSharp/TfsCmdlets/Extensions/ObjectExtensions.cs diff --git a/CSharp/TfsCmdlets.Common/Extensions/PSObjectExtensions.cs b/CSharp/TfsCmdlets/Extensions/PSObjectExtensions.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Extensions/PSObjectExtensions.cs rename to CSharp/TfsCmdlets/Extensions/PSObjectExtensions.cs diff --git a/CSharp/TfsCmdlets.Common/Extensions/PipelineExtensions.cs b/CSharp/TfsCmdlets/Extensions/PipelineExtensions.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Extensions/PipelineExtensions.cs rename to CSharp/TfsCmdlets/Extensions/PipelineExtensions.cs diff --git a/CSharp/TfsCmdlets.Common/Extensions/StringExtensions.cs b/CSharp/TfsCmdlets/Extensions/StringExtensions.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Extensions/StringExtensions.cs rename to CSharp/TfsCmdlets/Extensions/StringExtensions.cs diff --git a/CSharp/TfsCmdlets.Common/Extensions/TaskExtensions.cs b/CSharp/TfsCmdlets/Extensions/TaskExtensions.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Extensions/TaskExtensions.cs rename to CSharp/TfsCmdlets/Extensions/TaskExtensions.cs diff --git a/CSharp/TfsCmdlets.Common/Extensions/TeamProjectExtensions.cs b/CSharp/TfsCmdlets/Extensions/TeamProjectExtensions.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Extensions/TeamProjectExtensions.cs rename to CSharp/TfsCmdlets/Extensions/TeamProjectExtensions.cs diff --git a/CSharp/TfsCmdlets.Common/Extensions/WorkItemExtensions.cs b/CSharp/TfsCmdlets/Extensions/WorkItemExtensions.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Extensions/WorkItemExtensions.cs rename to CSharp/TfsCmdlets/Extensions/WorkItemExtensions.cs diff --git a/CSharp/TfsCmdlets.Common/Extensions/XmlExtensions.cs b/CSharp/TfsCmdlets/Extensions/XmlExtensions.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Extensions/XmlExtensions.cs rename to CSharp/TfsCmdlets/Extensions/XmlExtensions.cs diff --git a/CSharp/TfsCmdlets/GlobalUsings.cs b/CSharp/TfsCmdlets/GlobalUsings.cs index 2a6de590..086f0a9f 100644 --- a/CSharp/TfsCmdlets/GlobalUsings.cs +++ b/CSharp/TfsCmdlets/GlobalUsings.cs @@ -9,6 +9,7 @@ global using TfsCmdlets.Controllers; global using TfsCmdlets.Extensions; global using TfsCmdlets.Services; +global using TfsCmdlets.HttpClients; global using WebApiWorkItem = Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItem; global using WebApiTeamProject = Microsoft.TeamFoundation.Core.WebApi.TeamProject; diff --git a/CSharp/TfsCmdlets.Common/HttpClients/HttpClientAttribute.cs b/CSharp/TfsCmdlets/HttpClients/HttpClientAttribute.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/HttpClients/HttpClientAttribute.cs rename to CSharp/TfsCmdlets/HttpClients/HttpClientAttribute.cs diff --git a/CSharp/TfsCmdlets/HttpClients/IAccountLicensingHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IAccountLicensingHttpClient.cs new file mode 100644 index 00000000..1fdad0e2 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IAccountLicensingHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.Licensing.Client; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(AccountLicensingHttpClient))] + partial interface IAccountLicensingHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IBuildHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IBuildHttpClient.cs new file mode 100644 index 00000000..1836bb41 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IBuildHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.TeamFoundation.Build.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(BuildHttpClient))] + partial interface IBuildHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IExtensionManagementHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IExtensionManagementHttpClient.cs new file mode 100644 index 00000000..3ee0f204 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IExtensionManagementHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.ExtensionManagement.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(ExtensionManagementHttpClient))] + partial interface IExtensionManagementHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IFeedHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IFeedHttpClient.cs new file mode 100644 index 00000000..11f3cf95 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IFeedHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.Feed.WebApi; + +namespace TfsCmdlets.HttpClients { + + [HttpClient(typeof(FeedHttpClient))] + partial interface IFeedHttpClient { + + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IGenericHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IGenericHttpClient.cs new file mode 100644 index 00000000..238ffc20 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IGenericHttpClient.cs @@ -0,0 +1,7 @@ +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(GenericHttpClient))] + partial interface IGenericHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IGitExtendedHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IGitExtendedHttpClient.cs new file mode 100644 index 00000000..d751893c --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IGitExtendedHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(GitExtendedHttpClient))] + partial interface IGitExtendedHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IGitHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IGitHttpClient.cs new file mode 100644 index 00000000..5871cb3a --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IGitHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.TeamFoundation.SourceControl.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(GitHttpClient))] + partial interface IGitHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IGraphHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IGraphHttpClient.cs new file mode 100644 index 00000000..2e9e3edf --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IGraphHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.Graph.Client; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(GraphHttpClient))] + partial interface IGraphHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IIdentityHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IIdentityHttpClient.cs new file mode 100644 index 00000000..f9a9eb59 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IIdentityHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.Identity.Client; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(IdentityHttpClient))] + partial interface IIdentityHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IOperationsHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IOperationsHttpClient.cs new file mode 100644 index 00000000..a2eb79ba --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IOperationsHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.Operations; + +namespace TfsCmdlets.HttpClients { + + [HttpClient(typeof(OperationsHttpClient))] + partial interface IOperationsHttpClient { + + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IPolicyHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IPolicyHttpClient.cs new file mode 100644 index 00000000..505347d9 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IPolicyHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.TeamFoundation.Policy.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(PolicyHttpClient))] + partial interface IPolicyHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IProcessHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IProcessHttpClient.cs new file mode 100644 index 00000000..827ce0a2 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IProcessHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.TeamFoundation.Core.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(ProcessHttpClient))] + partial interface IProcessHttpClient { + + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IProjectHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IProjectHttpClient.cs new file mode 100644 index 00000000..e3715367 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IProjectHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.TeamFoundation.Core.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(ProjectHttpClient))] + partial interface IProjectHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IReleaseHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IReleaseHttpClient.cs new file mode 100644 index 00000000..14c42e00 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IReleaseHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.Clients; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(ReleaseHttpClient))] + partial interface IReleaseHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IReleaseHttpClient2.cs b/CSharp/TfsCmdlets/HttpClients/IReleaseHttpClient2.cs new file mode 100644 index 00000000..f13cbe28 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IReleaseHttpClient2.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.Clients; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(ReleaseHttpClient2))] + partial interface IReleaseHttpClient2 + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/ISearchHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/ISearchHttpClient.cs new file mode 100644 index 00000000..a7d16645 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/ISearchHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.Search.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(SearchHttpClient))] + partial interface ISearchHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IServiceHooksPublisherHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IServiceHooksPublisherHttpClient.cs new file mode 100644 index 00000000..ce189c8c --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IServiceHooksPublisherHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.ServiceHooks.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(ServiceHooksPublisherHttpClient))] + partial interface IServiceHooksPublisherHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/ITaggingHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/ITaggingHttpClient.cs new file mode 100644 index 00000000..cfde10a2 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/ITaggingHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.TeamFoundation.Core.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(TaggingHttpClient))] + partial interface ITaggingHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/ITeamAdminHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/ITeamAdminHttpClient.cs new file mode 100644 index 00000000..282dbf1a --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/ITeamAdminHttpClient.cs @@ -0,0 +1,136 @@ +using System.Runtime.Serialization; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(TeamAdminHttpClient))] + partial interface ITeamAdminHttpClient + { + } + + #region Data Classes + + /// + /// Represents a collection of team administrators + /// + [DataContract] + public class TeamAdmins + { + /// + /// Collection of team administrators + /// + [DataMember(Name = "admins")] + public TeamAdmin[] Admins { get; set; } + } + + /// + /// Represents a team administrator + /// + public class TeamAdmin + { + /// + /// Identity Type + /// + public string IdentityType { get; set; } + + /// + /// Friendly Display Name + /// + public string FriendlyDisplayName { get; set; } + + /// + /// Display Name + /// + public string DisplayName { get; set; } + + /// + /// Sub-header + /// + public string SubHeader { get; set; } + + /// + /// Team Foundation Id + /// + public string TeamFoundationId { get; set; } + + /// + /// Entity Id + /// + public string EntityId { get; set; } + + /// + /// List of Errors + /// + public object[] Errors { get; set; } + + /// + /// List of Warnings + /// + public object[] Warnings { get; set; } + + /// + /// User Domain + /// + public string Domain { get; set; } + + /// + /// User Account Name + /// + public string AccountName { get; set; } + + /// + /// Is Windows User + /// + public bool IsWindowsUser { get; set; } + + /// + /// Email Address + /// + public string MailAddress { get; set; } + + /// + public override string ToString() + { + return $"{FriendlyDisplayName} ({AccountName})"; + } + } + + /// + /// The request body to submit to the "Add Admin" service + /// + [DataContract] + public class AddTeamAdminRequestData + { + /// + /// TeamId + /// + [DataMember(Name = "teamId")] + public Guid Team { get; set; } + + /// + /// List of New Users + /// + [DataMember(Name = "newUsersJson")] + public string NewUsers { get; set; } + + /// + /// List of Existing Users + /// + [DataMember(Name = "existingUsersJson")] + public string ExistingUsers { get; set; } + } + + /// + /// The request body to submit to the "Remove Admin" service + /// + [DataContract] + public class RemoveTeamAdminResult + { + /// + /// Indicates the success of the operation + /// + [DataMember(Name = "success")] + public bool Success { get; set; } + } + + #endregion +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/ITeamHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/ITeamHttpClient.cs new file mode 100644 index 00000000..fe4a56e0 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/ITeamHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.TeamFoundation.Core.WebApi; + +namespace TfsCmdlets.HttpClients { + + [HttpClient(typeof(TeamHttpClient))] + partial interface ITeamHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/ITestPlanHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/ITestPlanHttpClient.cs new file mode 100644 index 00000000..9b0fafdf --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/ITestPlanHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.Services.TestManagement.TestPlanning.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(TestPlanHttpClient))] + partial interface ITestPlanHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets.Common/HttpClients/IVssHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IVssHttpClient.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/HttpClients/IVssHttpClient.cs rename to CSharp/TfsCmdlets/HttpClients/IVssHttpClient.cs diff --git a/CSharp/TfsCmdlets/HttpClients/IWikiHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IWikiHttpClient.cs new file mode 100644 index 00000000..92c80ecb --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IWikiHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.TeamFoundation.Wiki.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(WikiHttpClient))] + partial interface IWikiHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IWorkHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IWorkHttpClient.cs new file mode 100644 index 00000000..82221dde --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IWorkHttpClient.cs @@ -0,0 +1,10 @@ +using Microsoft.TeamFoundation.Work.WebApi; + +namespace TfsCmdlets.HttpClients +{ + + [HttpClient(typeof(WorkHttpClient))] + partial interface IWorkHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/IWorkItemTrackingProcessHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/IWorkItemTrackingProcessHttpClient.cs new file mode 100644 index 00000000..af2a1e41 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/IWorkItemTrackingProcessHttpClient.cs @@ -0,0 +1,9 @@ +using Microsoft.TeamFoundation.WorkItemTracking.Process.WebApi; + +namespace TfsCmdlets.HttpClients +{ + [HttpClient(typeof(WorkItemTrackingProcessHttpClient))] + partial interface IWorkItemTrackingProcessHttpClient + { + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets.Common/HttpClients/GenericHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/Impl/GenericHttpClient.cs similarity index 98% rename from CSharp/TfsCmdlets.Common/HttpClients/GenericHttpClient.cs rename to CSharp/TfsCmdlets/HttpClients/Impl/GenericHttpClient.cs index 0e19136b..b3d8a8e1 100644 --- a/CSharp/TfsCmdlets.Common/HttpClients/GenericHttpClient.cs +++ b/CSharp/TfsCmdlets/HttpClients/Impl/GenericHttpClient.cs @@ -10,8 +10,10 @@ namespace TfsCmdlets.HttpClients /// Generic HTTP Client, used by the Invoke-TfsRestApi cmdlet /// [Export] - public class GenericHttpClient : VssHttpClientBase + public class GenericHttpClient : VssHttpClientBase, IGenericHttpClient { + private Uri _Uri; + #region Constructors and fields /// @@ -54,7 +56,7 @@ public GenericHttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequest /// /// Gets the API URL /// - public Uri Uri { get; private set; } + public Uri GetUri() => _Uri; /// /// Sends a GET request to an Azure DevOps API @@ -305,7 +307,7 @@ private HttpRequestMessage CreateMessage(HttpMethod method, string apiPath, stri queryParameters, mediaType); - Uri = msg.RequestUri; + _Uri = msg.RequestUri; return msg; } diff --git a/CSharp/TfsCmdlets/HttpClients/Impl/GitExtendedHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/Impl/GitExtendedHttpClient.cs new file mode 100644 index 00000000..cbf2c29e --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/Impl/GitExtendedHttpClient.cs @@ -0,0 +1,80 @@ +using System.Net.Http; +using Microsoft.VisualStudio.Services.Common; + +namespace TfsCmdlets.HttpClients; + +internal class GitExtendedHttpClient : GenericHttpClient +{ + /// + /// Enables/disables a Git repository + /// + public void UpdateRepositoryEnabledStatus(Guid projectId, Guid repoId, bool enabled) + { + UpdateRepositoryEnabledStatus(projectId.ToString(), repoId, enabled); + } + + /// + /// Enables/disables a Git repository + /// + public void UpdateRepositoryEnabledStatus(string project, Guid repoId, bool enabled) + { + PostForm( + $"{project}/_api/_versioncontrol/UpdateRepositoryOption", + new Dictionary + { + ["repositoryId"] = repoId.ToString(), + ["option"] = $"{{'key':'IsDisabled', 'value':{(!enabled).ToString().ToLowerInvariant()}}}" + }, + true, + $"/{project}/_settings/repositories?repo={repoId}", + null, + new Dictionary { + ["__v"] = "5", + ["repositoryId"] = repoId.ToString() + } + ); + } + + #region Constructors and fields + + /// + /// Creates a new instance of the GitExtendedHttpClient class + /// + public GitExtendedHttpClient(Uri baseUrl, VssCredentials credentials) : base(baseUrl, credentials) + { + } + + /// + /// Creates a new instance of the GitExtendedHttpClient class + /// + public GitExtendedHttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings) : base( + baseUrl, credentials, settings) + { + } + + /// + /// Creates a new instance of the GitExtendedHttpClient class + /// + public GitExtendedHttpClient(Uri baseUrl, VssCredentials credentials, params DelegatingHandler[] handlers) : base( + baseUrl, credentials, handlers) + { + } + + /// + /// Creates a new instance of the GitExtendedHttpClient class + /// + public GitExtendedHttpClient(Uri baseUrl, HttpMessageHandler pipeline, bool disposeHandler) : base(baseUrl, + pipeline, disposeHandler) + { + } + + /// + /// Creates a new instance of the GitExtendedHttpClient class + /// + public GitExtendedHttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings, + params DelegatingHandler[] handlers) : base(baseUrl, credentials, settings, handlers) + { + } + + #endregion +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/HttpClients/Impl/TeamAdminHttpClient.cs b/CSharp/TfsCmdlets/HttpClients/Impl/TeamAdminHttpClient.cs new file mode 100644 index 00000000..385b69a9 --- /dev/null +++ b/CSharp/TfsCmdlets/HttpClients/Impl/TeamAdminHttpClient.cs @@ -0,0 +1,108 @@ +using System.Net.Http; +using Microsoft.VisualStudio.Services.Common; + +namespace TfsCmdlets.HttpClients; + +/// +/// Custom HTTP Client to handle team administrator management +/// +public class TeamAdminHttpClient : GenericHttpClient +{ + /// + /// Adds an administrator to a team + /// + public IEnumerable AddTeamAdmin(Guid projectId, Guid teamId, IEnumerable userIds) + { + return AddTeamAdmin(projectId.ToString(), teamId, userIds); + } + + /// + /// Adds an administrator to a team + /// + public IEnumerable AddTeamAdmin(string project, Guid teamId, IEnumerable userIds) + { + var result = Post( + $"/{project}/_api/_identity/AddTeamAdmins", + new AddTeamAdminRequestData + { + Team = teamId, + NewUsers = "[]", + ExistingUsers = $"[{string.Join(", ", userIds.Select(id => "\"" + id + "\""))}]" + }); + + return result.Admins; + } + + /// + /// Removes an administrator from a team + /// + public bool RemoveTeamAdmin(Guid project, Guid teamId, Guid userId) + { + return RemoveTeamAdmin(project.ToString(), teamId, userId); + } + + /// + /// Removes an administrator from a team + /// + public bool RemoveTeamAdmin(string project, Guid teamId, Guid userId) + { + var result = PostForm( + $"{project}/_api/_identity/RemoveTeamAdmin", + new Dictionary + { + ["teamId"] = teamId.ToString(), + ["tfidToRemove"] = userId.ToString() + }, + true, + $"/{project}/_settings/teams?teamId={teamId}", + null, + new Dictionary {["__v"] = "5"}, + "application/json" + ); + + return result.Success; + } + + #region Constructors and fields + + /// + /// Creates a new instance of the TeamAdminHttpClient class + /// + public TeamAdminHttpClient(Uri baseUrl, VssCredentials credentials) : base(baseUrl, credentials) + { + } + + /// + /// Creates a new instance of the TeamAdminHttpClient class + /// + public TeamAdminHttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings) : base( + baseUrl, credentials, settings) + { + } + + /// + /// Creates a new instance of the TeamAdminHttpClient class + /// + public TeamAdminHttpClient(Uri baseUrl, VssCredentials credentials, params DelegatingHandler[] handlers) : base( + baseUrl, credentials, handlers) + { + } + + /// + /// Creates a new instance of the TeamAdminHttpClient class + /// + public TeamAdminHttpClient(Uri baseUrl, HttpMessageHandler pipeline, bool disposeHandler) : base(baseUrl, + pipeline, disposeHandler) + { + } + + /// + /// Creates a new instance of the TeamAdminHttpClient class + /// + public TeamAdminHttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings, + params DelegatingHandler[] handlers) : base(baseUrl, credentials, settings, handlers) + { + } + + #endregion +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets.Common/Models/BacklogLevelConfiguration.cs b/CSharp/TfsCmdlets/Models/BacklogLevelConfiguration.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/BacklogLevelConfiguration.cs rename to CSharp/TfsCmdlets/Models/BacklogLevelConfiguration.cs diff --git a/CSharp/TfsCmdlets.Common/Models/Board.cs b/CSharp/TfsCmdlets/Models/Board.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/Board.cs rename to CSharp/TfsCmdlets/Models/Board.cs diff --git a/CSharp/TfsCmdlets.Common/Models/CardRule.cs b/CSharp/TfsCmdlets/Models/CardRule.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/CardRule.cs rename to CSharp/TfsCmdlets/Models/CardRule.cs diff --git a/CSharp/TfsCmdlets.Common/Models/ClassificationNode.cs b/CSharp/TfsCmdlets/Models/ClassificationNode.cs similarity index 96% rename from CSharp/TfsCmdlets.Common/Models/ClassificationNode.cs rename to CSharp/TfsCmdlets/Models/ClassificationNode.cs index 9e2d0bcd..4926d8cf 100644 --- a/CSharp/TfsCmdlets.Common/Models/ClassificationNode.cs +++ b/CSharp/TfsCmdlets/Models/ClassificationNode.cs @@ -9,11 +9,11 @@ namespace TfsCmdlets.Models /// public class ClassificationNode : ModelBase { - private readonly WorkItemTrackingHttpClient _client; + private readonly IWorkItemTrackingHttpClient _client; private readonly string _rootPath; private readonly string _relativePath; - public ClassificationNode(WorkItemClassificationNode n, string projectName, WorkItemTrackingHttpClient client) + public ClassificationNode(WorkItemClassificationNode n, string projectName, IWorkItemTrackingHttpClient client) : base(n) { ProjectName = projectName; diff --git a/CSharp/TfsCmdlets.Common/Models/Connection.cs b/CSharp/TfsCmdlets/Models/Connection.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/Connection.cs rename to CSharp/TfsCmdlets/Models/Connection.cs diff --git a/CSharp/TfsCmdlets.Common/Models/ContributionNodeQuery.cs b/CSharp/TfsCmdlets/Models/ContributionNodeQuery.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/ContributionNodeQuery.cs rename to CSharp/TfsCmdlets/Models/ContributionNodeQuery.cs diff --git a/CSharp/TfsCmdlets.Common/Models/GitItem.cs b/CSharp/TfsCmdlets/Models/GitItem.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/GitItem.cs rename to CSharp/TfsCmdlets/Models/GitItem.cs diff --git a/CSharp/TfsCmdlets.Common/Models/GlobalList.cs b/CSharp/TfsCmdlets/Models/GlobalList.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/GlobalList.cs rename to CSharp/TfsCmdlets/Models/GlobalList.cs diff --git a/CSharp/TfsCmdlets.Common/Models/Identity.cs b/CSharp/TfsCmdlets/Models/Identity.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/Identity.cs rename to CSharp/TfsCmdlets/Models/Identity.cs diff --git a/CSharp/TfsCmdlets.Common/Models/IdentityRefWrapper.cs b/CSharp/TfsCmdlets/Models/IdentityRefWrapper.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/IdentityRefWrapper.cs rename to CSharp/TfsCmdlets/Models/IdentityRefWrapper.cs diff --git a/CSharp/TfsCmdlets.Common/Models/ModelBase.cs b/CSharp/TfsCmdlets/Models/ModelBase.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/ModelBase.cs rename to CSharp/TfsCmdlets/Models/ModelBase.cs diff --git a/CSharp/TfsCmdlets.Common/Models/ServerVersion.cs b/CSharp/TfsCmdlets/Models/ServerVersion.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/ServerVersion.cs rename to CSharp/TfsCmdlets/Models/ServerVersion.cs diff --git a/CSharp/TfsCmdlets.Common/Models/Team.cs b/CSharp/TfsCmdlets/Models/Team.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/Team.cs rename to CSharp/TfsCmdlets/Models/Team.cs diff --git a/CSharp/TfsCmdlets.Common/Models/TeamAdmin.cs b/CSharp/TfsCmdlets/Models/TeamAdmin.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/TeamAdmin.cs rename to CSharp/TfsCmdlets/Models/TeamAdmin.cs diff --git a/CSharp/TfsCmdlets.Common/Models/TeamMember.cs b/CSharp/TfsCmdlets/Models/TeamMember.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/TeamMember.cs rename to CSharp/TfsCmdlets/Models/TeamMember.cs diff --git a/CSharp/TfsCmdlets.Common/Models/TeamProjectMember.cs b/CSharp/TfsCmdlets/Models/TeamProjectMember.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/TeamProjectMember.cs rename to CSharp/TfsCmdlets/Models/TeamProjectMember.cs diff --git a/CSharp/TfsCmdlets.Common/Models/TfsInstallationPath.cs b/CSharp/TfsCmdlets/Models/TfsInstallationPath.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/TfsInstallationPath.cs rename to CSharp/TfsCmdlets/Models/TfsInstallationPath.cs diff --git a/CSharp/TfsCmdlets.Common/Models/WorkItem/Query/QueryItem.cs b/CSharp/TfsCmdlets/Models/WorkItem/Query/QueryItem.cs similarity index 77% rename from CSharp/TfsCmdlets.Common/Models/WorkItem/Query/QueryItem.cs rename to CSharp/TfsCmdlets/Models/WorkItem/Query/QueryItem.cs index 489b007d..95031fa5 100644 --- a/CSharp/TfsCmdlets.Common/Models/WorkItem/Query/QueryItem.cs +++ b/CSharp/TfsCmdlets/Models/WorkItem/Query/QueryItem.cs @@ -1,6 +1,6 @@ using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; -namespace TfsCmdlets.Models.WorkItem.Query +namespace TfsCmdlets.Models { [Model(typeof(QueryHierarchyItem))] partial class QueryItem diff --git a/CSharp/TfsCmdlets.Common/Models/WorkItemHistory.cs b/CSharp/TfsCmdlets/Models/WorkItemHistory.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Models/WorkItemHistory.cs rename to CSharp/TfsCmdlets/Models/WorkItemHistory.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IAsyncOperationAwaiter.cs b/CSharp/TfsCmdlets/Services/IAsyncOperationAwaiter.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IAsyncOperationAwaiter.cs rename to CSharp/TfsCmdlets/Services/IAsyncOperationAwaiter.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IController.cs b/CSharp/TfsCmdlets/Services/IController.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IController.cs rename to CSharp/TfsCmdlets/Services/IController.cs diff --git a/CSharp/TfsCmdlets.Common/Services/ICurrentConnections.cs b/CSharp/TfsCmdlets/Services/ICurrentConnections.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/ICurrentConnections.cs rename to CSharp/TfsCmdlets/Services/ICurrentConnections.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IDataManager.cs b/CSharp/TfsCmdlets/Services/IDataManager.cs similarity index 96% rename from CSharp/TfsCmdlets.Common/Services/IDataManager.cs rename to CSharp/TfsCmdlets/Services/IDataManager.cs index a7100887..ce7b3c6d 100644 --- a/CSharp/TfsCmdlets.Common/Services/IDataManager.cs +++ b/CSharp/TfsCmdlets/Services/IDataManager.cs @@ -42,8 +42,6 @@ public interface IDataManager bool TryGetTeam(out WebApiTeam returnTeam, bool includeSettings = false, bool includeMembers = false); - T GetClient(object overridingParameters = null); - T GetService(object overridingParameters = null); } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets.Common/Services/IDescriptorService.cs b/CSharp/TfsCmdlets/Services/IDescriptorService.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IDescriptorService.cs rename to CSharp/TfsCmdlets/Services/IDescriptorService.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IInteractiveAuthentication.cs b/CSharp/TfsCmdlets/Services/IInteractiveAuthentication.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IInteractiveAuthentication.cs rename to CSharp/TfsCmdlets/Services/IInteractiveAuthentication.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IKnownWorkItemLinkTypes.cs b/CSharp/TfsCmdlets/Services/IKnownWorkItemLinkTypes.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IKnownWorkItemLinkTypes.cs rename to CSharp/TfsCmdlets/Services/IKnownWorkItemLinkTypes.cs diff --git a/CSharp/TfsCmdlets.Common/Services/ILegacyWorkItemService.cs b/CSharp/TfsCmdlets/Services/ILegacyWorkItemService.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/ILegacyWorkItemService.cs rename to CSharp/TfsCmdlets/Services/ILegacyWorkItemService.cs diff --git a/CSharp/TfsCmdlets.Common/Services/ILogger.cs b/CSharp/TfsCmdlets/Services/ILogger.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/ILogger.cs rename to CSharp/TfsCmdlets/Services/ILogger.cs diff --git a/CSharp/TfsCmdlets.Common/Services/INodeUtil.cs b/CSharp/TfsCmdlets/Services/INodeUtil.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/INodeUtil.cs rename to CSharp/TfsCmdlets/Services/INodeUtil.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IPaginator.cs b/CSharp/TfsCmdlets/Services/IPaginator.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IPaginator.cs rename to CSharp/TfsCmdlets/Services/IPaginator.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IParameterManager.cs b/CSharp/TfsCmdlets/Services/IParameterManager.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IParameterManager.cs rename to CSharp/TfsCmdlets/Services/IParameterManager.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IPowerShellService.cs b/CSharp/TfsCmdlets/Services/IPowerShellService.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IPowerShellService.cs rename to CSharp/TfsCmdlets/Services/IPowerShellService.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IProcessUtil.cs b/CSharp/TfsCmdlets/Services/IProcessUtil.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IProcessUtil.cs rename to CSharp/TfsCmdlets/Services/IProcessUtil.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IRegistryService.cs b/CSharp/TfsCmdlets/Services/IRegistryService.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IRegistryService.cs rename to CSharp/TfsCmdlets/Services/IRegistryService.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IRestApiService.cs b/CSharp/TfsCmdlets/Services/IRestApiService.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IRestApiService.cs rename to CSharp/TfsCmdlets/Services/IRestApiService.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IRuntimeUtil.cs b/CSharp/TfsCmdlets/Services/IRuntimeUtil.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IRuntimeUtil.cs rename to CSharp/TfsCmdlets/Services/IRuntimeUtil.cs diff --git a/CSharp/TfsCmdlets.Common/Services/ITfsServiceProvider.cs b/CSharp/TfsCmdlets/Services/ITfsServiceProvider.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/ITfsServiceProvider.cs rename to CSharp/TfsCmdlets/Services/ITfsServiceProvider.cs diff --git a/CSharp/TfsCmdlets.Common/Services/ITfsVersionTable.cs b/CSharp/TfsCmdlets/Services/ITfsVersionTable.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/ITfsVersionTable.cs rename to CSharp/TfsCmdlets/Services/ITfsVersionTable.cs diff --git a/CSharp/TfsCmdlets.Common/Services/IWorkItemPatchBuilder.cs b/CSharp/TfsCmdlets/Services/IWorkItemPatchBuilder.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/IWorkItemPatchBuilder.cs rename to CSharp/TfsCmdlets/Services/IWorkItemPatchBuilder.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/AsyncOperationAwaiterImpl.cs b/CSharp/TfsCmdlets/Services/Impl/AsyncOperationAwaiterImpl.cs similarity index 80% rename from CSharp/TfsCmdlets.Common/Services/Impl/AsyncOperationAwaiterImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/AsyncOperationAwaiterImpl.cs index deba97a1..761e513c 100644 --- a/CSharp/TfsCmdlets.Common/Services/Impl/AsyncOperationAwaiterImpl.cs +++ b/CSharp/TfsCmdlets/Services/Impl/AsyncOperationAwaiterImpl.cs @@ -9,6 +9,8 @@ internal class AsyncOperationAwaiterImpl: IAsyncOperationAwaiter { private IDataManager Data { get; } + private IOperationsHttpClient OperationsClient { get; } + public Operation Wait(Task operation, string errorMessage, int waitTimeInSecs = 2) { return Wait(operation.GetResult(errorMessage), waitTimeInSecs); @@ -16,8 +18,7 @@ public Operation Wait(Task operation, string errorMessage, i public Operation Wait(OperationReference operation, int waitTimeInSecs = 2) { - var client = Data.GetClient(); - var token = client.GetOperation(operation.Id) + var token = OperationsClient.GetOperation(operation.Id) .GetResult("Error getting operation status"); while ( (token.Status != OperationStatus.Succeeded) && @@ -25,16 +26,17 @@ public Operation Wait(OperationReference operation, int waitTimeInSecs = 2) (token.Status != OperationStatus.Cancelled)) { Thread.Sleep(waitTimeInSecs * 1000); - token = client.GetOperation(operation.Id) + token = OperationsClient.GetOperation(operation.Id) .GetResult("Error getting operation status"); } return token; } [ImportingConstructor] - public AsyncOperationAwaiterImpl(IDataManager dataManager) + public AsyncOperationAwaiterImpl(IDataManager dataManager, IOperationsHttpClient operationsClient) { Data = dataManager; + OperationsClient = operationsClient; } } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/CurrentConnectionsImpl.cs b/CSharp/TfsCmdlets/Services/Impl/CurrentConnectionsImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/CurrentConnectionsImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/CurrentConnectionsImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/DataManagerImpl.cs b/CSharp/TfsCmdlets/Services/Impl/DataManagerImpl.cs similarity index 97% rename from CSharp/TfsCmdlets.Common/Services/Impl/DataManagerImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/DataManagerImpl.cs index 98e9361f..09be6e62 100644 --- a/CSharp/TfsCmdlets.Common/Services/Impl/DataManagerImpl.cs +++ b/CSharp/TfsCmdlets/Services/Impl/DataManagerImpl.cs @@ -149,14 +149,6 @@ public bool TryGetTeam(out WebApiTeam team, bool includeSettings = false, bool i return team != null; } - public T GetClient(object overridingParameters = null) - { - var conn = ((ITfsServiceProvider)GetCollection(overridingParameters)); - - Logger.Log($"GetClient: Getting an instance of [{typeof(T).FullName}]"); - return (T)conn.GetClient(typeof(T)); - } - public T GetService(object overridingParameters = null) { var conn = ((ITfsServiceProvider)GetCollection(overridingParameters)); diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/DescriptorServiceImpl.cs b/CSharp/TfsCmdlets/Services/Impl/DescriptorServiceImpl.cs similarity index 53% rename from CSharp/TfsCmdlets.Common/Services/Impl/DescriptorServiceImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/DescriptorServiceImpl.cs index 212f7815..266fe444 100644 --- a/CSharp/TfsCmdlets.Common/Services/Impl/DescriptorServiceImpl.cs +++ b/CSharp/TfsCmdlets/Services/Impl/DescriptorServiceImpl.cs @@ -5,20 +5,13 @@ namespace TfsCmdlets.Services.Impl [Export(typeof(IDescriptorService)), Shared] internal class DescriptorServiceImpl: IDescriptorService { - private IDataManager Data {get;set;} + [Import] + private IGraphHttpClient Client { get; set; } public GraphDescriptorResult GetDescriptor(Guid storageKey) { - var client = Data.GetClient(); - - return client.GetDescriptorAsync(storageKey) + return Client.GetDescriptorAsync(storageKey) .GetResult("Error getting descriptor"); } - - [ImportingConstructor] - public DescriptorServiceImpl(IDataManager dataManager) - { - Data = dataManager; - } } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/InteractiveAuthenticationImpl.cs b/CSharp/TfsCmdlets/Services/Impl/InteractiveAuthenticationImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/InteractiveAuthenticationImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/InteractiveAuthenticationImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/KnownWorkItemLinkTypesImpl.cs b/CSharp/TfsCmdlets/Services/Impl/KnownWorkItemLinkTypesImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/KnownWorkItemLinkTypesImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/KnownWorkItemLinkTypesImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/LegacyWorkItemServiceImpl.cs b/CSharp/TfsCmdlets/Services/Impl/LegacyWorkItemServiceImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/LegacyWorkItemServiceImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/LegacyWorkItemServiceImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/LoggerImpl.cs b/CSharp/TfsCmdlets/Services/Impl/LoggerImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/LoggerImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/LoggerImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/NodeUtilImpl.cs b/CSharp/TfsCmdlets/Services/Impl/NodeUtilImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/NodeUtilImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/NodeUtilImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/PaginatorImpl.cs b/CSharp/TfsCmdlets/Services/Impl/PaginatorImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/PaginatorImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/PaginatorImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/ParameterManagerImpl.cs b/CSharp/TfsCmdlets/Services/Impl/ParameterManagerImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/ParameterManagerImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/ParameterManagerImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/PowerShellServiceImpl.cs b/CSharp/TfsCmdlets/Services/Impl/PowerShellServiceImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/PowerShellServiceImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/PowerShellServiceImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/ProcessUtil.cs b/CSharp/TfsCmdlets/Services/Impl/ProcessUtil.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/ProcessUtil.cs rename to CSharp/TfsCmdlets/Services/Impl/ProcessUtil.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/RegistryServiceImpl.cs b/CSharp/TfsCmdlets/Services/Impl/RegistryServiceImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/RegistryServiceImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/RegistryServiceImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/RestApiServiceImpl.cs b/CSharp/TfsCmdlets/Services/Impl/RestApiServiceImpl.cs similarity index 96% rename from CSharp/TfsCmdlets.Common/Services/Impl/RestApiServiceImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/RestApiServiceImpl.cs index 5d69eb71..586b43ea 100644 --- a/CSharp/TfsCmdlets.Common/Services/Impl/RestApiServiceImpl.cs +++ b/CSharp/TfsCmdlets/Services/Impl/RestApiServiceImpl.cs @@ -11,9 +11,11 @@ namespace TfsCmdlets.Services.Impl [Export(typeof(IRestApiService))] public class RestApiServiceImpl : IRestApiService { - private GenericHttpClient _client; + private IGenericHttpClient Client {get; set;} + private ILogger Logger { get; set; } - public Uri Url => _client.Uri; + + public Uri Url => Client.GetUri(); Task IRestApiService.InvokeTemplateAsync( Models.Connection connection, @@ -190,7 +192,7 @@ Task IRestApiService.QueueOperationAsync( queryParameters, apiVersion); } - private GenericHttpClient GetClient(Models.Connection connection, string serviceHostName) + private IGenericHttpClient GetClient(Models.Connection connection, string serviceHostName) { var conn = connection.InnerObject; var host = serviceHostName ?? conn.Uri.Host; @@ -219,7 +221,7 @@ private GenericHttpClient GetClient(Models.Connection connection, string service vssConn.CallHiddenMethod("RegisterClientServiceInstance", typeof(GenericHttpClient), client); } - return _client = client; + return Client = client; } public Task QueryContributionNodeAsync( @@ -249,9 +251,10 @@ private bool IsHttpMethod(string method) } [ImportingConstructor] - public RestApiServiceImpl(IDataManager data, ILogger logger) + public RestApiServiceImpl(ILogger logger, IGenericHttpClient client) { Logger = logger; + Client = client; } } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/RuntimeUtilImpl.cs b/CSharp/TfsCmdlets/Services/Impl/RuntimeUtilImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/RuntimeUtilImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/RuntimeUtilImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/TfsVersionTableImpl.cs b/CSharp/TfsCmdlets/Services/Impl/TfsVersionTableImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/TfsVersionTableImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/TfsVersionTableImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/Impl/WorkItemPatchBuilderImpl.cs b/CSharp/TfsCmdlets/Services/Impl/WorkItemPatchBuilderImpl.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/Impl/WorkItemPatchBuilderImpl.cs rename to CSharp/TfsCmdlets/Services/Impl/WorkItemPatchBuilderImpl.cs diff --git a/CSharp/TfsCmdlets.Common/Services/ServiceLocator.cs b/CSharp/TfsCmdlets/Services/ServiceLocator.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Services/ServiceLocator.cs rename to CSharp/TfsCmdlets/Services/ServiceLocator.cs diff --git a/CSharp/TfsCmdlets.Common/ShellHelper.cs b/CSharp/TfsCmdlets/ShellHelper.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/ShellHelper.cs rename to CSharp/TfsCmdlets/ShellHelper.cs diff --git a/CSharp/TfsCmdlets/TfsCmdlets.csproj b/CSharp/TfsCmdlets/TfsCmdlets.csproj index 87819e31..f5aef48d 100644 --- a/CSharp/TfsCmdlets/TfsCmdlets.csproj +++ b/CSharp/TfsCmdlets/TfsCmdlets.csproj @@ -21,7 +21,6 @@ - @@ -35,6 +34,8 @@ + + diff --git a/CSharp/TfsCmdlets.Common/Util/ErrorUtil.cs b/CSharp/TfsCmdlets/Util/ErrorUtil.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Util/ErrorUtil.cs rename to CSharp/TfsCmdlets/Util/ErrorUtil.cs diff --git a/CSharp/TfsCmdlets.Common/Util/LazyProperty.cs b/CSharp/TfsCmdlets/Util/LazyProperty.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Util/LazyProperty.cs rename to CSharp/TfsCmdlets/Util/LazyProperty.cs diff --git a/CSharp/TfsCmdlets.Common/Util/Mru.cs b/CSharp/TfsCmdlets/Util/Mru.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Util/Mru.cs rename to CSharp/TfsCmdlets/Util/Mru.cs diff --git a/CSharp/TfsCmdlets.Common/Util/PSJsonConverter.cs b/CSharp/TfsCmdlets/Util/PSJsonConverter.cs similarity index 100% rename from CSharp/TfsCmdlets.Common/Util/PSJsonConverter.cs rename to CSharp/TfsCmdlets/Util/PSJsonConverter.cs From f78bbb03315929fffe8bbc0c97d27e9add4696e7 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 2 Aug 2024 02:53:44 -0300 Subject: [PATCH 16/90] Remove obsolete project --- .../TfsCmdlets.Common.csproj | 66 ------------------- 1 file changed, 66 deletions(-) delete mode 100644 CSharp/TfsCmdlets.Common/TfsCmdlets.Common.csproj diff --git a/CSharp/TfsCmdlets.Common/TfsCmdlets.Common.csproj b/CSharp/TfsCmdlets.Common/TfsCmdlets.Common.csproj deleted file mode 100644 index a5cabc0d..00000000 --- a/CSharp/TfsCmdlets.Common/TfsCmdlets.Common.csproj +++ /dev/null @@ -1,66 +0,0 @@ - - - - netcoreapp3.1;net471 - TfsCmdlets - true - 8.0 - $(TargetPath)\TfsCmdlets.xml - 1591 - preview - - - - true - obj/Generated - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_Parameter1>TfsCmdlets - - - <_Parameter1>TfsCmdlets.Legacy - - - <_Parameter1>TfsCmdlets.Tests.UnitTests - - - \ No newline at end of file From 694c63ee7b4f3f452a27b9fe37444a612640e3d8 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 2 Aug 2024 03:45:36 -0300 Subject: [PATCH 17/90] Fix source gen --- .../DiagnosticDescriptors.cs | 7 +++ .../TfsCmdlets.SourceGenerators/Extensions.cs | 13 ++++- .../Generators/Controllers/Analyzers.cs | 48 +++++++++++++++++++ .../Generators/Controllers/ControllerInfo.cs | 14 +++++- .../Generators/Controllers/TypeProcessor.cs | 2 +- .../Cmdlets/Identity/Group/NewGroup.cs | 2 +- .../ServiceHook/GetServiceHookConsumer.cs | 2 +- CSharp/TfsCmdlets/Cmdlets/Team/RemoveTeam.cs | 2 +- .../Cmdlets/Team/TeamAdmin/RemoveTeamAdmin.cs | 2 +- .../GetClassificationNodeController.cs | 5 +- .../MoveClassificationNodeController.cs | 4 +- .../NewClassificationNodeController.cs | 5 +- .../RemoveClassificationNodeController.cs | 4 +- .../RenameClassificationNodeController.cs | 5 +- CSharp/TfsCmdlets/Services/ServiceLocator.cs | 1 - 15 files changed, 93 insertions(+), 23 deletions(-) diff --git a/CSharp/TfsCmdlets.SourceGenerators/DiagnosticDescriptors.cs b/CSharp/TfsCmdlets.SourceGenerators/DiagnosticDescriptors.cs index 373c0b78..286661a1 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/DiagnosticDescriptors.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/DiagnosticDescriptors.cs @@ -27,5 +27,12 @@ internal static class DiagnosticDescriptors "CodeGen", DiagnosticSeverity.Error, true); + + internal static DiagnosticDescriptor ClientMustBeInterface { get; } = new DiagnosticDescriptor( + "TFS004", + "HttpClient attribute must refer to a valid interface", "'{0}' must be an interface, derived from TfsCmdlets.HttpClients.IVssHttpClient.", + "CodeGen", + DiagnosticSeverity.Error, + true); } } diff --git a/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs b/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs index 81c925d6..ac4195fc 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs @@ -72,14 +72,23 @@ public static int FindIndex(this string input, Predicate predicate, int st } public static string GetImportingConstructorArguments(this INamedTypeSymbol type, INamedTypeSymbol baseClass) - => string.Join(", ", type.GetPropertiesWithAttribute("ImportAttribute") + { + var parms = type.GetPropertiesWithAttribute("ImportAttribute") .Select(p => $"{p.Type.Name} {p.Name[0].ToString().ToLower()}{p.Name.Substring(1)}") .Concat(baseClass .Constructors[0] .Parameters .Select(p => $"{p.Type.Name} {p.Name}") ) - ); + .ToList(); + + var client = type.GetAttributeNamedValue("CmdletControllerAttribute", "Client"); + + if (client != null) parms.Add($"{client.FullName()} client"); + + + return string.Join(", ", parms); + } public static string GetConstructorArguments(this INamedTypeSymbol type) { diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/Analyzers.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/Analyzers.cs index 9f8631c7..fab01ded 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/Analyzers.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/Analyzers.cs @@ -51,4 +51,52 @@ private static IEnumerable GetFilters() yield return new Generators.Controllers.Filter(); } } + + /// + /// Analyzer for "Client must be interface" + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class ClientMustBeInterface : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(DiagnosticDescriptors.ClientMustBeInterface); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSymbolAction(AnalyzeNamedType, SymbolKind.NamedType); + } + + private static void AnalyzeNamedType(SymbolAnalysisContext context) + { + var type = (INamedTypeSymbol)context.Symbol; + var filters = GetFilters().ToList(); + + foreach (var declaringSyntaxReference in type.DeclaringSyntaxReferences) + { + if (declaringSyntaxReference.GetSyntax() is not TypeDeclarationSyntax cds) continue; + if (!filters.Any(filter => filter.ShouldProcessType(type))) continue; + + var clientType = type.GetAttributeNamedValue("CmdletControllerAttribute", "Client"); + if(clientType == null) continue; + + if ((clientType.TypeKind == TypeKind.Interface) && clientType.Interfaces.Any(i => i.Name.Equals("IVssHttpClient"))) continue; + + var error = Diagnostic.Create(DiagnosticDescriptors.ClientMustBeInterface, + cds.Identifier.GetLocation(), + clientType.Name, + DiagnosticSeverity.Error); + + Debug.WriteLine($"[TfsCmdlets.Analyzer] Adding {type}"); + + context.ReportDiagnostic(error); + } + } + + private static IEnumerable GetFilters() + { + yield return new Generators.Controllers.Filter(); + } + } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs index 0f2fd9af..fa56ccbf 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/ControllerInfo.cs @@ -84,8 +84,18 @@ private void GenerateProperties() } private static string GetImportingConstructorBody(INamedTypeSymbol type) - => string.Join("\n", type.GetPropertiesWithAttribute("ImportAttribute") - .Select(p => $" {p.Name} = {p.Name[0].ToString().ToLower()}{p.Name.Substring(1)};")); + { + var parms = type + .GetPropertiesWithAttribute("ImportAttribute") + .Select(p => $" {p.Name} = {p.Name[0].ToString().ToLower()}{p.Name.Substring(1)};") + .ToList(); + + var client = type.GetAttributeNamedValue("CmdletControllerAttribute", "Client"); + + if (client != null) parms.Add($" Client = client;"); + + return string.Join("\n", parms); + } private string GetUsingStatements(INamedTypeSymbol cmdlet) => cmdlet.GetDeclaringSyntax().FindParentOfType()?.Usings.ToString(); diff --git a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs index aef36784..266e4a01 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Generators/Controllers/TypeProcessor.cs @@ -30,7 +30,7 @@ public override string GenerateCode() if(controller.Client != null) { - clientProp = $"[Import] private {controller.Client.FullName()} Client {{ get; set; }}"; + clientProp = $"private {controller.Client.FullName()} Client {{ get; }}"; } foreach (var prop in controller.GeneratedProperties.Values) diff --git a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs index c74c7417..5d6cd719 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs @@ -29,7 +29,7 @@ partial class NewGroup public GroupScope Scope { get; set; } = GroupScope.Collection; } - [CmdletController(typeof(GraphGroup), Client=typeof(GraphHttpClient))] + [CmdletController(typeof(GraphGroup), Client=typeof(IGraphHttpClient))] partial class NewGroupController { [Import] diff --git a/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookConsumer.cs b/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookConsumer.cs index 3a62ae19..314d00f3 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookConsumer.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ServiceHook/GetServiceHookConsumer.cs @@ -27,7 +27,7 @@ partial class GetServiceHookConsumer public string Consumer { get; set; } = "*"; } - [CmdletController(typeof(WebApiConsumer), Client=typeof(ServiceHooksPublisherHttpClient))] + [CmdletController(typeof(WebApiConsumer), Client=typeof(IServiceHooksPublisherHttpClient))] partial class GetServiceHookConsumerController { protected override IEnumerable Run() diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/RemoveTeam.cs b/CSharp/TfsCmdlets/Cmdlets/Team/RemoveTeam.cs index c18fbd57..7c723ca7 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/RemoveTeam.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/RemoveTeam.cs @@ -18,7 +18,7 @@ partial class RemoveTeam public object Team { get; set; } } - [CmdletController(typeof(Models.Team), Client=typeof(TeamHttpClient))] + [CmdletController(typeof(Models.Team), Client=typeof(ITeamHttpClient))] partial class RemoveTeamController { protected override IEnumerable Run() diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/RemoveTeamAdmin.cs b/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/RemoveTeamAdmin.cs index 85933d07..935b9889 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/RemoveTeamAdmin.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/TeamAdmin/RemoveTeamAdmin.cs @@ -17,7 +17,7 @@ partial class RemoveTeamAdmin public object Admin { get; set; } } - [CmdletController(typeof(Models.TeamAdmin), Client=typeof(TeamAdminHttpClient))] + [CmdletController(typeof(Models.TeamAdmin), Client=typeof(ITeamAdminHttpClient))] partial class RemoveTeamAdminController { protected override IEnumerable Run() diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/GetClassificationNodeController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/GetClassificationNodeController.cs index 487be4e2..ed6f2e5f 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/GetClassificationNodeController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/GetClassificationNodeController.cs @@ -6,10 +6,8 @@ namespace TfsCmdlets.Cmdlets.WorkItem.AreasIterations { internal abstract class GetClassificationNodeController: ControllerBase { - [Import] private INodeUtil NodeUtil { get; set; } - [Import] private IWorkItemTrackingHttpClient Client { get; set; } protected override IEnumerable Run() @@ -83,10 +81,11 @@ protected override IEnumerable Run() } [ImportingConstructor] - protected GetClassificationNodeController(INodeUtil nodeUtil, IPowerShellService powerShell, IDataManager data, IParameterManager parameters, ILogger logger) + protected GetClassificationNodeController(INodeUtil nodeUtil, IPowerShellService powerShell, IDataManager data, IParameterManager parameters, ILogger logger, IWorkItemTrackingHttpClient client) : base(powerShell, data, parameters, logger) { NodeUtil = nodeUtil; + Client = client; } } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/MoveClassificationNodeController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/MoveClassificationNodeController.cs index 35829079..0aeef95e 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/MoveClassificationNodeController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/MoveClassificationNodeController.cs @@ -7,7 +7,6 @@ namespace TfsCmdlets.Cmdlets.WorkItem.AreasIterations { internal abstract class MoveClassificationNodeController : ControllerBase { - [Import] private IWorkItemTrackingHttpClient Client { get; set; } protected override IEnumerable Run() @@ -60,9 +59,10 @@ protected override IEnumerable Run() } [ImportingConstructor] - protected MoveClassificationNodeController(IPowerShellService powerShell, IDataManager data, IParameterManager parameters, ILogger logger) + protected MoveClassificationNodeController(IPowerShellService powerShell, IDataManager data, IParameterManager parameters, ILogger logger, IWorkItemTrackingHttpClient client) : base(powerShell, data, parameters, logger) { + Client = client; } } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/NewClassificationNodeController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/NewClassificationNodeController.cs index 2a9b43df..222ab0b0 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/NewClassificationNodeController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/NewClassificationNodeController.cs @@ -6,10 +6,8 @@ namespace TfsCmdlets.Cmdlets.WorkItem.AreasIterations { internal abstract class NewClassificationNodeController: ControllerBase { - [Import] private INodeUtil NodeUtil { get; } - [Import] private IWorkItemTrackingHttpClient Client { get; set; } protected override IEnumerable Run() @@ -69,10 +67,11 @@ protected override IEnumerable Run() } [ImportingConstructor] - protected NewClassificationNodeController(INodeUtil nodeUtil, IPowerShellService powerShell, IDataManager data, IParameterManager parameters, ILogger logger) + protected NewClassificationNodeController(INodeUtil nodeUtil, IPowerShellService powerShell, IDataManager data, IParameterManager parameters, ILogger logger, IWorkItemTrackingHttpClient client) : base(powerShell, data, parameters, logger) { NodeUtil = nodeUtil; + Client = client; } } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RemoveClassificationNodeController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RemoveClassificationNodeController.cs index 0a94c480..b3dd4477 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RemoveClassificationNodeController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RemoveClassificationNodeController.cs @@ -6,7 +6,6 @@ namespace TfsCmdlets.Cmdlets.WorkItem.AreasIterations { internal abstract class RemoveClassificationNodeController: ControllerBase { - [Import] private IWorkItemTrackingHttpClient Client { get; set; } protected override IEnumerable Run() @@ -47,9 +46,10 @@ protected override IEnumerable Run() } [ImportingConstructor] - protected RemoveClassificationNodeController(IPowerShellService powerShell, IDataManager data, IParameterManager parameters, ILogger logger) + protected RemoveClassificationNodeController(IPowerShellService powerShell, IDataManager data, IParameterManager parameters, ILogger logger, IWorkItemTrackingHttpClient client) : base(powerShell, data, parameters, logger) { + Client = client; } } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RenameClassificationNodeController.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RenameClassificationNodeController.cs index 292490f7..9410fdd6 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RenameClassificationNodeController.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/AreasIterations/RenameClassificationNodeController.cs @@ -8,10 +8,8 @@ namespace TfsCmdlets.Cmdlets.WorkItem.AreasIterations public abstract class RenameClassificationNodeController: ControllerBase { - [Import] private INodeUtil NodeUtil { get; set; } - [Import] private IWorkItemTrackingHttpClient Client { get; set; } protected override IEnumerable Run() @@ -43,10 +41,11 @@ protected override IEnumerable Run() } [ImportingConstructor] - protected RenameClassificationNodeController(INodeUtil nodeUtil, IPowerShellService powerShell, IDataManager data, IParameterManager parameters, ILogger logger) + protected RenameClassificationNodeController(INodeUtil nodeUtil, IPowerShellService powerShell, IDataManager data, IParameterManager parameters, ILogger logger, IWorkItemTrackingHttpClient client) : base(powerShell, data, parameters, logger) { NodeUtil = nodeUtil; + Client = client; } } } \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Services/ServiceLocator.cs b/CSharp/TfsCmdlets/Services/ServiceLocator.cs index 02e5d1fe..7abb3a9f 100644 --- a/CSharp/TfsCmdlets/Services/ServiceLocator.cs +++ b/CSharp/TfsCmdlets/Services/ServiceLocator.cs @@ -19,7 +19,6 @@ public static CompositionHost Instance { return _instance ??= new ContainerConfiguration() .WithAssembly(Assembly.GetAssembly(_site.GetType())) - .WithAssembly(Assembly.GetAssembly(typeof(ServiceLocator))) .CreateContainer(); } } From f151479d31081f15e471f59b6d70853097f2af8a Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 2 Aug 2024 03:56:28 -0300 Subject: [PATCH 18/90] Move file --- CSharp/TfsCmdlets/{Controllers => Cmdlets}/ControllerBase.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CSharp/TfsCmdlets/{Controllers => Cmdlets}/ControllerBase.cs (100%) diff --git a/CSharp/TfsCmdlets/Controllers/ControllerBase.cs b/CSharp/TfsCmdlets/Cmdlets/ControllerBase.cs similarity index 100% rename from CSharp/TfsCmdlets/Controllers/ControllerBase.cs rename to CSharp/TfsCmdlets/Cmdlets/ControllerBase.cs From f03f8b68bf2fd1b9b05932dddc52fd401745a2d2 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 2 Aug 2024 04:04:20 -0300 Subject: [PATCH 19/90] Update release notes +semver: minor --- .../WorkItem/Field/GetWorkItemField.cs | 2 +- Docs/ReleaseNotes/2.9.0.md | 11 ++++++++++ RELEASENOTES.md | 20 +++++++++---------- 3 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 Docs/ReleaseNotes/2.9.0.md diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs index 5a9bbf8b..8b5348f2 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs @@ -5,7 +5,7 @@ namespace TfsCmdlets.Cmdlets.WorkItem.Field { /// - /// Gets information from one or more process templates. + /// Gets information from one or more organization-wide work item fields. /// [TfsCmdlet(CmdletScope.Collection, OutputType = typeof(WorkItemField))] partial class GetWorkItemField diff --git a/Docs/ReleaseNotes/2.9.0.md b/Docs/ReleaseNotes/2.9.0.md new file mode 100644 index 00000000..84b40586 --- /dev/null +++ b/Docs/ReleaseNotes/2.9.0.md @@ -0,0 +1,11 @@ +# TfsCmdlets Release Notes + +## Version 2.9.0 (_03/Aug/2024_) + +This release adds some new process-related cmdlets. + +### New cmdlets + +- `Get-TfsWorkItemField`: Gets information from one or more organization-wide work item fields +- `New-TfsWorkItemField`: Creates a new organization-wide work item field +- `Remove-TfsWorkItemField`: Removes an organization-wide work item field diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 40ef8eb1..35f8d48f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,23 +1,23 @@ # TfsCmdlets Release Notes -## Version 2.8.2 (_24/Jul/2024_) - -This release brings some fixes and enhancements. - -### Enhancements +## Version 2.9.0 (_03/Aug/2024_) -- `Add-TfsWorkItemLink`: Added support for specifying the arguments `-SuppressNotification` and `-BypassRules` to suppress notifications and bypass rules when adding a work item link. -- Now when when a cmdlet fails, a full exception dump will be sent to the verbose output stream in addition to the short error message sent to the error stream. So, if your script is failing and you don't know why, you can enable the verbose output to get more information about the error. +This release adds some new process-related cmdlets. -### Fixes +### New cmdlets -- Fixed an issue where the `Get-TfsTeam` cmdlet would not work when specifying the `-Default` parameter. -- Fixed an issue where `Invoke-TfsRestApi` would ignore the parameters passed via the `-Parameters` argument [[#228](https://github.com/igoravl/TfsCmdlets/issues/)] +- `Get-TfsWorkItemField`: Gets information from one or more organization-wide work item fields +- `New-TfsWorkItemField`: Creates a new organization-wide work item field +- `Remove-TfsWorkItemField`: Removes an organization-wide work item field ----------------------- ## Previous Versions +## Version 2.8.2 (_24/Jul/2024_) + +See release notes [here](Docs/ReleaseNotes/2.8.2.md). + ### Version 2.8.1 (_16/Jul/2024_) See release notes [here](Docs/ReleaseNotes/2.8.1.md). From 7b02138529af4cf18515b9dae9bdf86000dccae8 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 08:37:31 -0300 Subject: [PATCH 20/90] Adjust rest result collection --- .github/workflows/main.yml | 7 +++++++ psake.ps1 | 9 ++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6a858481..61320b21 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,6 +37,13 @@ jobs: ./Build.ps1 -Targets Package -Config ${{ env.Config }} -Verbose:$${{ env.Debug }} -SkipReleaseNotes:$${{ env.SkipReleaseNotes }} - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 + - name: Publish Test Results + uses: dorny/test-reporter@v1 + if: always() + with: + name: Pester Test Results + path: 'out/TestResults-Pester*.xml' # Path to test results (inside artifact .zip) + reporter: dotnet-trx - name: Publish Nuget uses: actions/upload-artifact@v4 with: diff --git a/psake.ps1 b/psake.ps1 index 3ad4c82f..d9a1e57e 100644 --- a/psake.ps1 +++ b/psake.ps1 @@ -242,13 +242,16 @@ Task AllTests -PreCondition { -not $SkipTests } { try { Write-Output ' == PowerShell Core ==' - Exec { pwsh.exe -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -PassThru -ExcludeTagFilter 'Desktop' | Export-JUnitReport -Path ../../out/TestResults-Core.xml" } + Exec { pwsh.exe -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -ExcludeTagFilter 'Desktop'" } + Move-Item 'testResults.xml' -Destination $OutDir/TestResults-Pester-Core.xml -Force + Move-Item 'coverage.xml' -Destination $OutDir/Coverage-Pester-Core.xml -Force Write-Output ' == PowerShell Desktop ==' - Exec { powershell.exe -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -PassThru -ExcludeTagFilter 'Core' | Export-JUnitReport -Path ../../out/TestResults-Desktop.xml" } + Exec { powershell.exe -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -ExcludeTagFilter 'Core'" } + Move-Item 'testResults.xml' -Destination $OutDir/TestResults-Pester-Desktop.xml -Force + Move-Item 'coverage.xml' -Destination $OutDir/Coverage-Pester-Desktop.xml -Force } finally { - Remove-Item '*.xml' -Force -ErrorAction SilentlyContinue Pop-Location } } From b3b9e3489b0d62e71556ff5d43e659b1ebe0ed17 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 08:38:06 -0300 Subject: [PATCH 21/90] Add Source Gen unit test project to solution --- CSharp/TfsCmdlets.sln | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CSharp/TfsCmdlets.sln b/CSharp/TfsCmdlets.sln index 8d858da2..c25e0d0a 100644 --- a/CSharp/TfsCmdlets.sln +++ b/CSharp/TfsCmdlets.sln @@ -11,7 +11,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets", "TfsCmdlets\Tf EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets.SourceGenerators", "TfsCmdlets.SourceGenerators\TfsCmdlets.SourceGenerators.csproj", "{1045CB54-F17F-42EA-9497-5D60815BDC64}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TfsCmdlets.SourceGenerators.UnitTests", "TfsCmdlets.SourceGenerators.UnitTests\TfsCmdlets.SourceGenerators.UnitTests.csproj", "{76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TfsCmdlets.SourceGenerators.UnitTests", "TfsCmdlets.SourceGeneratores.UnitTests\TfsCmdlets.SourceGenerators.UnitTests.csproj", "{3C1104F8-09E2-4E36-960B-BC4340FA2B3F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -31,17 +31,17 @@ Global {1045CB54-F17F-42EA-9497-5D60815BDC64}.Debug|Any CPU.Build.0 = Debug|Any CPU {1045CB54-F17F-42EA-9497-5D60815BDC64}.Release|Any CPU.ActiveCfg = Release|Any CPU {1045CB54-F17F-42EA-9497-5D60815BDC64}.Release|Any CPU.Build.0 = Release|Any CPU - {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7}.Release|Any CPU.Build.0 = Release|Any CPU + {3C1104F8-09E2-4E36-960B-BC4340FA2B3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C1104F8-09E2-4E36-960B-BC4340FA2B3F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C1104F8-09E2-4E36-960B-BC4340FA2B3F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C1104F8-09E2-4E36-960B-BC4340FA2B3F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {1D239B0E-69CB-46E3-9D9A-A41349AF3BB9} = {990DD3E9-3685-472A-AB57-2D304A5C86D6} - {76E5ACA3-72D5-41DD-8BF6-9C6DC246D0B7} = {990DD3E9-3685-472A-AB57-2D304A5C86D6} + {3C1104F8-09E2-4E36-960B-BC4340FA2B3F} = {990DD3E9-3685-472A-AB57-2D304A5C86D6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FE0EFD1C-9A73-41C3-BD86-7E8112462C2B} From 6337a9a700370485153793d9105ed8fe8c78b0a3 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 08:51:42 -0300 Subject: [PATCH 22/90] Rename existing tests --- ...fsConfigurationConnectionString.Tests.ps1} | 2 +- .../GetTfsInstallationPath.Tests.ps1} | 2 +- .../GetTfsVersion.Tests.ps1} | 2 +- .../GetTfsExtension.Tests.ps1} | 2 +- .../Build/GetTfsBuildDefinition.Tests.ps1} | 2 +- .../GetTfsBuildDefinitionFolder.Tests.ps1} | 2 +- .../InvokeTfsRestApi.Tests.ps1} | 2 +- .../GetTfsTeam.Tests.ps1} | 2 +- .../GetTfsTeamProject.Tests.ps1} | 2 +- .../GetTfsWiki.Tests.ps1} | 2 +- .../GetTfsIteration.Tests.ps1} | 2 +- .../GetTfsWorkItem.Tests.ps1} | 2 +- .../History/GetTfsWorkItemHistory.Tests.ps1} | 2 +- .../Query/GetTfsWorkItemQuery.Tests.ps1} | 2 +- .../GetTfsWorkItemQueryFolder.Tests.ps1} | 2 +- ...tems.AreasIterations.Get-TfsArea.Tests.ps1 | 20 -------------- PS/_Tests/testResults.xml | 27 +++++++++++++++++++ 17 files changed, 42 insertions(+), 35 deletions(-) rename PS/_Tests/{Admin.Get-TfsConfigurationConnectionString.Tests.ps1 => Admin/GetTfsConfigurationConnectionString.Tests.ps1} (74%) rename PS/_Tests/{Admin.Get-TfsInstallationPath.Tests.ps1 => Admin/GetTfsInstallationPath.Tests.ps1} (89%) rename PS/_Tests/{Admin.Get-TfsVersion.Tests.ps1 => Admin/GetTfsVersion.Tests.ps1} (89%) rename PS/_Tests/{ExtensionManagement.Get-TfsExtension.ps1 => ExtensionManagement/GetTfsExtension.Tests.ps1} (94%) rename PS/_Tests/{Pipeline.Get-TfsBuildDefinition.Tests.ps1 => Pipeline/Build/GetTfsBuildDefinition.Tests.ps1} (83%) rename PS/_Tests/{Pipeline.Get-TfsBuildDefinitionFolder.Tests.ps1 => Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1} (83%) rename PS/_Tests/{RestApi.Invoke-TfsRestApi.Tests.ps1 => RestApi/InvokeTfsRestApi.Tests.ps1} (97%) rename PS/_Tests/{Team.Get-TfsTeam.Tests.ps1 => Team/GetTfsTeam.Tests.ps1} (95%) rename PS/_Tests/{TeamProject.Get-TfsTeamProject.Tests.ps1 => TeamProject/GetTfsTeamProject.Tests.ps1} (94%) rename PS/_Tests/{Wiki.Get-TfsWiki.Tests.ps1 => Wiki/GetTfsWiki.Tests.ps1} (90%) rename PS/_Tests/{WorkItems.AreasIterations.Get-TfsIteration.Tests.ps1 => WorkItem/AreasIterations/GetTfsIteration.Tests.ps1} (94%) rename PS/_Tests/{WorkItems.Get-TfsWorkItem.Tests.ps1 => WorkItem/GetTfsWorkItem.Tests.ps1} (98%) rename PS/_Tests/{WorkItems.Get-TfsWorkItemHistory.Tests.ps1 => WorkItem/History/GetTfsWorkItemHistory.Tests.ps1} (78%) rename PS/_Tests/{WorkItems.Queries.Get-TfsWorkItemQuery.Tests.ps1 => WorkItem/Query/GetTfsWorkItemQuery.Tests.ps1} (98%) rename PS/_Tests/{WorkItems.Queries.Get-TfsWorkItemQueryFolder.Tests.ps1 => WorkItem/Query/GetTfsWorkItemQueryFolder.Tests.ps1} (97%) delete mode 100644 PS/_Tests/WorkItems.AreasIterations.Get-TfsArea.Tests.ps1 create mode 100644 PS/_Tests/testResults.xml diff --git a/PS/_Tests/Admin.Get-TfsConfigurationConnectionString.Tests.ps1 b/PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 similarity index 74% rename from PS/_Tests/Admin.Get-TfsConfigurationConnectionString.Tests.ps1 rename to PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 index 10cbdfe7..abe3349a 100644 --- a/PS/_Tests/Admin.Get-TfsConfigurationConnectionString.Tests.ps1 +++ b/PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/Admin.Get-TfsInstallationPath.Tests.ps1 b/PS/_Tests/Admin/GetTfsInstallationPath.Tests.ps1 similarity index 89% rename from PS/_Tests/Admin.Get-TfsInstallationPath.Tests.ps1 rename to PS/_Tests/Admin/GetTfsInstallationPath.Tests.ps1 index 51c8358b..28375bfc 100644 --- a/PS/_Tests/Admin.Get-TfsInstallationPath.Tests.ps1 +++ b/PS/_Tests/Admin/GetTfsInstallationPath.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/Admin.Get-TfsVersion.Tests.ps1 b/PS/_Tests/Admin/GetTfsVersion.Tests.ps1 similarity index 89% rename from PS/_Tests/Admin.Get-TfsVersion.Tests.ps1 rename to PS/_Tests/Admin/GetTfsVersion.Tests.ps1 index d6c1a8cf..c5cb9f7d 100644 --- a/PS/_Tests/Admin.Get-TfsVersion.Tests.ps1 +++ b/PS/_Tests/Admin/GetTfsVersion.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/ExtensionManagement.Get-TfsExtension.ps1 b/PS/_Tests/ExtensionManagement/GetTfsExtension.Tests.ps1 similarity index 94% rename from PS/_Tests/ExtensionManagement.Get-TfsExtension.ps1 rename to PS/_Tests/ExtensionManagement/GetTfsExtension.Tests.ps1 index 1c530e17..d1124f60 100644 --- a/PS/_Tests/ExtensionManagement.Get-TfsExtension.ps1 +++ b/PS/_Tests/ExtensionManagement/GetTfsExtension.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/Pipeline.Get-TfsBuildDefinition.Tests.ps1 b/PS/_Tests/Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 similarity index 83% rename from PS/_Tests/Pipeline.Get-TfsBuildDefinition.Tests.ps1 rename to PS/_Tests/Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 index d65f8245..329e4f90 100644 --- a/PS/_Tests/Pipeline.Get-TfsBuildDefinition.Tests.ps1 +++ b/PS/_Tests/Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/Pipeline.Get-TfsBuildDefinitionFolder.Tests.ps1 b/PS/_Tests/Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 similarity index 83% rename from PS/_Tests/Pipeline.Get-TfsBuildDefinitionFolder.Tests.ps1 rename to PS/_Tests/Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 index 719068e0..f466fb83 100644 --- a/PS/_Tests/Pipeline.Get-TfsBuildDefinitionFolder.Tests.ps1 +++ b/PS/_Tests/Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/RestApi.Invoke-TfsRestApi.Tests.ps1 b/PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 similarity index 97% rename from PS/_Tests/RestApi.Invoke-TfsRestApi.Tests.ps1 rename to PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 index d8778066..cb39915d 100644 --- a/PS/_Tests/RestApi.Invoke-TfsRestApi.Tests.ps1 +++ b/PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/Team.Get-TfsTeam.Tests.ps1 b/PS/_Tests/Team/GetTfsTeam.Tests.ps1 similarity index 95% rename from PS/_Tests/Team.Get-TfsTeam.Tests.ps1 rename to PS/_Tests/Team/GetTfsTeam.Tests.ps1 index 7a394d4b..8b319533 100644 --- a/PS/_Tests/Team.Get-TfsTeam.Tests.ps1 +++ b/PS/_Tests/Team/GetTfsTeam.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/TeamProject.Get-TfsTeamProject.Tests.ps1 b/PS/_Tests/TeamProject/GetTfsTeamProject.Tests.ps1 similarity index 94% rename from PS/_Tests/TeamProject.Get-TfsTeamProject.Tests.ps1 rename to PS/_Tests/TeamProject/GetTfsTeamProject.Tests.ps1 index c9fcf6e3..fe2fb781 100644 --- a/PS/_Tests/TeamProject.Get-TfsTeamProject.Tests.ps1 +++ b/PS/_Tests/TeamProject/GetTfsTeamProject.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/Wiki.Get-TfsWiki.Tests.ps1 b/PS/_Tests/Wiki/GetTfsWiki.Tests.ps1 similarity index 90% rename from PS/_Tests/Wiki.Get-TfsWiki.Tests.ps1 rename to PS/_Tests/Wiki/GetTfsWiki.Tests.ps1 index 89013e3c..7d976ba2 100644 --- a/PS/_Tests/Wiki.Get-TfsWiki.Tests.ps1 +++ b/PS/_Tests/Wiki/GetTfsWiki.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/WorkItems.AreasIterations.Get-TfsIteration.Tests.ps1 b/PS/_Tests/WorkItem/AreasIterations/GetTfsIteration.Tests.ps1 similarity index 94% rename from PS/_Tests/WorkItems.AreasIterations.Get-TfsIteration.Tests.ps1 rename to PS/_Tests/WorkItem/AreasIterations/GetTfsIteration.Tests.ps1 index 41c18d9b..30e56808 100644 --- a/PS/_Tests/WorkItems.AreasIterations.Get-TfsIteration.Tests.ps1 +++ b/PS/_Tests/WorkItem/AreasIterations/GetTfsIteration.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/WorkItems.Get-TfsWorkItem.Tests.ps1 b/PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 similarity index 98% rename from PS/_Tests/WorkItems.Get-TfsWorkItem.Tests.ps1 rename to PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 index 5317b714..d1ad6846 100644 --- a/PS/_Tests/WorkItems.Get-TfsWorkItem.Tests.ps1 +++ b/PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/WorkItems.Get-TfsWorkItemHistory.Tests.ps1 b/PS/_Tests/WorkItem/History/GetTfsWorkItemHistory.Tests.ps1 similarity index 78% rename from PS/_Tests/WorkItems.Get-TfsWorkItemHistory.Tests.ps1 rename to PS/_Tests/WorkItem/History/GetTfsWorkItemHistory.Tests.ps1 index 677d4441..7671cd6c 100644 --- a/PS/_Tests/WorkItems.Get-TfsWorkItemHistory.Tests.ps1 +++ b/PS/_Tests/WorkItem/History/GetTfsWorkItemHistory.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/WorkItems.Queries.Get-TfsWorkItemQuery.Tests.ps1 b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQuery.Tests.ps1 similarity index 98% rename from PS/_Tests/WorkItems.Queries.Get-TfsWorkItemQuery.Tests.ps1 rename to PS/_Tests/WorkItem/Query/GetTfsWorkItemQuery.Tests.ps1 index e59a6a4d..20a8254d 100644 --- a/PS/_Tests/WorkItems.Queries.Get-TfsWorkItemQuery.Tests.ps1 +++ b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQuery.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/WorkItems.Queries.Get-TfsWorkItemQueryFolder.Tests.ps1 b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryFolder.Tests.ps1 similarity index 97% rename from PS/_Tests/WorkItems.Queries.Get-TfsWorkItemQueryFolder.Tests.ps1 rename to PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryFolder.Tests.ps1 index 3125f51a..df3374e7 100644 --- a/PS/_Tests/WorkItems.Queries.Get-TfsWorkItemQueryFolder.Tests.ps1 +++ b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryFolder.Tests.ps1 @@ -1,4 +1,4 @@ -. $PSScriptRoot/_TestSetup.ps1 +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/WorkItems.AreasIterations.Get-TfsArea.Tests.ps1 b/PS/_Tests/WorkItems.AreasIterations.Get-TfsArea.Tests.ps1 deleted file mode 100644 index f249bfec..00000000 --- a/PS/_Tests/WorkItems.AreasIterations.Get-TfsArea.Tests.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -. $PSScriptRoot/_TestSetup.ps1 - -Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - - Context 'Integration Tests' { - - It 'Should throw on parameterless invocation' { - { Get-TfsArea } | Should -Throw - } - - It 'Should get areas (recursively)' { - Get-TfsArea '**' -Project $tfsProject | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('PUL', 'PUL-APP', 'PUL-DB') - } - - # It 'Should get areas non-recursively' { - # Write-Host "Project: $tfsProject" - # Get-TfsArea -Node '\*\' -Project $tfsProject | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('PUL', 'PUL-DB') - # } - } -} diff --git a/PS/_Tests/testResults.xml b/PS/_Tests/testResults.xml new file mode 100644 index 00000000..75ddd2b8 --- /dev/null +++ b/PS/_Tests/testResults.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + ArgumentException: No team project collection (organization) information available. Either supply a valid -Collection argument or use Connect-TfsTeamProjectCollection (or Connect-TfsOrganization) prior to invoking this cmdlet. + at <ScriptBlock>, P:\Repos\Gh\igoravl\TfsCmdlets\PS\_Tests\Admin\GetTfsVersion.Tests.ps1:7 + + + + + + + + + + + \ No newline at end of file From 4acc55d277343a72d1edb7f42857b128f35018e6 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 18:01:36 -0300 Subject: [PATCH 23/90] Throw an error when in "anonymous mode" --- .../TeamProjectCollection/ConnectTeamProjectCollection.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/ConnectTeamProjectCollection.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/ConnectTeamProjectCollection.cs index 294c5aa1..6d491aad 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/ConnectTeamProjectCollection.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/ConnectTeamProjectCollection.cs @@ -1,4 +1,5 @@ using System.Management.Automation; +using Microsoft.VisualStudio.Services.ClientNotification; using Microsoft.VisualStudio.Services.WebApi; using TfsCmdlets.Models; @@ -60,6 +61,12 @@ protected override IEnumerable Run() { var tpc = Data.GetCollection(new { Collection = Collection ?? Parameters.Get("Organization") }); tpc.Connect(); + + if(tpc.CurrentUserUniqueName.Equals("Anonymous")) { + var connectionType = Collection == null? "organization": "team project collection"; + throw new NotAuthorizedException($"You are not authorized to access {connectionType} [{tpc.Uri}]. Check your credentials and try again."); + } + var srv = tpc.ConfigurationServer; CurrentConnections.Set(srv, tpc); From b2730951bd8049b2ef6a6c19dadf24fca570f4a7 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 18:02:36 -0300 Subject: [PATCH 24/90] Update gitignore --- .gitignore | 1 + PS/_Tests/testResults.xml | 234 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 223 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index f6847fc8..fb580a70 100644 --- a/.gitignore +++ b/.gitignore @@ -219,3 +219,4 @@ CSharp/.idea/** CSharp/*/Generated/** Docs/AzDO-Security-Actions.csv +**/testResults.xml diff --git a/PS/_Tests/testResults.xml b/PS/_Tests/testResults.xml index 75ddd2b8..186990ff 100644 --- a/PS/_Tests/testResults.xml +++ b/PS/_Tests/testResults.xml @@ -1,21 +1,231 @@  - - + + - + - + - + - + - - - ArgumentException: No team project collection (organization) information available. Either supply a valid -Collection argument or use Connect-TfsTeamProjectCollection (or Connect-TfsOrganization) prior to invoking this cmdlet. - at <ScriptBlock>, P:\Repos\Gh\igoravl\TfsCmdlets\PS\_Tests\Admin\GetTfsVersion.Tests.ps1:7 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e481e81124b9f5282bb3ce03a5e0f5c4a29bce1d Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 18:03:18 -0300 Subject: [PATCH 25/90] Update integration tests selection logic --- psake.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/psake.ps1 b/psake.ps1 index d9a1e57e..3173e2fe 100644 --- a/psake.ps1 +++ b/psake.ps1 @@ -242,12 +242,12 @@ Task AllTests -PreCondition { -not $SkipTests } { try { Write-Output ' == PowerShell Core ==' - Exec { pwsh.exe -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -ExcludeTagFilter 'Desktop'" } + Exec { pwsh.exe -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -ExcludeTagFilter 'Desktop', 'Server'" } Move-Item 'testResults.xml' -Destination $OutDir/TestResults-Pester-Core.xml -Force Move-Item 'coverage.xml' -Destination $OutDir/Coverage-Pester-Core.xml -Force Write-Output ' == PowerShell Desktop ==' - Exec { powershell.exe -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -ExcludeTagFilter 'Core'" } + Exec { powershell.exe -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -ExcludeTagFilter 'Core', 'Server'" } Move-Item 'testResults.xml' -Destination $OutDir/TestResults-Pester-Desktop.xml -Force Move-Item 'coverage.xml' -Destination $OutDir/Coverage-Pester-Desktop.xml -Force } From 0515d42a0f94c075cbe97713b7222a3c3a992b67 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 18:03:28 -0300 Subject: [PATCH 26/90] Update test setup logic --- PS/_Tests/_TestSetup.ps1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/PS/_Tests/_TestSetup.ps1 b/PS/_Tests/_TestSetup.ps1 index 744ce1b3..89c1ccc9 100644 --- a/PS/_Tests/_TestSetup.ps1 +++ b/PS/_Tests/_TestSetup.ps1 @@ -1,4 +1,5 @@ BeforeAll { + $scriptRoot = $PSScriptRoot $solutionDir = Join-Path $scriptRoot '../..' -Resolve $outDir = Join-Path $solutionDir 'out' -Resolve @@ -8,6 +9,12 @@ BeforeAll { $tfsAccessToken = $env:TFSCMDLETS_ACCESS_TOKEN $tfsCollectionUrl = $env:TFSCMDLETS_COLLECTION_URL + + if((-not $tfsAccessToken) -or (-not $tfsCollectionUrl)) + { + throw 'Missing credentials. Please provide both TFSCMDLETS_ACCESS_TOKEN and TFSCMDLETS_COLLECTION_URL environment variables' + } + $global:tfsProject = 'TestProject' if (-not $hasBuild) { From 54b69f8bbd1f3f5044ca2d0447697794acddc5b5 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 18:04:00 -0300 Subject: [PATCH 27/90] Add new tests --- CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeed.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeed.cs b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeed.cs index eeacd8ed..a04a2e42 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeed.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifactFeed.cs @@ -70,8 +70,11 @@ string s when s.IsGuid() => Guid.Parse(s), yield return Client.GetFeedsAsync(Role, false, false) .GetResult($"Error getting artifact feed(s) '{s}'") .Where(f1 => f1.Name.IsLike(s) && ( - (string.IsNullOrEmpty(f1.Project?.Name) && ((Scope & ProjectOrCollectionScope.Collection) > 0)) || - (!string.IsNullOrEmpty(f1.Project?.Name) && ((Scope & ProjectOrCollectionScope.Project) > 0)))); + (string.IsNullOrEmpty(f1.Project?.Name) && + ((Scope & ProjectOrCollectionScope.Collection) > 0)) || + (!string.IsNullOrEmpty(f1.Project?.Name) && + ((Scope & ProjectOrCollectionScope.Project) > 0) && + (!Has_Project || f1.Project.Name.Equals(Project.Name, StringComparison.OrdinalIgnoreCase))))); break; } default: From 2aab3e0e176794f1b7e68a0f75740175ade7cee4 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 18:04:16 -0300 Subject: [PATCH 28/90] Update test tags --- PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 b/PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 index abe3349a..675020bc 100644 --- a/PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 +++ b/PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 @@ -3,7 +3,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Unit Tests' { - It 'Should get connection string from local server' -Tag 'Desktop' { + It 'Should get connection string from local server' -Tag 'Server' { } } } \ No newline at end of file From 14563e527645a263d64ee3d6f50eb39b48319773 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 18:04:34 -0300 Subject: [PATCH 29/90] Add new tests --- .../Artifact/GetTfsArtifactFeed.Tests.ps1 | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 PS/_Tests/Artifact/GetTfsArtifactFeed.Tests.ps1 diff --git a/PS/_Tests/Artifact/GetTfsArtifactFeed.Tests.ps1 b/PS/_Tests/Artifact/GetTfsArtifactFeed.Tests.ps1 new file mode 100644 index 00000000..01569174 --- /dev/null +++ b/PS/_Tests/Artifact/GetTfsArtifactFeed.Tests.ps1 @@ -0,0 +1,34 @@ +& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + + It 'Should Return only Org-level feeds' { + $feeds = Get-TfsArtifactFeed -Scope Collection + $feeds.Count | Should -BeGreaterOrEqual 1 + $feeds.Project | Should -BeNullOrEmpty + } + + It 'Should return feeds from all scopes' { + (Get-TfsArtifactFeed).Name | Sort-Object -Unique | Should -Be @('Default', 'TestProjectFeed', 'TfsCmdletsAAD', 'TfsCmdletsFeed') + } + + It 'Should filter feeds by name' { + (Get-TfsArtifactFeed -Feed 'Default').Name | Should -Be 'Default' + (Get-TfsArtifactFeed 'T*').Name | Sort-Object -Unique | Should -Be @('TestProjectFeed', 'TfsCmdletsAAD', 'TfsCmdletsFeed') + } + + It 'Should Return All Project-Level feeds' { + $feeds = Get-TfsArtifactFeed -Scope Project + $feeds.Count | Should -Be 3 + $feeds.Project.Name | Sort-Object -Unique | Should -Be @('AgileGit', 'TestProject') + } + + It 'Should Filter feeds by project' { + (Get-TfsArtifactFeed -Scope Project -Project 'AgileGit').Project.Name | Select-Object -Unique | Should -Be 'AgileGit' + (Get-TfsArtifactFeed -Scope Project -Project 'A*').Project.Name | Select-Object -Unique | Should -Be 'AgileGit' + (Get-TfsArtifactFeed -Scope Project -Project 'TestProject').Project.Name | Select-Object -Unique | Should -Be 'TestProject' + } + } +} \ No newline at end of file From e12e9b31f12c0750af18a0b34969349964c3778e Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 18:05:43 -0300 Subject: [PATCH 30/90] Remove obsolete file --- PS/_Tests/testResults.xml | 237 -------------------------------------- 1 file changed, 237 deletions(-) delete mode 100644 PS/_Tests/testResults.xml diff --git a/PS/_Tests/testResults.xml b/PS/_Tests/testResults.xml deleted file mode 100644 index 186990ff..00000000 --- a/PS/_Tests/testResults.xml +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 2da3716700ef4fbb655b1af8f8f04bedaa4ed0f4 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 21:32:47 -0300 Subject: [PATCH 31/90] Add fix for deleted and delisted package handling --- CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifact.cs | 4 ++-- RELEASENOTES.md | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifact.cs b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifact.cs index f13a5eaf..34ae2311 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifact.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Artifact/GetArtifact.cs @@ -106,9 +106,9 @@ string s when s.IsGuid() => Guid.Parse(s), protocolType: ProtocolType, getTopPackageVersions: getTopPackageVersions, includeAllVersions: getTopPackageVersions || IncludeAllVersions, - includeDeleted: IncludeDeleted? null: true, + includeDeleted: IncludeDeleted? true: null, includeDescription: !getTopPackageVersions, - isListed: IncludeDelisted? null: true, + isListed: IncludeDelisted || IncludeDeleted? null: true, isRelease: IncludePrerelease? null: true) .GetResult($"Error getting artifact feed(s) '{s}'") .Where(p => p.Name.IsLike(s)); diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 35f8d48f..ad8f46a4 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -4,12 +4,18 @@ This release adds some new process-related cmdlets. -### New cmdlets +## New cmdlets - `Get-TfsWorkItemField`: Gets information from one or more organization-wide work item fields - `New-TfsWorkItemField`: Creates a new organization-wide work item field - `Remove-TfsWorkItemField`: Removes an organization-wide work item field +## Fixes and enhancements + +- Fixed an issue with `Get-TfsArtifact` where it wasn't listing deleted packages. +- Fixed an issue with `Get-TfsArtifactFeed` where it would ignore the -Project argument and thus not filter feeds by project. +- Now `Connect-TfsTeamProjectCollection` (and its counterpart `Connect-TfsOrganization`) throws an error when trying to connect with invalid credentials instead of silently going into "anonymous mode". That help preventing subtle script errors where the lack of authorization would only be noticed later in the script, when trying to actually perform some command that required valid credentials. Now you get the warning that something is wrong as early in the script as possible. + ----------------------- ## Previous Versions From eadac23cbaed8dd631a73f7b3062bad43880a401 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 23:18:04 -0300 Subject: [PATCH 32/90] Fix issue with WorkItem argument handling --- .../WorkItem/WorkItemType/GetWorkItemType.cs | 75 ++++++++++--------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/GetWorkItemType.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/GetWorkItemType.cs index 33def061..c0813ee2 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/GetWorkItemType.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/GetWorkItemType.cs @@ -25,46 +25,53 @@ partial class GetWorkItemType public object WorkItem { get; set; } } - [CmdletController(typeof(WebApiWorkItemType), Client=typeof(IWorkItemTrackingHttpClient))] + [CmdletController(typeof(WebApiWorkItemType), Client = typeof(IWorkItemTrackingHttpClient))] partial class GetWorkItemTypeController { protected override IEnumerable Run() { - var type = Parameters.Get(nameof(GetWorkItemType.Type)); - - var shouldSkipWi = !Parameters.HasParameter(nameof(GetWorkItemType.WorkItem)) || - (Parameters.Get(nameof(GetWorkItemType.WorkItem)) is int i && i == 0); - - if (Parameters.HasParameter(nameof(GetWorkItemType.WorkItem)) && !shouldSkipWi) + foreach (var input in Type) { - var workItem = Data.GetItem(nameof(GetWorkItemType.WorkItem)); - type = workItem.Fields["System.WorkItemType"]; + switch (input) + { + case object _ when Has_WorkItem: + { + var wi = Data.GetItem(new { WorkItem }); + if (wi == null) + { + Logger.LogError(new ArgumentException($"Work item '{WorkItem}' not found")); + break; + } + var type = wi.Fields["System.WorkItemType"].ToString(); + yield return Client.GetWorkItemTypeAsync(type: type, project: Project.Id) + .GetResult($"Error getting type '{type}' from work item '{WorkItem}'"); + break; + } + case WebApiWorkItemType t: + { + yield return t; + break; + } + case string s when s.IsWildcard(): + { + yield return Client.GetWorkItemTypesAsync(Project.Id) + .GetResult($"Error getting type(s) '{s}'") + .Where(t1 => t1.Name.IsLike(s)); + break; + } + case string s: + { + yield return Client.GetWorkItemTypeAsync(type: s, project: Project.Id) + .GetResult($"Error getting type '{s}'"); + break; + } + default: + { + Logger.LogError(new ArgumentException($"Invalid or non-existent work item type '{Type}'")); + break; + } + } } - - var tp = Data.GetProject(); - - switch (type) - { - case WebApiWorkItemType t: - { - return new[] { t }; - } - case string t: - { - return Client.GetWorkItemTypesAsync(tp.Id) - .GetResult($"Error getting type(s) '{t}'") - .Where(t1 => t1.Name.IsLike(t)); - } - case IEnumerable types: - { - return Client.GetWorkItemTypesAsync(tp.Id) - .GetResult($"Error getting type(s) '{string.Join(", ", types)}'") - .Where(t1 => types.Any(t2 => t1.Name.IsLike(t2))); - } - } - - Logger.LogError(new ArgumentException($"Invalid or non-existent work item type '{type}'")); - return null; } } } \ No newline at end of file From cbb0ccb43386de967cca3d7ce66d0e70b0f6ec3c Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 23:18:12 -0300 Subject: [PATCH 33/90] Update release notes --- RELEASENOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index ad8f46a4..bcb40ea7 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -14,6 +14,7 @@ This release adds some new process-related cmdlets. - Fixed an issue with `Get-TfsArtifact` where it wasn't listing deleted packages. - Fixed an issue with `Get-TfsArtifactFeed` where it would ignore the -Project argument and thus not filter feeds by project. +- Fixed an issue with `Get-TfsWorkItemType`where it would throw a "Parameter count mismatch" error when trying to get the work item type of a given work item. - Now `Connect-TfsTeamProjectCollection` (and its counterpart `Connect-TfsOrganization`) throws an error when trying to connect with invalid credentials instead of silently going into "anonymous mode". That help preventing subtle script errors where the lack of authorization would only be noticed later in the script, when trying to actually perform some command that required valid credentials. Now you get the warning that something is wrong as early in the script as possible. ----------------------- From cb52ea93aeb081cba480692ae41aacec72b1c715 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 23:18:27 -0300 Subject: [PATCH 34/90] Update test execution logic --- psake.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/psake.ps1 b/psake.ps1 index 3173e2fe..86881816 100644 --- a/psake.ps1 +++ b/psake.ps1 @@ -242,12 +242,12 @@ Task AllTests -PreCondition { -not $SkipTests } { try { Write-Output ' == PowerShell Core ==' - Exec { pwsh.exe -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -ExcludeTagFilter 'Desktop', 'Server'" } + Exec { pwsh.exe -NonInteractive -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -ExcludeTagFilter 'Desktop', 'Server'" } Move-Item 'testResults.xml' -Destination $OutDir/TestResults-Pester-Core.xml -Force Move-Item 'coverage.xml' -Destination $OutDir/Coverage-Pester-Core.xml -Force Write-Output ' == PowerShell Desktop ==' - Exec { powershell.exe -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -ExcludeTagFilter 'Core', 'Server'" } + Exec { powershell.exe -NonInteractive -NoLogo -Command "Invoke-Pester -CI -Output $outputLevel -ExcludeTagFilter 'Core', 'Server'" } Move-Item 'testResults.xml' -Destination $OutDir/TestResults-Pester-Desktop.xml -Force Move-Item 'coverage.xml' -Destination $OutDir/Coverage-Pester-Desktop.xml -Force } From f4a64d928de6b2c78e9d29925a528c1f697b96ac Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 23:18:42 -0300 Subject: [PATCH 35/90] Add new tests --- .../WorkItemType/GetTfsWorkItemType.Tests.ps1 | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 PS/_Tests/WorkItem/WorkItemType/GetTfsWorkItemType.Tests.ps1 diff --git a/PS/_Tests/WorkItem/WorkItemType/GetTfsWorkItemType.Tests.ps1 b/PS/_Tests/WorkItem/WorkItemType/GetTfsWorkItemType.Tests.ps1 new file mode 100644 index 00000000..91bff76c --- /dev/null +++ b/PS/_Tests/WorkItem/WorkItemType/GetTfsWorkItemType.Tests.ps1 @@ -0,0 +1,54 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Common' { + It 'Should require mandatory parameters' { + { Get-TfsWorkItemType } | Should -Throw + { Get-TfsWorkItemType -Type Task } | Should -Throw + { Get-TfsWorkItemType -WorkItem '123' } | Should -Throw + } + } + + Context 'Get by type' { + # Get-TfsWorkItemType + # [[-Type] ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return a work item type' { + $workItemType = Get-TfsWorkItemType -Type 'Task' -Project $tfsProject + $workItemType | Should -Not -BeNullOrEmpty + } + + It 'Should return multiple types from a list' { + $workItemType = Get-TfsWorkItemType 'Task', 'Epic', 'Feature' -Project $tfsProject + $workItemType.Name | Sort-Object | Should -Be @('Epic', 'Feature', 'Task') + } + + It 'Should return all work item types' { + $workItemTypes = Get-TfsWorkItemType -Project $tfsProject + $workItemTypes.Name | Sort-Object | Should -Be @('Bug', 'Code Review Request', 'Code Review Response', 'Epic', 'Feature', 'Feedback Request', 'Feedback Response', 'Impediment', 'Product Backlog Item', 'Shared Parameter', 'Shared Steps', 'Task', 'Test Case', 'Test Plan', 'Test Suite') + } + + It 'Should support wildcards' { + $workItemTypes = Get-TfsWorkItemType -Type 'T*' -Project $tfsProject + $workItemTypes.Name | Sort-Object | Should -Be @('Task', 'Test Case', 'Test Plan', 'Test Suite') + } + + context 'Get by work item' { + # Get-TfsWorkItemType + # -WorkItem + # [-Project ] + # [-Collection ] + # [-Server ] []' + + It 'Should return a type given a WI' { + $wi = (Get-TfsWorkItem -Type 'Task' -Project $tfsProject)[0] + $workItemType = Get-TfsWorkItemType -WorkItem $wi -Project $tfsProject + $workItemType.Name | Should -Be 'Task' + } + } + } +} \ No newline at end of file From 9f8bf6d24bc5c3956a33cced8e226cc6383a521a Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 23:18:58 -0300 Subject: [PATCH 36/90] Add new IsPreRelease script property --- .../Microsoft.VisualStudio.Services.Feed.WebApi.Package.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PS/_Types/Microsoft.VisualStudio.Services.Feed.WebApi.Package.yml b/PS/_Types/Microsoft.VisualStudio.Services.Feed.WebApi.Package.yml index affdc7a5..e6d9cfe6 100644 --- a/PS/_Types/Microsoft.VisualStudio.Services.Feed.WebApi.Package.yml +++ b/PS/_Types/Microsoft.VisualStudio.Services.Feed.WebApi.Package.yml @@ -24,3 +24,6 @@ - ScriptProperty: Name: PublishDate GetScriptBlock: $this.Versions[0].PublishDate +- ScriptProperty: + Name: IsPreRelease + GetScriptBlock: $this.Versions[0].NormalizedVersion -like '*-*' \ No newline at end of file From 64566d82942bcf3c36453b5e40a6406b2457c057 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 23:39:03 -0300 Subject: [PATCH 37/90] Add new tests --- .../Tagging/GetTfsWorkItemTag.Tests.ps1 | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 PS/_Tests/WorkItem/Tagging/GetTfsWorkItemTag.Tests.ps1 diff --git a/PS/_Tests/WorkItem/Tagging/GetTfsWorkItemTag.Tests.ps1 b/PS/_Tests/WorkItem/Tagging/GetTfsWorkItemTag.Tests.ps1 new file mode 100644 index 00000000..2f7e80b0 --- /dev/null +++ b/PS/_Tests/WorkItem/Tagging/GetTfsWorkItemTag.Tests.ps1 @@ -0,0 +1,43 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context '__AllParameterSets' { + # Get-TfsWorkItemTag + # [[-Tag] ] + # [-IncludeInactive] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all tags' { + $tags = Get-TfsWorkItemTag -Project $tfsProject + $tags.Name | Sort-Object | Should -Be @('MyTag1', 'MyTag2') + } + + It 'Should return all tags including inactive' { + $tags = Get-TfsWorkItemTag -IncludeInactive -Project $tfsProject + $tags.Name | Sort-Object | Should -Be @('MyTag1', 'MyTag2', 'MyTag3') + } + + It 'Should return a tag' { + $tag = Get-TfsWorkItemTag -Tag 'MyTag1' -Project $tfsProject + $tag.Name | Should -Be 'MyTag1' + } + + It 'Should return a tag including inactive' { + $tag = Get-TfsWorkItemTag -Tag 'MyTag3' -IncludeInactive -Project $tfsProject + $tag.Name | Should -Be 'MyTag3' + } + + It 'Should return multiple tags from a list' { + $tags = Get-TfsWorkItemTag 'MyTag1', 'MyTag2' -Project $tfsProject + $tags.Name | Sort-Object | Should -Be @('MyTag1', 'MyTag2') + } + + It 'Should return multiple tags from a list including inactive' { + $tags = Get-TfsWorkItemTag 'MyTag1', 'MyTag3' -Project $tfsProject -IncludeInactive + $tags.Name | Sort-Object | Should -Be @('MyTag1', 'MyTag3') + } + } +} \ No newline at end of file From d7a7014dd75008a5f362e2eed0f08de5b3736ef3 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 23:41:41 -0300 Subject: [PATCH 38/90] Fix an issue in Get-TfsWorkItemTag --- .../WorkItem/Tagging/GetWorkItemTag.cs | 55 ++++++++++--------- RELEASENOTES.md | 3 +- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/GetWorkItemTag.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/GetWorkItemTag.cs index 74d22233..8e8cd16f 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/GetWorkItemTag.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Tagging/GetWorkItemTag.cs @@ -26,39 +26,40 @@ partial class GetWorkItemTag public SwitchParameter IncludeInactive { get; set; } } - [CmdletController(typeof(WebApiTagDefinition), Client=typeof(ITaggingHttpClient))] + [CmdletController(typeof(WebApiTagDefinition), Client = typeof(ITaggingHttpClient))] partial class GetWorkItemTagController { protected override IEnumerable Run() { - var tag = Parameters.Get(nameof(GetWorkItemTag.Tag)); - var includeInactive = Parameters.Get(nameof(GetWorkItemTag.IncludeInactive)); - - var tp = Data.GetProject(); - - switch (tag) + foreach (var input in Tag) { - case WebApiTagDefinition t: - { - return new[] { t }; - } - case string s: - { - return Client.GetTagsAsync(tp.Id, includeInactive) - .GetResult($"Error getting work item tag(s) '{s}'") - .Where(t => t.Name.IsLike(s)); - } - case IEnumerable tags: - { - return Client.GetTagsAsync(tp.Id, includeInactive) - .GetResult($"Error getting work item tag(s) '{string.Join(", ", tags)}'") - .Where(t => tags.Any(tag => t.Name.IsLike(tag))); - } + switch (input) + { + case WebApiTagDefinition t: + { + yield return t; + break; + } + case string s when s.IsWildcard(): + { + yield return Client.GetTagsAsync(Project.Id, IncludeInactive) + .GetResult($"Error getting work item tag(s) '{s}'") + .Where(t => t.Name.IsLike(s)); + break; + } + case string s: + { + yield return Client.GetTagAsync(scopeId: Project.Id, name: s) + .GetResult($"Error getting work item tag(s) '{s}'"); + break; + } + default: + { + Logger.LogError(new ArgumentException($"Invalid or non-existent tag '{input}'")); + break; + } + } } - - Logger.LogError(new ArgumentException($"Invalid or non-existent tag '{tag}'")); - - return null; } } } \ No newline at end of file diff --git a/RELEASENOTES.md b/RELEASENOTES.md index bcb40ea7..9a57595c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -14,7 +14,8 @@ This release adds some new process-related cmdlets. - Fixed an issue with `Get-TfsArtifact` where it wasn't listing deleted packages. - Fixed an issue with `Get-TfsArtifactFeed` where it would ignore the -Project argument and thus not filter feeds by project. -- Fixed an issue with `Get-TfsWorkItemType`where it would throw a "Parameter count mismatch" error when trying to get the work item type of a given work item. +- Fixed an issue with `Get-TfsWorkItemTag` where it would fail when given a list of tags as input. +- Fixed an issue with `Get-TfsWorkItemType` where it would throw a "Parameter count mismatch" error when trying to get the work item type of a given work item. - Now `Connect-TfsTeamProjectCollection` (and its counterpart `Connect-TfsOrganization`) throws an error when trying to connect with invalid credentials instead of silently going into "anonymous mode". That help preventing subtle script errors where the lack of authorization would only be noticed later in the script, when trying to actually perform some command that required valid credentials. Now you get the warning that something is wrong as early in the script as possible. ----------------------- From 77d0b2da21fc3f758683b73bee8f1d449cdbb1d7 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 23:41:57 -0300 Subject: [PATCH 39/90] Update tests --- PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 b/PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 index d1ad6846..c425ef97 100644 --- a/PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 +++ b/PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { @@ -6,17 +6,17 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { It 'Should get by ID and revision' { (Get-TfsWorkItem -ID 150).Id | Should -Be 150 - (Get-TfsWorkItem -ID 150).Rev | Should -Be 4 + (Get-TfsWorkItem -ID 150).Rev | Should -Be 6 (Get-TfsWorkItem -ID 150 -Revision 2).Rev | Should -Be 2 } It 'Should get by Where' { (Get-TfsWorkItem -Where '[System.Id] = 150').Id | Should -Be 150 - (Get-TfsWorkItem -Where '[System.Id] = 150').Rev | Should -Be 4 + (Get-TfsWorkItem -Where '[System.Id] = 150').Rev | Should -Be 6 } It 'Should support ASOF when getting by ID' { - (Get-TfsWorkItem 150 -AsOf (Get-Date)).Rev | Should -Be 4 + (Get-TfsWorkItem 150 -AsOf (Get-Date)).Rev | Should -Be 6 (Get-TfsWorkItem 150 -AsOf (Get-Date '2022-01-21')).Rev | Should -Be 3 } From f8d37918a80441894ed777d17c22888444a702b0 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 5 Aug 2024 23:43:38 -0300 Subject: [PATCH 40/90] Update tests --- ...TfsConfigurationConnectionString.Tests.ps1 | 14 ++--- ...figurationServerConnectionString.Tests.ps1 | 8 +++ .../Admin/GetTfsInstallationPath.Tests.ps1 | 3 +- PS/_Tests/Admin/GetTfsVersion.Tests.ps1 | 2 +- .../Registry/GetTfsRegistryValue.Tests.ps1 | 8 +++ PS/_Tests/Artifact/GetTfsArtifact.Tests.ps1 | 57 +++++++++++++++++++ .../Artifact/GetTfsArtifactFeed.Tests.ps1 | 6 +- .../Artifact/GetTfsArtifactFeedView.Tests.ps1 | 8 +++ .../Artifact/GetTfsArtifactVersion.Tests.ps1 | 8 +++ .../GetTfsExtension.Tests.ps1 | 2 +- .../Git/Branch/GetTfsGitBranch.Tests.ps1 | 8 +++ .../Git/Commit/GetTfsGitCommit.Tests.ps1 | 8 +++ PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 | 8 +++ PS/_Tests/Git/Item/GetTfsGitItem.Tests.ps1 | 8 +++ .../Policy/GetTfsGitBranchPolicy.Tests.ps1 | 8 +++ .../Git/Policy/GetTfsGitPolicyType.Tests.ps1 | 8 +++ PS/_Tests/Identity/GetTfsIdentity.Tests.ps1 | 8 +++ .../Identity/Group/GetTfsGroup.Tests.ps1 | 8 +++ .../Group/GetTfsGroupMember.Tests.ps1 | 8 +++ PS/_Tests/Identity/User/GetTfsUser.Tests.ps1 | 8 +++ .../Organization/GetTfsOrganization.Tests.ps1 | 8 +++ .../GetTfsBuildDefinition.Tests.ps1 | 8 +++ .../GetTfsBuildDefinitionFolder.Tests.ps1 | 8 +++ .../Build/GetTfsBuildDefinition.Tests.ps1 | 2 +- .../GetTfsBuildDefinitionFolder.Tests.ps1 | 2 +- .../GetTfsReleaseDefinition.Tests.ps1 | 8 +++ .../GetTfsReleaseDefinitionFolder.Tests.ps1 | 8 +++ .../GetTfsProcessTemplate.Tests.ps1 | 8 +++ PS/_Tests/RestApi/GetTfsRestClient.Tests.ps1 | 8 +++ PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 | 8 +-- .../GetTfsServiceHookConsumer.Tests.ps1 | 8 +++ ...fsServiceHookNotificationHistory.Tests.ps1 | 8 +++ .../GetTfsServiceHookPublisher.Tests.ps1 | 8 +++ .../GetTfsServiceHookSubscription.Tests.ps1 | 8 +++ .../Backlog/GetTfsTeamBacklogLevel.Tests.ps1 | 8 +++ .../Team/Board/GetTfsTeamBoard.Tests.ps1 | 8 +++ .../Board/GetTfsTeamBoardCardRule.Tests.ps1 | 8 +++ PS/_Tests/Team/GetTfsTeam.Tests.ps1 | 6 +- .../Team/TeamAdmin/GetTfsTeamAdmin.Tests.ps1 | 8 +++ .../TeamMember/GetTfsTeamMember.Tests.ps1 | 8 +++ .../TeamProject/GetTfsTeamProject.Tests.ps1 | 4 +- .../Member/GetTfsTeamProjectMember.Tests.ps1 | 8 +++ .../GetTfsTeamProjectCollection.Tests.ps1 | 8 +++ .../TestManagement/GetTfsTestPlan.Tests.ps1 | 8 +++ PS/_Tests/Wiki/GetTfsWiki.Tests.ps1 | 6 +- .../AreasIterations/GetTfsArea.Tests.ps1 | 8 +++ .../AreasIterations/GetTfsIteration.Tests.ps1 | 2 +- .../Field/GetTfsWorkItemField.Tests.ps1 | 8 +++ .../History/GetTfsWorkItemHistory.Tests.ps1 | 4 +- .../Linking/GetTfsWorkItemLink.Tests.ps1 | 8 +++ .../GetTfsWorkItemLinkEndType.Tests.ps1 | 8 +++ .../Query/GetTfsWorkItemQuery.Tests.ps1 | 2 +- .../Query/GetTfsWorkItemQueryFolder.Tests.ps1 | 2 +- ...etTfsWorkItemQueryItemController.Tests.ps1 | 8 +++ 54 files changed, 394 insertions(+), 32 deletions(-) create mode 100644 PS/_Tests/Admin/GetTfsConfigurationServerConnectionString.Tests.ps1 create mode 100644 PS/_Tests/Admin/Registry/GetTfsRegistryValue.Tests.ps1 create mode 100644 PS/_Tests/Artifact/GetTfsArtifact.Tests.ps1 create mode 100644 PS/_Tests/Artifact/GetTfsArtifactFeedView.Tests.ps1 create mode 100644 PS/_Tests/Artifact/GetTfsArtifactVersion.Tests.ps1 create mode 100644 PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 create mode 100644 PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 create mode 100644 PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 create mode 100644 PS/_Tests/Git/Item/GetTfsGitItem.Tests.ps1 create mode 100644 PS/_Tests/Git/Policy/GetTfsGitBranchPolicy.Tests.ps1 create mode 100644 PS/_Tests/Git/Policy/GetTfsGitPolicyType.Tests.ps1 create mode 100644 PS/_Tests/Identity/GetTfsIdentity.Tests.ps1 create mode 100644 PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 create mode 100644 PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 create mode 100644 PS/_Tests/Identity/User/GetTfsUser.Tests.ps1 create mode 100644 PS/_Tests/Organization/GetTfsOrganization.Tests.ps1 create mode 100644 PS/_Tests/Pipeline/Build/Definition/GetTfsBuildDefinition.Tests.ps1 create mode 100644 PS/_Tests/Pipeline/Build/Folder/GetTfsBuildDefinitionFolder.Tests.ps1 create mode 100644 PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinition.Tests.ps1 create mode 100644 PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinitionFolder.Tests.ps1 create mode 100644 PS/_Tests/ProcessTemplate/GetTfsProcessTemplate.Tests.ps1 create mode 100644 PS/_Tests/RestApi/GetTfsRestClient.Tests.ps1 create mode 100644 PS/_Tests/ServiceHook/GetTfsServiceHookConsumer.Tests.ps1 create mode 100644 PS/_Tests/ServiceHook/GetTfsServiceHookNotificationHistory.Tests.ps1 create mode 100644 PS/_Tests/ServiceHook/GetTfsServiceHookPublisher.Tests.ps1 create mode 100644 PS/_Tests/ServiceHook/GetTfsServiceHookSubscription.Tests.ps1 create mode 100644 PS/_Tests/Team/Backlog/GetTfsTeamBacklogLevel.Tests.ps1 create mode 100644 PS/_Tests/Team/Board/GetTfsTeamBoard.Tests.ps1 create mode 100644 PS/_Tests/Team/Board/GetTfsTeamBoardCardRule.Tests.ps1 create mode 100644 PS/_Tests/Team/TeamAdmin/GetTfsTeamAdmin.Tests.ps1 create mode 100644 PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 create mode 100644 PS/_Tests/TeamProject/Member/GetTfsTeamProjectMember.Tests.ps1 create mode 100644 PS/_Tests/TeamProjectCollection/GetTfsTeamProjectCollection.Tests.ps1 create mode 100644 PS/_Tests/TestManagement/GetTfsTestPlan.Tests.ps1 create mode 100644 PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 create mode 100644 PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 create mode 100644 PS/_Tests/WorkItem/Linking/GetTfsWorkItemLink.Tests.ps1 create mode 100644 PS/_Tests/WorkItem/Linking/GetTfsWorkItemLinkEndType.Tests.ps1 create mode 100644 PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryItemController.Tests.ps1 diff --git a/PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 b/PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 index 675020bc..38844fcb 100644 --- a/PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 +++ b/PS/_Tests/Admin/GetTfsConfigurationConnectionString.Tests.ps1 @@ -1,9 +1,9 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +# & "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" -Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { +# Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Unit Tests' { - It 'Should get connection string from local server' -Tag 'Server' { - } - } -} \ No newline at end of file +# Context 'Unit Tests' { +# It 'Should get connection string from local server' -Tag 'Server' { +# } +# } +# } \ No newline at end of file diff --git a/PS/_Tests/Admin/GetTfsConfigurationServerConnectionString.Tests.ps1 b/PS/_Tests/Admin/GetTfsConfigurationServerConnectionString.Tests.ps1 new file mode 100644 index 00000000..0037dfa3 --- /dev/null +++ b/PS/_Tests/Admin/GetTfsConfigurationServerConnectionString.Tests.ps1 @@ -0,0 +1,8 @@ +# & "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +# Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + +# Context 'Integration Tests' { +# Write-Warning "Not implemented" +# } +# } \ No newline at end of file diff --git a/PS/_Tests/Admin/GetTfsInstallationPath.Tests.ps1 b/PS/_Tests/Admin/GetTfsInstallationPath.Tests.ps1 index 28375bfc..689cc174 100644 --- a/PS/_Tests/Admin/GetTfsInstallationPath.Tests.ps1 +++ b/PS/_Tests/Admin/GetTfsInstallationPath.Tests.ps1 @@ -1,4 +1,5 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +echo $PSScriptRoot +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/Admin/GetTfsVersion.Tests.ps1 b/PS/_Tests/Admin/GetTfsVersion.Tests.ps1 index c5cb9f7d..bd2767a4 100644 --- a/PS/_Tests/Admin/GetTfsVersion.Tests.ps1 +++ b/PS/_Tests/Admin/GetTfsVersion.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/Admin/Registry/GetTfsRegistryValue.Tests.ps1 b/PS/_Tests/Admin/Registry/GetTfsRegistryValue.Tests.ps1 new file mode 100644 index 00000000..0037dfa3 --- /dev/null +++ b/PS/_Tests/Admin/Registry/GetTfsRegistryValue.Tests.ps1 @@ -0,0 +1,8 @@ +# & "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +# Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + +# Context 'Integration Tests' { +# Write-Warning "Not implemented" +# } +# } \ No newline at end of file diff --git a/PS/_Tests/Artifact/GetTfsArtifact.Tests.ps1 b/PS/_Tests/Artifact/GetTfsArtifact.Tests.ps1 new file mode 100644 index 00000000..63597b10 --- /dev/null +++ b/PS/_Tests/Artifact/GetTfsArtifact.Tests.ps1 @@ -0,0 +1,57 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context '__AllParameterSets' { + + # Get-TfsArtifact + # [[-Artifact] ] + # -Feed + # [-IncludeDeleted] + # [-IncludeDescription] + # [-IncludePrerelease] + # [-IncludeDelisted] + # [-ProtocolType ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should throw when mandatory parameters are missing' { + { Get-TfsArtifact -Artifact 'foo' } | Should -Throw + } + + It 'Should return all artifacts' { + (Get-TfsArtifact -Feed Default).Name | Sort-Object -Unique | Should -Be @('portable-installer', 'TfsCmdlets') + } + + It 'Should return from a list of artifacts' { + (Get-TfsArtifact 'TfsCmdlets', 'portable-installer' -Feed Default).Name | Sort-Object -Unique | Should -Be @('portable-installer', 'TfsCmdlets') + } + + It 'Should support wildcards' { + (Get-TfsArtifact 'Tfs*' -Feed Default).Name | Should -Be 'TfsCmdlets' + (Get-TfsArtifact 'Tfs*', 'p*' -Feed Default).Name | Sort-Object -Unique | Should -Be @('portable-installer', 'TfsCmdlets') + } + + It 'Should return delisted packages' { + (Get-TfsArtifact -Feed Default -IncludeDelisted ` + | Where-Object IsListed -eq $false ` + | Select-Object -ExpandProperty Name) ` + | Should -Be 'Microsoft.TeamFoundationServer.Client' + } + + It 'Should return deleted packages' { + (Get-TfsArtifact -Feed Default -IncludeDeleted ` + | Where-Object IsDeleted -eq $true ` + | Select-Object -ExpandProperty Name) ` + | Should -Be @('old-portable-installer', 'tfscmdlets-portable') + } + + It 'Should return prerelease packages' { + (Get-TfsArtifact -Feed Default -IncludePrerelease ` + | Where-Object IsPreRelease -eq $true ` + | Select-Object -ExpandProperty Name) ` + | Should -Be TfsCmdlets + } + } +} \ No newline at end of file diff --git a/PS/_Tests/Artifact/GetTfsArtifactFeed.Tests.ps1 b/PS/_Tests/Artifact/GetTfsArtifactFeed.Tests.ps1 index 01569174..e8ea9b52 100644 --- a/PS/_Tests/Artifact/GetTfsArtifactFeed.Tests.ps1 +++ b/PS/_Tests/Artifact/GetTfsArtifactFeed.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { @@ -22,13 +22,13 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { It 'Should Return All Project-Level feeds' { $feeds = Get-TfsArtifactFeed -Scope Project $feeds.Count | Should -Be 3 - $feeds.Project.Name | Sort-Object -Unique | Should -Be @('AgileGit', 'TestProject') + $feeds.Project.Name | Sort-Object -Unique | Should -Be @('AgileGit', $tfsProject) } It 'Should Filter feeds by project' { (Get-TfsArtifactFeed -Scope Project -Project 'AgileGit').Project.Name | Select-Object -Unique | Should -Be 'AgileGit' (Get-TfsArtifactFeed -Scope Project -Project 'A*').Project.Name | Select-Object -Unique | Should -Be 'AgileGit' - (Get-TfsArtifactFeed -Scope Project -Project 'TestProject').Project.Name | Select-Object -Unique | Should -Be 'TestProject' + (Get-TfsArtifactFeed -Scope Project -Project $tfsProject).Project.Name | Select-Object -Unique | Should -Be $tfsProject } } } \ No newline at end of file diff --git a/PS/_Tests/Artifact/GetTfsArtifactFeedView.Tests.ps1 b/PS/_Tests/Artifact/GetTfsArtifactFeedView.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Artifact/GetTfsArtifactFeedView.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Artifact/GetTfsArtifactVersion.Tests.ps1 b/PS/_Tests/Artifact/GetTfsArtifactVersion.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Artifact/GetTfsArtifactVersion.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/ExtensionManagement/GetTfsExtension.Tests.ps1 b/PS/_Tests/ExtensionManagement/GetTfsExtension.Tests.ps1 index d1124f60..11841db2 100644 --- a/PS/_Tests/ExtensionManagement/GetTfsExtension.Tests.ps1 +++ b/PS/_Tests/ExtensionManagement/GetTfsExtension.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 b/PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 b/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 b/PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Git/Item/GetTfsGitItem.Tests.ps1 b/PS/_Tests/Git/Item/GetTfsGitItem.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Git/Item/GetTfsGitItem.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Git/Policy/GetTfsGitBranchPolicy.Tests.ps1 b/PS/_Tests/Git/Policy/GetTfsGitBranchPolicy.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Git/Policy/GetTfsGitBranchPolicy.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Git/Policy/GetTfsGitPolicyType.Tests.ps1 b/PS/_Tests/Git/Policy/GetTfsGitPolicyType.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Git/Policy/GetTfsGitPolicyType.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Identity/GetTfsIdentity.Tests.ps1 b/PS/_Tests/Identity/GetTfsIdentity.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Identity/GetTfsIdentity.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 b/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 b/PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Identity/User/GetTfsUser.Tests.ps1 b/PS/_Tests/Identity/User/GetTfsUser.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Identity/User/GetTfsUser.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Organization/GetTfsOrganization.Tests.ps1 b/PS/_Tests/Organization/GetTfsOrganization.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Organization/GetTfsOrganization.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Pipeline/Build/Definition/GetTfsBuildDefinition.Tests.ps1 b/PS/_Tests/Pipeline/Build/Definition/GetTfsBuildDefinition.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Pipeline/Build/Definition/GetTfsBuildDefinition.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Pipeline/Build/Folder/GetTfsBuildDefinitionFolder.Tests.ps1 b/PS/_Tests/Pipeline/Build/Folder/GetTfsBuildDefinitionFolder.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Pipeline/Build/Folder/GetTfsBuildDefinitionFolder.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 b/PS/_Tests/Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 index 329e4f90..1e79e54e 100644 --- a/PS/_Tests/Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 +++ b/PS/_Tests/Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 b/PS/_Tests/Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 index f466fb83..49e9b1fc 100644 --- a/PS/_Tests/Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 +++ b/PS/_Tests/Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinition.Tests.ps1 b/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinition.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinition.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinitionFolder.Tests.ps1 b/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinitionFolder.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinitionFolder.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/ProcessTemplate/GetTfsProcessTemplate.Tests.ps1 b/PS/_Tests/ProcessTemplate/GetTfsProcessTemplate.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/ProcessTemplate/GetTfsProcessTemplate.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/RestApi/GetTfsRestClient.Tests.ps1 b/PS/_Tests/RestApi/GetTfsRestClient.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/RestApi/GetTfsRestClient.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 b/PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 index cb39915d..5f785af4 100644 --- a/PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 +++ b/PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { @@ -8,7 +8,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Invoke-TfsRestApi 'GET https://dev.azure.com/{organization}/_apis/projects?api-version=6.1' ` | ForEach-Object { $_.Name } ` | Sort-Object ` - | Should -Be @('AgileGit', 'TestProject', 'TfsCmdlets') + | Should -Be @('AgileGit', $tfsProject, 'TfsCmdlets') } It 'Should call an alternate host' { @@ -34,7 +34,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Invoke-TfsRestApi 'GET https://dev.azure.com/{organization}/_apis/projects?api-version=6.1' ` | ForEach-Object { $_.Name } ` | Sort-Object ` - | Should -Be @('AgileGit', 'TestProject', 'TfsCmdlets') + | Should -Be @('AgileGit', $tfsProject, 'TfsCmdlets') } @@ -42,7 +42,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Invoke-TfsRestApi -Path '/_apis/projects?api-version=6.1' -Method 'GET' -ApiVersion '6.1' -RequestContentType 'application/json' -ResponseContentType 'application/json' ` | ForEach-Object { $_.Name } ` | Sort-Object ` - | Should -Be @('AgileGit', 'TestProject', 'TfsCmdlets') + | Should -Be @('AgileGit', $tfsProject, 'TfsCmdlets') } It 'Should support NoAutoUnwrap' { diff --git a/PS/_Tests/ServiceHook/GetTfsServiceHookConsumer.Tests.ps1 b/PS/_Tests/ServiceHook/GetTfsServiceHookConsumer.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/ServiceHook/GetTfsServiceHookConsumer.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/ServiceHook/GetTfsServiceHookNotificationHistory.Tests.ps1 b/PS/_Tests/ServiceHook/GetTfsServiceHookNotificationHistory.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/ServiceHook/GetTfsServiceHookNotificationHistory.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/ServiceHook/GetTfsServiceHookPublisher.Tests.ps1 b/PS/_Tests/ServiceHook/GetTfsServiceHookPublisher.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/ServiceHook/GetTfsServiceHookPublisher.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/ServiceHook/GetTfsServiceHookSubscription.Tests.ps1 b/PS/_Tests/ServiceHook/GetTfsServiceHookSubscription.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/ServiceHook/GetTfsServiceHookSubscription.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Team/Backlog/GetTfsTeamBacklogLevel.Tests.ps1 b/PS/_Tests/Team/Backlog/GetTfsTeamBacklogLevel.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Team/Backlog/GetTfsTeamBacklogLevel.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Team/Board/GetTfsTeamBoard.Tests.ps1 b/PS/_Tests/Team/Board/GetTfsTeamBoard.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Team/Board/GetTfsTeamBoard.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Team/Board/GetTfsTeamBoardCardRule.Tests.ps1 b/PS/_Tests/Team/Board/GetTfsTeamBoardCardRule.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Team/Board/GetTfsTeamBoardCardRule.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Team/GetTfsTeam.Tests.ps1 b/PS/_Tests/Team/GetTfsTeam.Tests.ps1 index 8b319533..385b45f9 100644 --- a/PS/_Tests/Team/GetTfsTeam.Tests.ps1 +++ b/PS/_Tests/Team/GetTfsTeam.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { @@ -9,7 +9,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { } It 'Should get all teams' { - Get-TfsTeam -Project $tfsProject | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('PUL', 'PUL-DB', 'TestProject Team') + Get-TfsTeam -Project $tfsProject | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('PUL', 'PUL-DB', "$tfsProject Team") } It 'Should get some teams' { @@ -17,7 +17,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { } It 'Should get default team' { - Get-TfsTeam -Default -Project $tfsProject | Select-Object -ExpandProperty Name | Should -Be 'TestProject Team' + Get-TfsTeam -Default -Project $tfsProject | Select-Object -ExpandProperty Name | Should -Be "$tfsProject Team" } It 'Should get settings with -IncludeSettings' { diff --git a/PS/_Tests/Team/TeamAdmin/GetTfsTeamAdmin.Tests.ps1 b/PS/_Tests/Team/TeamAdmin/GetTfsTeamAdmin.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Team/TeamAdmin/GetTfsTeamAdmin.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 b/PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/TeamProject/GetTfsTeamProject.Tests.ps1 b/PS/_Tests/TeamProject/GetTfsTeamProject.Tests.ps1 index fe2fb781..6b576fcc 100644 --- a/PS/_Tests/TeamProject/GetTfsTeamProject.Tests.ps1 +++ b/PS/_Tests/TeamProject/GetTfsTeamProject.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { @@ -9,7 +9,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { } It 'Should get all projects' { - Get-TfsTeamProject | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('AgileGit', 'TestProject', 'TfsCmdlets') + Get-TfsTeamProject | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('AgileGit', $tfsProject, 'TfsCmdlets') } It 'Should not get process info without IncludeDetails' { diff --git a/PS/_Tests/TeamProject/Member/GetTfsTeamProjectMember.Tests.ps1 b/PS/_Tests/TeamProject/Member/GetTfsTeamProjectMember.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/TeamProject/Member/GetTfsTeamProjectMember.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/TeamProjectCollection/GetTfsTeamProjectCollection.Tests.ps1 b/PS/_Tests/TeamProjectCollection/GetTfsTeamProjectCollection.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/TeamProjectCollection/GetTfsTeamProjectCollection.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/TestManagement/GetTfsTestPlan.Tests.ps1 b/PS/_Tests/TestManagement/GetTfsTestPlan.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/TestManagement/GetTfsTestPlan.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/Wiki/GetTfsWiki.Tests.ps1 b/PS/_Tests/Wiki/GetTfsWiki.Tests.ps1 index 7d976ba2..c78e43c2 100644 --- a/PS/_Tests/Wiki/GetTfsWiki.Tests.ps1 +++ b/PS/_Tests/Wiki/GetTfsWiki.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { @@ -9,11 +9,11 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { } It 'Should get all wikis' { - Get-TfsWiki -Project $tfsProject | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('Docs', 'TestProject_wiki') + Get-TfsWiki -Project $tfsProject | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('Docs', "${tfsProject}_wiki") } It 'Should get project wikis' { - Get-TfsWiki -Project $tfsProject -ProjectWiki | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('TestProject_wiki') + Get-TfsWiki -Project $tfsProject -ProjectWiki | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @("${tfsProject}_wiki") } } diff --git a/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 b/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/WorkItem/AreasIterations/GetTfsIteration.Tests.ps1 b/PS/_Tests/WorkItem/AreasIterations/GetTfsIteration.Tests.ps1 index 30e56808..03bee780 100644 --- a/PS/_Tests/WorkItem/AreasIterations/GetTfsIteration.Tests.ps1 +++ b/PS/_Tests/WorkItem/AreasIterations/GetTfsIteration.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 b/PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/WorkItem/History/GetTfsWorkItemHistory.Tests.ps1 b/PS/_Tests/WorkItem/History/GetTfsWorkItemHistory.Tests.ps1 index 7671cd6c..8e5ef08b 100644 --- a/PS/_Tests/WorkItem/History/GetTfsWorkItemHistory.Tests.ps1 +++ b/PS/_Tests/WorkItem/History/GetTfsWorkItemHistory.Tests.ps1 @@ -1,11 +1,11 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { It 'Should get all revisions' { - (Get-TfsWorkItemHistory 150).Count | Should -Be 4 + (Get-TfsWorkItemHistory 150).Count | Should -Be 6 } } diff --git a/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLink.Tests.ps1 b/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLink.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLink.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLinkEndType.Tests.ps1 b/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLinkEndType.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLinkEndType.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file diff --git a/PS/_Tests/WorkItem/Query/GetTfsWorkItemQuery.Tests.ps1 b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQuery.Tests.ps1 index 20a8254d..7863fe3f 100644 --- a/PS/_Tests/WorkItem/Query/GetTfsWorkItemQuery.Tests.ps1 +++ b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQuery.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryFolder.Tests.ps1 b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryFolder.Tests.ps1 index df3374e7..966d8617 100644 --- a/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryFolder.Tests.ps1 +++ b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryFolder.Tests.ps1 @@ -1,4 +1,4 @@ -& "$($PSScriptRoot.Split('_Tests')[0])/_Tests/_TestSetup.ps1" +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { diff --git a/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryItemController.Tests.ps1 b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryItemController.Tests.ps1 new file mode 100644 index 00000000..ba690284 --- /dev/null +++ b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryItemController.Tests.ps1 @@ -0,0 +1,8 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context 'Integration Tests' { + Write-Warning "${$}: Not implemented" + } +} \ No newline at end of file From 45d5af0c54ebb8726f41786b2237c75ab701c130 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 6 Aug 2024 00:12:56 -0300 Subject: [PATCH 41/90] Add new tests --- PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 | 62 ++++++++++++++++++++- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 b/PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 index ba690284..aa8266e1 100644 --- a/PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 +++ b/PS/_Tests/Git/GetTfsGitRepository.Tests.ps1 @@ -2,7 +2,63 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" - } + Context 'Get by ID or Name' { + # Get-TfsGitRepository + # [[-Repository] ] + # [-IncludeParent] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return the repository by ID' { + $repository = Get-TfsGitRepository -Repository '5db56f26-27b9-44a1-b906-814d03982840' -Project $tfsProject + $repository.Name | Should -Be 'PartsUnlimited' + $repository | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitRepository' + } + + It 'Should return the repository by Name' { + $repository = Get-TfsGitRepository -Repository 'PartsUnlimited' -Project $tfsProject + $repository.Name | Sort-Object | Should -Be 'PartsUnlimited' + $repository | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitRepository' + } + + It 'Should return multiple repositories by Name' { + $repositories = Get-TfsGitRepository -Repository 'PartsUnlimited', 'OtherRepo' -Project $tfsProject + $repositories.Name | Sort-Object | Should -Be @('OtherRepo', 'PartsUnlimited') + } + + It 'Should support wildcards' { + $repository = Get-TfsGitRepository -Repository '*Repo' -Project $tfsProject + $repository.Name | Sort-Object | Should -Be @('DisableRepo', 'OtherRepo') + $repository | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitRepository' + } + + It 'Should return the parent of a forked repo with IncludeParent' { + $repository = Get-TfsGitRepository -Repository 'AgileGit' -IncludeParent -Project $tfsProject + $repository.Name | Should -Be 'AgileGit' + $repository.ParentRepository | Should -Not -BeNullOrEmpty + $repository | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitRepository' + } + + It 'Should return the repository by Name with Project' { + $repository = Get-TfsGitRepository -Repository 'PartsUnlimited' -Project $tfsProject + $repository.Name | Should -Be 'PartsUnlimited' + $repository | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitRepository' + } + } + + Context 'Get default' { + # Get-TfsGitRepository + # -Default + # [-IncludeParent] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return the default repository' { + $repository = Get-TfsGitRepository -Default -Project $tfsProject + $repository.Name | Should -Be $tfsProject + $repository | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitRepository' + } + } } \ No newline at end of file From 5a7c8aaefcf64c4a957d93cb91906dd31bc6e14f Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 6 Aug 2024 10:32:24 -0300 Subject: [PATCH 42/90] Update test publish task --- .github/workflows/main.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 61320b21..58546cbf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,12 +38,11 @@ jobs: - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 - name: Publish Test Results - uses: dorny/test-reporter@v1 + uses: EnricoMi/publish-unit-test-result-action/windows@v2 if: always() with: - name: Pester Test Results - path: 'out/TestResults-Pester*.xml' # Path to test results (inside artifact .zip) - reporter: dotnet-trx + files: | + out\TestResults-Pester*.xml - name: Publish Nuget uses: actions/upload-artifact@v4 with: From 7ab64448c2d450fa650461db0a26102ac63498e8 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 6 Aug 2024 23:38:49 -0300 Subject: [PATCH 43/90] Update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fb580a70..6f9636e8 100644 --- a/.gitignore +++ b/.gitignore @@ -220,3 +220,4 @@ CSharp/.idea/** CSharp/*/Generated/** Docs/AzDO-Security-Actions.csv **/testResults.xml +PS/_Tests/coverage.xml From 815320cfbdfcfb7f0ce216fed3b35f9e94b80f90 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 6 Aug 2024 23:39:00 -0300 Subject: [PATCH 44/90] Remove obsolete tests --- .../Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 | 14 -------------- .../Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 | 14 -------------- 2 files changed, 28 deletions(-) delete mode 100644 PS/_Tests/Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 delete mode 100644 PS/_Tests/Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 diff --git a/PS/_Tests/Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 b/PS/_Tests/Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 deleted file mode 100644 index 1e79e54e..00000000 --- a/PS/_Tests/Pipeline/Build/GetTfsBuildDefinition.Tests.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" - -Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - - Context 'Integration Tests' { - - It 'Should get pipelines' { - Get-TfsBuildDefinition -Project $tfsProject ` - | Select-Object -ExpandProperty Name ` - | Should -Be 'PartsUnlimitedE2E' - } - - } -} diff --git a/PS/_Tests/Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 b/PS/_Tests/Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 deleted file mode 100644 index 49e9b1fc..00000000 --- a/PS/_Tests/Pipeline/GetTfsBuildDefinitionFolder.Tests.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" - -Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - - Context 'Integration Tests' { - - It 'Should get all folders' { - Get-TfsBuildDefinitionFolder -Project $tfsProject ` - | Select-Object -ExpandProperty Name ` - | Should -Be @('\', 'CD Pipelines') - } - - } -} From 2179aee09b5cd110e87e06e75bc32a00527803cc Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 6 Aug 2024 23:39:08 -0300 Subject: [PATCH 45/90] Add new tests --- .../Member/GetTeamProjectMember.cs | 49 +++++++++++++------ .../GetTfsBuildDefinition.Tests.ps1 | 28 ++++++++++- .../GetTfsBuildDefinitionFolder.Tests.ps1 | 26 +++++++++- .../GetTfsReleaseDefinition.Tests.ps1 | 20 +++++++- .../GetTfsReleaseDefinitionFolder.Tests.ps1 | 26 +++++++++- .../GetTfsProcessTemplate.Tests.ps1 | 32 +++++++++++- PS/_Tests/RestApi/GetTfsRestClient.Tests.ps1 | 12 ++++- PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 | 3 +- .../Member/GetTfsTeamProjectMember.Tests.ps1 | 42 +++++++++++++++- 9 files changed, 207 insertions(+), 31 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProject/Member/GetTeamProjectMember.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProject/Member/GetTeamProjectMember.cs index 1d78764d..ed947feb 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProject/Member/GetTeamProjectMember.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProject/Member/GetTeamProjectMember.cs @@ -1,6 +1,6 @@ using System.Management.Automation; -using Newtonsoft.Json.Linq; using TfsCmdlets.Models; +using TfsIdentity = Microsoft.VisualStudio.Services.Identity.Identity; namespace TfsCmdlets.Cmdlets.TeamProject.Member { @@ -15,7 +15,7 @@ partial class GetTeamProjectMember /// Specifies the name of a team project member. Wildcards are supported. /// When omitted, all team project members are returned. /// - [Parameter()] + [Parameter(Position = 0)] public object Member { get; set; } = "*"; /// @@ -30,19 +30,24 @@ partial class GetTeamProjectMember partial class GetTeamProjectMemberController { [Import] - private IRestApiService RestApiService {get;} + private IRestApiService RestApiService { get; } protected override IEnumerable Run() { - var query = new ContributionNodeQuery { + var query = new ContributionNodeQuery + { ContributionIds = new[] { "ms.vss-tfs-web.project-members-data-provider-verticals" }, - DataProviderContext = new DataProviderContext{ - Properties = new Dictionary() { + DataProviderContext = new DataProviderContext + { + Properties = new Dictionary() + { ["forceRefresh"] = true, - ["sourcePage"] = new Dictionary() { + ["sourcePage"] = new Dictionary() + { ["url"] = Project.GetLink("web"), ["routeId"] = "ms.vss-tfs-web.project-overview-route", - ["routeValues"] = new Dictionary() { + ["routeValues"] = new Dictionary() + { ["project"] = Project.Name, ["controller"] = "Apps", ["action"] = "ContributedHub" @@ -59,16 +64,30 @@ protected override IEnumerable Run() .DataProviders["ms.vss-tfs-web.project-members-data-provider-verticals"] .ToObject(); - foreach(var member in col.Members) + foreach (var input in Member) { - if(AsIdentity) + string member = input switch { + string s => s, + TeamProjectMember m => m.Email, + TfsIdentity i => i.Properties["Account"]?.ToString(), + _ => null + } ?? throw new ArgumentException($"Invalid member [{input}]"); + + foreach (var m in col.Members) { - yield return GetItem(new {Identity=member.Email}); - continue; - } + if(!(m.Email.IsLike(member) || m.Name.IsLike(member))) + { + continue; + } + + if(AsIdentity) { + yield return GetItem(new {Identity=m.Email}); + continue; + } - member.TeamProject = Project.Name; - yield return member; + m.TeamProject = Project.Name; + yield return m; + } } } } diff --git a/PS/_Tests/Pipeline/Build/Definition/GetTfsBuildDefinition.Tests.ps1 b/PS/_Tests/Pipeline/Build/Definition/GetTfsBuildDefinition.Tests.ps1 index ba690284..06843717 100644 --- a/PS/_Tests/Pipeline/Build/Definition/GetTfsBuildDefinition.Tests.ps1 +++ b/PS/_Tests/Pipeline/Build/Definition/GetTfsBuildDefinition.Tests.ps1 @@ -2,7 +2,31 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Context '__AllParameterSets' { + # Get-TfsBuildDefinition + # [[-Definition] ] + # [-QueryOrder ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all build definitions' { + $result = Get-TfsBuildDefinition -Project $tfsProject + $result | Should -BeOfType 'Microsoft.TeamFoundation.Build.WebApi.BuildDefinition' + $result.Name | Sort-Object | Should -Be @('PartsUnlimitedE2E', 'TestProject') + } + + It 'Should support wildcards' { + $result = Get-TfsBuildDefinition -Project $tfsProject -Definition 'PartsUnlimited*' + $result | Should -BeOfType 'Microsoft.TeamFoundation.Build.WebApi.BuildDefinition' + $result.Name | Should -Be 'PartsUnlimitedE2E' + } + + It 'Should support -QueryOrder' { + (Get-TfsBuildDefinition -QueryOrder DefinitionNameAscending -Project $tfsProject).Name | Should -Be @('PartsUnlimitedE2E', 'TestProject') + (Get-TfsBuildDefinition -QueryOrder DefinitionNameDescending -Project $tfsProject).Name | Should -Be @('TestProject', 'PartsUnlimitedE2E') + (Get-TfsBuildDefinition -QueryOrder LastModifiedAscending -Project $tfsProject).Name | Should -Be @('PartsUnlimitedE2E', 'TestProject') + (Get-TfsBuildDefinition -QueryOrder LastModifiedDescending -Project $tfsProject).Name | Should -Be @('TestProject', 'PartsUnlimitedE2E') + } } } \ No newline at end of file diff --git a/PS/_Tests/Pipeline/Build/Folder/GetTfsBuildDefinitionFolder.Tests.ps1 b/PS/_Tests/Pipeline/Build/Folder/GetTfsBuildDefinitionFolder.Tests.ps1 index ba690284..487782db 100644 --- a/PS/_Tests/Pipeline/Build/Folder/GetTfsBuildDefinitionFolder.Tests.ps1 +++ b/PS/_Tests/Pipeline/Build/Folder/GetTfsBuildDefinitionFolder.Tests.ps1 @@ -2,7 +2,29 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Context '__AllParameterSets' { + # Get-TfsBuildDefinitionFolder + # [[-Folder] ] + # [-QueryOrder ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all build definition folders' { + $result = Get-TfsBuildDefinitionFolder -Project $tfsProject + $result | Should -BeOfType 'Microsoft.TeamFoundation.Build.WebApi.Folder' + $result.Name | Sort-Object | Should -Be @('\', 'CD Pipelines', 'YAML Pipelines') + } + + It 'Should support wildcards' { + $result = Get-TfsBuildDefinitionFolder 'CD*' -Project $tfsProject + $result | Should -BeOfType 'Microsoft.TeamFoundation.Build.WebApi.Folder' + $result.Name | Should -Be 'CD Pipelines' + } + + It 'Should support -QueryOrder' { + (Get-TfsBuildDefinitionFolder -QueryOrder FolderAscending -Project $tfsProject).Name | Should -Be @('\', 'CD Pipelines', 'YAML Pipelines') + (Get-TfsBuildDefinitionFolder -QueryOrder FolderDescending -Project $tfsProject).Name | Should -Be @('YAML Pipelines', 'CD Pipelines', '\') + } } } \ No newline at end of file diff --git a/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinition.Tests.ps1 b/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinition.Tests.ps1 index ba690284..4421e447 100644 --- a/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinition.Tests.ps1 +++ b/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinition.Tests.ps1 @@ -2,7 +2,23 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Context '__AllParameterSets' { + # Get-TfsReleaseDefinition + # [[-Definition] ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all release definitions' { + $result = Get-TfsReleaseDefinition -Project $tfsProject + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.ReleaseDefinition' + $result.Name | Sort-Object | Should -Be @('PartsUnlimitedE2E', 'TestProject-CD') + } + + It 'Should support wildcards' { + $result = Get-TfsReleaseDefinition -Project $tfsProject -Definition 'Parts*' + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.ReleaseDefinition' + $result.Name | Should -Be 'PartsUnlimitedE2E' + } } } \ No newline at end of file diff --git a/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinitionFolder.Tests.ps1 b/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinitionFolder.Tests.ps1 index ba690284..bd80ceb9 100644 --- a/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinitionFolder.Tests.ps1 +++ b/PS/_Tests/Pipeline/ReleaseManagement/GetTfsReleaseDefinitionFolder.Tests.ps1 @@ -2,7 +2,29 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Context '__AllParameterSets' { + # Get-TfsReleaseDefinitionFolder + # [[-Folder] ] + # [-QueryOrder ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all release definition folders' { + $result = Get-TfsReleaseDefinitionFolder -Project $tfsProject + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.Folder' + $result.Name | Sort-Object | Should -Be @('\', 'TestProject') + } + + It 'Should support wildcards' { + $result = Get-TfsReleaseDefinitionFolder -Project $tfsProject -Folder 'Test*' + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.Folder' + $result.Name | Should -Be 'TestProject' + } + + It 'Should support -QueryOrder' { + (Get-TfsReleaseDefinitionFolder -QueryOrder Ascending -Project $tfsProject).Name | Should -Be @('\', 'TestProject') + (Get-TfsReleaseDefinitionFolder -QueryOrder Descending -Project $tfsProject).Name | Should -Be @('TestProject', '\') + } } } \ No newline at end of file diff --git a/PS/_Tests/ProcessTemplate/GetTfsProcessTemplate.Tests.ps1 b/PS/_Tests/ProcessTemplate/GetTfsProcessTemplate.Tests.ps1 index ba690284..a5d186fc 100644 --- a/PS/_Tests/ProcessTemplate/GetTfsProcessTemplate.Tests.ps1 +++ b/PS/_Tests/ProcessTemplate/GetTfsProcessTemplate.Tests.ps1 @@ -2,7 +2,35 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Context 'Get by name' { + # Get-TfsProcessTemplate + # [[-ProcessTemplate] ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all processes' { + $result = Get-TfsProcessTemplate + $result | Should -BeOfType 'Microsoft.TeamFoundation.Core.WebApi.Process' + $result.Name | Sort-Object | Should -Be @('Agile', 'Agile TfsCmdlets', 'Agile-TfsCmdlets', 'Basic', 'CMMI', 'Scrum') + } + + It 'Should support wildcards' { + $result = Get-TfsProcessTemplate -ProcessTemplate 'Agile*' + $result | Should -BeOfType 'Microsoft.TeamFoundation.Core.WebApi.Process' + $result.Name | Sort-Object | Should -Be @('Agile', 'Agile TfsCmdlets', 'Agile-TfsCmdlets') + } + } + + Context 'Get default process' { + # Get-TfsProcessTemplate + # -Default + # [-Collection ] + # [-Server ] [] + + It 'Should return the default process' { + $result = Get-TfsProcessTemplate -Default + $result | Should -BeOfType 'Microsoft.TeamFoundation.Core.WebApi.Process' + $result.Name | Should -Be 'Agile TfsCmdlets' + } } } \ No newline at end of file diff --git a/PS/_Tests/RestApi/GetTfsRestClient.Tests.ps1 b/PS/_Tests/RestApi/GetTfsRestClient.Tests.ps1 index ba690284..de9ed8a0 100644 --- a/PS/_Tests/RestApi/GetTfsRestClient.Tests.ps1 +++ b/PS/_Tests/RestApi/GetTfsRestClient.Tests.ps1 @@ -2,7 +2,15 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Context '__AllParameterSets' { + # Get-TfsRestClient + # [-TypeName] + # [-Collection ] + # [-Server ] [] + + It 'Should get a REST client' { + $result = Get-TfsRestClient 'Microsoft.TeamFoundation.Core.WebApi.ProjectHttpClient' + $result | Should -BeOfType 'Microsoft.TeamFoundation.Core.WebApi.ProjectHttpClient' + } } } \ No newline at end of file diff --git a/PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 b/PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 index 5f785af4..2acaf4fb 100644 --- a/PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 +++ b/PS/_Tests/RestApi/InvokeTfsRestApi.Tests.ps1 @@ -29,7 +29,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Invoke-TfsRestApi 'GET https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/definitions?api-version=6.1' -Project $tfsProject ` | ForEach-Object { $_.name } ` | Sort-Object ` - | Should -Be @('PartsUnlimitedE2E') + | Should -Be @('PartsUnlimitedE2E', 'TestProject-CD') Invoke-TfsRestApi 'GET https://dev.azure.com/{organization}/_apis/projects?api-version=6.1' ` | ForEach-Object { $_.Name } ` @@ -37,7 +37,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { | Should -Be @('AgileGit', $tfsProject, 'TfsCmdlets') } - It 'Should support standard parameter-based call' { Invoke-TfsRestApi -Path '/_apis/projects?api-version=6.1' -Method 'GET' -ApiVersion '6.1' -RequestContentType 'application/json' -ResponseContentType 'application/json' ` | ForEach-Object { $_.Name } ` diff --git a/PS/_Tests/TeamProject/Member/GetTfsTeamProjectMember.Tests.ps1 b/PS/_Tests/TeamProject/Member/GetTfsTeamProjectMember.Tests.ps1 index ba690284..aed9ad1a 100644 --- a/PS/_Tests/TeamProject/Member/GetTfsTeamProjectMember.Tests.ps1 +++ b/PS/_Tests/TeamProject/Member/GetTfsTeamProjectMember.Tests.ps1 @@ -2,7 +2,45 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Context '__AllParameterSets' { + # Get-TfsTeamProjectMember + # [-Member ] + # [-AsIdentity] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all members' { + $result = Get-TfsTeamProjectMember -Project $tfsProject + $result | Should -BeOfType 'TfsCmdlets.Models.TeamProjectMember' + $result.Name | Sort-Object | Should -Be @('Brian Keller', 'Igor Abade (T-Shooter)') + } + + It 'Should return a single member by name' { + $result = Get-TfsTeamProjectMember -Member 'Igor Abade (T-Shooter)' -Project $tfsProject + $result | Should -BeOfType 'TfsCmdlets.Models.TeamProjectMember' + $result.Name | Should -Be 'Igor Abade (T-Shooter)' + } + + It 'Should return a single member by email' { + $result = Get-TfsTeamProjectMember -Member 'igor@tshooter.com.br' -Project $tfsProject + $result | Should -BeOfType 'TfsCmdlets.Models.TeamProjectMember' + $result.Name | Should -Be 'Igor Abade (T-Shooter)' + } + + It 'Should support wildcards' { + $result = Get-TfsTeamProjectMember -Project $tfsProject -Member 'Brian K*' + $result | Should -BeOfType 'TfsCmdlets.Models.TeamProjectMember' + $result.Name | Should -Be 'Brian Keller' + $result = Get-TfsTeamProjectMember -Project $tfsProject -Member 'brian@*' + $result | Should -BeOfType 'TfsCmdlets.Models.TeamProjectMember' + $result.Name | Should -Be 'Brian Keller' + } + + It 'Should return as identity' { + $result = Get-TfsTeamProjectMember 'Igor Abade (T-Shooter)' -Project $tfsProject -AsIdentity + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' + $result.DisplayName | Should -Be 'Igor Abade (T-Shooter)' + } } } \ No newline at end of file From e20078cb2a4147d80054cbd3e0a53a77a8067718 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 6 Aug 2024 23:41:15 -0300 Subject: [PATCH 46/90] Update test --- PS/_Tests/Team/GetTfsTeam.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PS/_Tests/Team/GetTfsTeam.Tests.ps1 b/PS/_Tests/Team/GetTfsTeam.Tests.ps1 index 385b45f9..cce605f7 100644 --- a/PS/_Tests/Team/GetTfsTeam.Tests.ps1 +++ b/PS/_Tests/Team/GetTfsTeam.Tests.ps1 @@ -27,7 +27,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { It 'Should get members with -QueryMembership' { (Get-TfsTeam -Default -Project $tfsProject).TeamMembers.Length | Should -Be 0 - (Get-TfsTeam -Default -Project $tfsProject -QueryMembership).TeamMembers.Length | Should -Be 1 + (Get-TfsTeam -Default -Project $tfsProject -QueryMembership).TeamMembers.Length | Should -Be 2 } } From dfeb1af105eee5169a99314dca08d7fb7b8f4158 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Wed, 7 Aug 2024 00:29:42 -0300 Subject: [PATCH 47/90] Add new tests --- .../Team/Board/GetTfsTeamBoard.Tests.ps1 | 27 ++++++++++++-- PS/_Tests/Team/GetTfsTeam.Tests.ps1 | 4 +-- .../Team/TeamAdmin/GetTfsTeamAdmin.Tests.ps1 | 36 +++++++++++++++++-- .../TeamMember/GetTfsTeamMember.Tests.ps1 | 36 +++++++++++++++++-- 4 files changed, 95 insertions(+), 8 deletions(-) diff --git a/PS/_Tests/Team/Board/GetTfsTeamBoard.Tests.ps1 b/PS/_Tests/Team/Board/GetTfsTeamBoard.Tests.ps1 index ba690284..83944b4b 100644 --- a/PS/_Tests/Team/Board/GetTfsTeamBoard.Tests.ps1 +++ b/PS/_Tests/Team/Board/GetTfsTeamBoard.Tests.ps1 @@ -2,7 +2,30 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Context '__AllParameterSets' { + # Get-TfsTeamBoard + # [[-Board] ] + # [-Team ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all team boards' { + $result = Get-TfsTeamBoard -Project $tfsProject -Team "$tfsProject Team" + $result | Should -BeOfType 'Microsoft.TeamFoundation.Work.WebApi.Board' + $result.Name | Sort-Object | Should -Be @('Backlog items', 'Epics', 'Features') + } + + It 'Should return a single team board by name' { + $result = Get-TfsTeamBoard -Board 'Epics' -Project $tfsProject -Team "$tfsProject Team" + $result | Should -BeOfType 'Microsoft.TeamFoundation.Work.WebApi.Board' + $result.Name | Should -Be 'Epics' + } + + It 'Should support wildcards' { + $result = Get-TfsTeamBoard 'F*' -Project $tfsProject -Team "$tfsProject Team" + $result | Should -BeOfType 'Microsoft.TeamFoundation.Work.WebApi.Board' + $result.Name | Should -Be 'Features' + } } } \ No newline at end of file diff --git a/PS/_Tests/Team/GetTfsTeam.Tests.ps1 b/PS/_Tests/Team/GetTfsTeam.Tests.ps1 index cce605f7..dc8d71db 100644 --- a/PS/_Tests/Team/GetTfsTeam.Tests.ps1 +++ b/PS/_Tests/Team/GetTfsTeam.Tests.ps1 @@ -26,8 +26,8 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { } It 'Should get members with -QueryMembership' { - (Get-TfsTeam -Default -Project $tfsProject).TeamMembers.Length | Should -Be 0 - (Get-TfsTeam -Default -Project $tfsProject -QueryMembership).TeamMembers.Length | Should -Be 2 + (Get-TfsTeam -Default -Project $tfsProject).TeamMembers.Count | Should -Be 0 + (Get-TfsTeam -Default -Project $tfsProject -QueryMembership).TeamMembers.Count | Should -Be 2 } } diff --git a/PS/_Tests/Team/TeamAdmin/GetTfsTeamAdmin.Tests.ps1 b/PS/_Tests/Team/TeamAdmin/GetTfsTeamAdmin.Tests.ps1 index ba690284..8c0943b9 100644 --- a/PS/_Tests/Team/TeamAdmin/GetTfsTeamAdmin.Tests.ps1 +++ b/PS/_Tests/Team/TeamAdmin/GetTfsTeamAdmin.Tests.ps1 @@ -2,7 +2,39 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Context '__AllParameterSets' { + # Get-TfsTeamAdmin + # [[-Admin] ] + # [-Team ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all team admins' { + $result = Get-TfsTeamAdmin -Project $tfsProject -Team "$tfsProject Team" + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' + $result.DisplayName | Sort-Object | Should -Be @('Igor Abade (T-Shooter)') + } + + It 'Should return a single team admin by name' { + $result = Get-TfsTeamAdmin -Admin 'Igor Abade (T-Shooter)' -Project $tfsProject -Team "$tfsProject Team" + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' + $result.DisplayName | Should -Be 'Igor Abade (T-Shooter)' + } + + It 'Should return a single team admin by email' { + $result = Get-TfsTeamAdmin -Admin 'igor@tshooter.com.br' -Project $tfsProject -Team "$tfsProject Team" + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' + $result.DisplayName | Should -Be 'Igor Abade (T-Shooter)' + } + + It 'Should support wildcards' { + $result = Get-TfsTeamAdmin -Project $tfsProject -Team "$tfsProject Team" -Admin 'Igor A*' + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' + $result.DisplayName | Should -Be 'Igor Abade (T-Shooter)' + $result = Get-TfsTeamAdmin -Project $tfsProject -Team "$tfsProject Team" -Admin 'igor@*' + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' + $result.DisplayName | Should -Be 'Igor Abade (T-Shooter)' + } } } \ No newline at end of file diff --git a/PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 b/PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 index ba690284..a15d6733 100644 --- a/PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 +++ b/PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 @@ -2,7 +2,39 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Context '__AllParameterSets' { + # Get-TfsTeamMember + # [[-Member] ] + # [-Team ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all team members' { + $result = Get-TfsTeamMember -Project $tfsProject -Team "$tfsProject Team" + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' + $result.DisplayName | Sort-Object | Should -Be @('Igor Abade (T-Shooter)') + } + + It 'Should return a single team member by name' { + $result = Get-TfsTeamMember -Member 'Igor Abade (T-Shooter)' -Project $tfsProject -Team "$tfsProject Team" + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' + $result.DisplayName | Should -Be 'Igor Abade (T-Shooter)' + } + + It 'Should return a single team member by email' { + $result = Get-TfsTeamMember -Member 'igor@tshooter.com.br' -Project $tfsProject -Team "$tfsProject Team" + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' + $result.DisplayName | Should -Be 'Igor Abade (T-Shooter)' + } + + It 'Should support wildcards' { + $result = Get-TfsTeamMember -Project $tfsProject -Team "$tfsProject Team" -Member 'Igor A*' + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' + $result.DisplayName | Should -Be 'Igor Abade (T-Shooter)' + $result = Get-TfsTeamMember -Project $tfsProject -Team "$tfsProject Team" -Member 'igor@*' + $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' + $result.DisplayName | Should -Be 'Igor Abade (T-Shooter)' + } } } \ No newline at end of file From d97ec181fd905211e3840d0d86e2cccd37ae22cb Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Wed, 7 Aug 2024 11:18:41 -0300 Subject: [PATCH 48/90] Update tests --- PS/_Tests/Admin/GetTfsVersion.Tests.ps1 | 2 +- PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PS/_Tests/Admin/GetTfsVersion.Tests.ps1 b/PS/_Tests/Admin/GetTfsVersion.Tests.ps1 index bd2767a4..39c575c0 100644 --- a/PS/_Tests/Admin/GetTfsVersion.Tests.ps1 +++ b/PS/_Tests/Admin/GetTfsVersion.Tests.ps1 @@ -2,7 +2,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Unit Tests' { + Context '__AllParameterSets' { It 'Should get version from hosted service' -Tag 'Hosted' { $version = Get-TfsVersion diff --git a/PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 b/PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 index a15d6733..e66e4276 100644 --- a/PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 +++ b/PS/_Tests/Team/TeamMember/GetTfsTeamMember.Tests.ps1 @@ -13,7 +13,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { It 'Should return all team members' { $result = Get-TfsTeamMember -Project $tfsProject -Team "$tfsProject Team" $result | Should -BeOfType 'Microsoft.VisualStudio.Services.Identity.Identity' - $result.DisplayName | Sort-Object | Should -Be @('Igor Abade (T-Shooter)') + $result.DisplayName | Sort-Object | Should -Be @('Brian Keller', 'Igor Abade (T-Shooter)') } It 'Should return a single team member by name' { From 7ed0efa684d1ab0fdc5784cf7fba7f16ddab7f2d Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Wed, 7 Aug 2024 12:59:14 -0300 Subject: [PATCH 49/90] Update release notes --- RELEASENOTES.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9a57595c..8fb2694f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,7 +2,7 @@ ## Version 2.9.0 (_03/Aug/2024_) -This release adds some new process-related cmdlets. +This release adds new process-related cmdlets along with some fixes, enhancements and a couple minor but potentially breaking changes. ## New cmdlets @@ -10,12 +10,17 @@ This release adds some new process-related cmdlets. - `New-TfsWorkItemField`: Creates a new organization-wide work item field - `Remove-TfsWorkItemField`: Removes an organization-wide work item field -## Fixes and enhancements +## Fixes - Fixed an issue with `Get-TfsArtifact` where it wasn't listing deleted packages. - Fixed an issue with `Get-TfsArtifactFeed` where it would ignore the -Project argument and thus not filter feeds by project. - Fixed an issue with `Get-TfsWorkItemTag` where it would fail when given a list of tags as input. - Fixed an issue with `Get-TfsWorkItemType` where it would throw a "Parameter count mismatch" error when trying to get the work item type of a given work item. + +## Changes and enhancements + +- **BREAKING**: In `Get-TfsGitBranch`, `-Repository` parameter is now mandatory. This is to reduce the ambiguity of the command when omitting that argument. Scripts that rely on the old behavior will need to be updated. +- **BREAKING**: In `Get-TfsGitBranchPolicy`, both `-Branch` and `-Repository` parameters are now mandatory. This is to reduce the ambiguity of the command when omitting those arguments. Scripts that rely on the old behavior will need to be updated. - Now `Connect-TfsTeamProjectCollection` (and its counterpart `Connect-TfsOrganization`) throws an error when trying to connect with invalid credentials instead of silently going into "anonymous mode". That help preventing subtle script errors where the lack of authorization would only be noticed later in the script, when trying to actually perform some command that required valid credentials. Now you get the warning that something is wrong as early in the script as possible. ----------------------- From a8ec61736a91b76cb37821e3c5fd363ae08f2484 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Wed, 7 Aug 2024 12:59:35 -0300 Subject: [PATCH 50/90] Make Repository mandatory --- .../TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs index b1f80764..8135d67a 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs @@ -21,16 +21,16 @@ partial class GetGitBranch public object Branch { get; set; } = "*"; /// - /// Returns the default branch in the given repository. + /// HELP_PARAM_GIT_REPOSITORY /// - [Parameter(Mandatory = true, ParameterSetName = "Get default")] - public SwitchParameter Default { get; set; } + [Parameter(ValueFromPipeline = true, Mandatory = true, Position = 1)] + public object Repository { get; set; } /// - /// HELP_PARAM_GIT_REPOSITORY + /// Returns the default branch in the given repository. /// - [Parameter(ValueFromPipeline = true)] - public object Repository { get; set; } + [Parameter(Mandatory = true, ParameterSetName = "Get default")] + public SwitchParameter Default { get; set; } } [CmdletController(typeof(GitBranchStats), Client=typeof(IGitHttpClient))] @@ -38,7 +38,7 @@ partial class GetGitBranchController { protected override IEnumerable Run() { - var repo = GetItem(new { Repository = Has_Repository? Repository: Project.Name }); + var repo = GetItem(new { Repository, Default = false }); if (repo.Size == 0) { From 3baafafd6cb7c72ff0b0c39aff509cd85681e5c2 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Wed, 7 Aug 2024 12:59:55 -0300 Subject: [PATCH 51/90] Make Repository and Branch mandatory --- CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitBranchPolicy.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitBranchPolicy.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitBranchPolicy.cs index cad964ee..fdd0e590 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitBranchPolicy.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Policy/GetGitBranchPolicy.cs @@ -18,17 +18,16 @@ partial class GetGitBranchPolicy public object PolicyType { get; set; } = "*"; /// - /// Specifies the name of the branch to query for branch policies. When omitted, - /// the default branch in the given repository is queried. + /// Specifies the name of the branch to query for branch policies. /// - [Parameter(ValueFromPipeline = true)] + [Parameter(ValueFromPipeline = true, Mandatory = true, Position = 1)] [Alias("RefName")] public object Branch { get; set; } /// /// HELP_PARAM_GIT_REPOSITORY /// - [Parameter] + [Parameter(Mandatory = true, Position = 2)] public object Repository { get; set; } } From 18414c6198524df37be2c284ef415098835c5e28 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Wed, 7 Aug 2024 13:00:01 -0300 Subject: [PATCH 52/90] Update tests --- .../Git/Branch/GetTfsGitBranch.Tests.ps1 | 46 +++++++++++++++++-- .../Policy/GetTfsGitBranchPolicy.Tests.ps1 | 30 ++++++++++-- .../Git/Policy/GetTfsGitPolicyType.Tests.ps1 | 35 ++++++++++++-- 3 files changed, 101 insertions(+), 10 deletions(-) diff --git a/PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 b/PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 index ba690284..d7a42844 100644 --- a/PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 +++ b/PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 @@ -2,7 +2,47 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" - } + Context 'Get by name' { + # Get-TfsGitBranch + # [[-Branch] ] + # [-Repository ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all branches' { + $result = Get-TfsGitBranch -Repository 'PartsUnlimited' -Project $tfsProject + $result | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitBranchStats' + $result.Name | Sort-Object | Should -Be @('AddTerms&Conditions', 'CodedUITest', 'demo-start', 'DependencyValidation', 'e2e-complete', 'FixSearchFunctionality', 'IntelliTest', 'LiveUnitTesting', 'master', 'PerfAndLoadTesting', 'sjbdemo') + } + + It 'Should support wildcards' { + $result = Get-TfsGitBranch 'd*' -Repository 'PartsUnlimited' -Project $tfsProject + $result | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitBranchStats' + $result.Name | Sort-Object | Should -Be @('demo-start', 'DependencyValidation') + } + } + + Context 'Get default' { + # Get-TfsGitBranch + # -Default + # [-Repository ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return the default branch' { + $result = Get-TfsGitBranch -Default -Repository 'PartsUnlimited' -Project $tfsProject + $result | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitBranchStats' + $result.Name | Should -Be 'master' + } + } + + Context 'Type And Format' { + It 'Should add Project and Repository properties' { + $result = Get-TfsGitBranch 'master' -Repository 'PartsUnlimited' -Project $tfsProject + $result.Project | Should -Be $tfsProject + $result.Repository | Should -Be 'PartsUnlimited' + } + } } \ No newline at end of file diff --git a/PS/_Tests/Git/Policy/GetTfsGitBranchPolicy.Tests.ps1 b/PS/_Tests/Git/Policy/GetTfsGitBranchPolicy.Tests.ps1 index ba690284..02a38144 100644 --- a/PS/_Tests/Git/Policy/GetTfsGitBranchPolicy.Tests.ps1 +++ b/PS/_Tests/Git/Policy/GetTfsGitBranchPolicy.Tests.ps1 @@ -2,7 +2,31 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Context '__AllParameterSets' { + # Get-TfsGitBranchPolicy + # [[-PolicyType] ] + # [-Branch ] + # [-Repository ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all branch policies' { + $result = Get-TfsGitBranchPolicy -Branch 'master' -Repository 'PartsUnlimited' -Project $tfsProject + $result | Should -BeOfType 'Microsoft.TeamFoundation.Policy.WebApi.PolicyConfiguration' + $result.DisplayName | Sort-Object | Should -Be @('Build', 'Minimum number of reviewers', 'Require a merge strategy', 'Status') + } + + It 'Should return a single policy' { + $result = Get-TfsGitBranchPolicy 'Build' -Branch 'master' -Repository 'PartsUnlimited' -Project $tfsProject + $result | Should -BeOfType 'Microsoft.TeamFoundation.Policy.WebApi.PolicyConfiguration' + $result.DisplayName | Should -Be 'Build' + } + + It 'Should support wildcards' { + $result = Get-TfsGitBranchPolicy 'B*' -Branch 'master' -Repository 'PartsUnlimited' -Project $tfsProject + $result | Should -BeOfType 'Microsoft.TeamFoundation.Policy.WebApi.PolicyConfiguration' + $result.DisplayName | Should -Be @('Build') + } } -} \ No newline at end of file +} diff --git a/PS/_Tests/Git/Policy/GetTfsGitPolicyType.Tests.ps1 b/PS/_Tests/Git/Policy/GetTfsGitPolicyType.Tests.ps1 index ba690284..df1e914d 100644 --- a/PS/_Tests/Git/Policy/GetTfsGitPolicyType.Tests.ps1 +++ b/PS/_Tests/Git/Policy/GetTfsGitPolicyType.Tests.ps1 @@ -2,7 +2,34 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" - } -} \ No newline at end of file + Context '__AllParameterSets' { + # Get-TfsGitPolicyType + # [[-PolicyType] ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return all policy types' { + $result = Get-TfsGitPolicyType -Project $tfsProject + $result | Should -BeOfType 'Microsoft.TeamFoundation.Policy.WebApi.PolicyType' + $result.DisplayName | Sort-Object | Should -Be @('Build', 'Comment requirements', 'Commit author email validation', ` + 'File name restriction', 'File size restriction', 'Git repository settings', ` + 'GitRepositorySettingsPolicyName', 'Minimum number of reviewers', ` + 'Path Length restriction', 'Proof Of Presence', 'Require a merge strategy', ` + 'Required reviewers', 'Reserved names restriction', 'Secrets scanning restriction', ` + 'Secrets scanning restriction', 'Status', 'Work item linking') + } + + It 'Should support wildcards' { + $result = Get-TfsGitPolicyType -Project $tfsProject -PolicyType 'B*' + $result | Should -BeOfType 'Microsoft.TeamFoundation.Policy.WebApi.PolicyType' + $result.DisplayName | Should -Be @('Build') + } + + It 'Should return a single policy type' { + $result = Get-TfsGitPolicyType -Project $tfsProject -PolicyType 'Build' + $result | Should -BeOfType 'Microsoft.TeamFoundation.Policy.WebApi.PolicyType' + $result.DisplayName | Should -Be 'Build' + } + } +} From 51813d64ba1f05dc12a1d7dd897d3ba706aa5556 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Wed, 7 Aug 2024 13:28:28 -0300 Subject: [PATCH 53/90] Update release notes --- Docs/ReleaseNotes/2.9.0.md | 18 ++++++++++++++++-- RELEASENOTES.md | 7 ++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Docs/ReleaseNotes/2.9.0.md b/Docs/ReleaseNotes/2.9.0.md index 84b40586..381822bd 100644 --- a/Docs/ReleaseNotes/2.9.0.md +++ b/Docs/ReleaseNotes/2.9.0.md @@ -2,10 +2,24 @@ ## Version 2.9.0 (_03/Aug/2024_) -This release adds some new process-related cmdlets. +This release adds new process-related cmdlets along with some fixes, enhancements and a couple minor but potentially breaking changes. -### New cmdlets +## New cmdlets - `Get-TfsWorkItemField`: Gets information from one or more organization-wide work item fields - `New-TfsWorkItemField`: Creates a new organization-wide work item field - `Remove-TfsWorkItemField`: Removes an organization-wide work item field + +## Fixes + +- Fixed an issue with `Get-TfsArtifact` where it wasn't listing deleted packages. +- Fixed an issue with `Get-TfsArtifactFeed` where it would ignore the -Project argument and thus not filter feeds by project. +- Fixed an issue with `Get-TfsWorkItemTag` where it would fail when given a list of tags as input. +- Fixed an issue with `Get-TfsWorkItemType` where it would throw a "Parameter count mismatch" error when trying to get the work item type of a given work item. + +## Changes and enhancements + +- **`Get-TfsGitBranch`**: Added a new `-Compare` argument to to get the "Compare" (base) branch of a given repository. +- **`Get-TfsGitBranch` (_BREAKING_)**: `-Repository` parameter is now mandatory. This is to reduce the ambiguity of the command when omitting that argument. Scripts that rely on the old behavior will need to be updated. +- **`Get-TfsGitBranchPolicy` (_BREAKING_)**: Both `-Branch` and `-Repository` parameters are now mandatory. This is to reduce the ambiguity of the command when omitting those arguments. Scripts that rely on the old behavior will need to be updated. +- **`Connect-TfsTeamProjectCollection`, `Connect-TfsOrganization`**: Now it throws an error when trying to connect with invalid credentials instead of silently going into "anonymous mode". That help preventing subtle script errors where the lack of authorization would only be noticed later in the script, when trying to actually perform some command that required valid credentials. Now you get the warning that something is wrong as early in the script as possible. diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 8fb2694f..70c21894 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -19,9 +19,10 @@ This release adds new process-related cmdlets along with some fixes, enhancement ## Changes and enhancements -- **BREAKING**: In `Get-TfsGitBranch`, `-Repository` parameter is now mandatory. This is to reduce the ambiguity of the command when omitting that argument. Scripts that rely on the old behavior will need to be updated. -- **BREAKING**: In `Get-TfsGitBranchPolicy`, both `-Branch` and `-Repository` parameters are now mandatory. This is to reduce the ambiguity of the command when omitting those arguments. Scripts that rely on the old behavior will need to be updated. -- Now `Connect-TfsTeamProjectCollection` (and its counterpart `Connect-TfsOrganization`) throws an error when trying to connect with invalid credentials instead of silently going into "anonymous mode". That help preventing subtle script errors where the lack of authorization would only be noticed later in the script, when trying to actually perform some command that required valid credentials. Now you get the warning that something is wrong as early in the script as possible. +- **`Get-TfsGitBranch`**: Added a new `-Compare` argument to to get the "Compare" (base) branch of a given repository. +- **`Get-TfsGitBranch` (_BREAKING_)**: `-Repository` parameter is now mandatory. This is to reduce the ambiguity of the command when omitting that argument. Scripts that rely on the old behavior will need to be updated. +- **`Get-TfsGitBranchPolicy` (_BREAKING_)**: Both `-Branch` and `-Repository` parameters are now mandatory. This is to reduce the ambiguity of the command when omitting those arguments. Scripts that rely on the old behavior will need to be updated. +- **`Connect-TfsTeamProjectCollection`, `Connect-TfsOrganization`**: Now it throws an error when trying to connect with invalid credentials instead of silently going into "anonymous mode". That help preventing subtle script errors where the lack of authorization would only be noticed later in the script, when trying to actually perform some command that required valid credentials. Now you get the warning that something is wrong as early in the script as possible. ----------------------- From 0c054ed42f08bbc89c2b6bd8acf99f2477f2207d Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Wed, 7 Aug 2024 13:28:47 -0300 Subject: [PATCH 54/90] Add -Compare support --- .../Cmdlets/Git/Branch/GetGitBranch.cs | 57 ++++++++++--------- .../Git/Branch/GetTfsGitBranch.Tests.ps1 | 16 ++++++ 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs index 8135d67a..f5097022 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Branch/GetGitBranch.cs @@ -6,7 +6,7 @@ namespace TfsCmdlets.Cmdlets.Git.Branch /// /// Gets information from one or more branches in a remote Git repository. /// - [TfsCmdlet(CmdletScope.Project, NoAutoPipeline = true, DefaultParameterSetName = "Get by name", + [TfsCmdlet(CmdletScope.Project, NoAutoPipeline = true, DefaultParameterSetName = "Get by name", OutputType = typeof(GitBranchStats))] partial class GetGitBranch { @@ -27,14 +27,20 @@ partial class GetGitBranch public object Repository { get; set; } /// - /// Returns the default branch in the given repository. + /// Returns the "Default" branch in the given repository. /// [Parameter(Mandatory = true, ParameterSetName = "Get default")] public SwitchParameter Default { get; set; } + + /// + /// Returns the "Compare" branch in the given repository. + /// + [Parameter(Mandatory = true, ParameterSetName = "Get compare")] + public SwitchParameter Compare { get; set; } } - [CmdletController(typeof(GitBranchStats), Client=typeof(IGitHttpClient))] - partial class GetGitBranchController + [CmdletController(typeof(GitBranchStats), Client = typeof(IGitHttpClient))] + partial class GetGitBranchController { protected override IEnumerable Run() { @@ -48,36 +54,35 @@ protected override IEnumerable Run() string branchName = null; - foreach(var branch in Branch) + foreach (var branch in Branch) { switch (branch) { case GitBranchStats gbs: - { - yield return gbs; - continue; - } - case null: - case string s when Default: - { - if (repo.DefaultBranch == null) { - throw new Exception($"Repository {repo.Name} does not have a default branch set."); + yield return gbs; + continue; } + case { } when Default: + { + if (repo.DefaultBranch == null) + { + throw new Exception($"Repository {repo.Name} does not have a default branch set."); + } - branchName = repo.DefaultBranch.Substring("refs/heads/".Length); - break; - } + branchName = repo.DefaultBranch.Substring("refs/heads/".Length); + break; + } case string s when !string.IsNullOrEmpty(s): - { - branchName = s; - break; - } + { + branchName = s; + break; + } default: - { - Logger.LogError(new ArgumentException($"Invalid branch '{branch}'", "Branch")); - break; - } + { + Logger.LogError(new ArgumentException($"Invalid branch '{branch}'", "Branch")); + break; + } } IEnumerable result; @@ -86,7 +91,7 @@ protected override IEnumerable Run() { result = Client.GetBranchesAsync(repo.ProjectReference.Name, repo.Id) .GetResult($"Error retrieving branch(es) '{branch}' from repository '{repo.Name}'") - .Where(b => b.Name.IsLike(branchName)); + .Where(b => b.Name.IsLike(branchName) && (!Has_Compare || b.IsBaseVersion == Compare)); } catch (Exception ex) { diff --git a/PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 b/PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 index d7a42844..5719de78 100644 --- a/PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 +++ b/PS/_Tests/Git/Branch/GetTfsGitBranch.Tests.ps1 @@ -38,6 +38,22 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { } } + Context 'Get compare' { + # Get-TfsGitBranch + # -Compare + # [-Repository ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return the Compare branch' { + $result = Get-TfsGitBranch -Compare -Repository 'PartsUnlimited' -Project $tfsProject + $result | Should -BeOfType 'Microsoft.TeamFoundation.SourceControl.WebApi.GitBranchStats' + $result.Name | Should -Be 'master' + $result.IsBaseVersion | Should -Be $true + } + } + Context 'Type And Format' { It 'Should add Project and Repository properties' { $result = Get-TfsGitBranch 'master' -Repository 'PartsUnlimited' -Project $tfsProject From e439e1224061785e027352468d648ffc63ef38b5 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 8 Aug 2024 22:20:45 -0300 Subject: [PATCH 55/90] Add new analyzer --- .../Analyzers/ParameterMustHaveComments.cs | 51 +++++++++++++++++++ .../DiagnosticDescriptors.cs | 7 +++ .../TfsCmdlets.SourceGenerators/Extensions.cs | 24 +++++++++ 3 files changed, 82 insertions(+) create mode 100644 CSharp/TfsCmdlets.SourceGenerators/Analyzers/ParameterMustHaveComments.cs diff --git a/CSharp/TfsCmdlets.SourceGenerators/Analyzers/ParameterMustHaveComments.cs b/CSharp/TfsCmdlets.SourceGenerators/Analyzers/ParameterMustHaveComments.cs new file mode 100644 index 00000000..a2862e82 --- /dev/null +++ b/CSharp/TfsCmdlets.SourceGenerators/Analyzers/ParameterMustHaveComments.cs @@ -0,0 +1,51 @@ +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace TfsCmdlets.SourceGenerators.Analyzers +{ + /// + /// Analyzer for "Parameter must be property" + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class ParameterMustHaveComments: DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(DiagnosticDescriptors.ParameterMustHaveComments); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property); + } + + private static void AnalyzeProperty(SymbolAnalysisContext context) + { + if (context.Symbol is not IPropertySymbol prop) return; + + if ((prop.DeclaredAccessibility & Accessibility.Public) != Accessibility.Public) return; + + foreach (var dsr in prop.DeclaringSyntaxReferences) + { + if (dsr.GetSyntax() is not PropertyDeclarationSyntax pds) continue; + + var attr = pds.AttributeLists.FirstOrDefault(a => a.Attributes.Any(a2 => a2.Name.ToString() == "Parameter")); + + if (attr == null) continue; + + if (pds.HasXmlCommentTag("summary")) continue; + + var error = Diagnostic.Create(DiagnosticDescriptors.ParameterMustHaveComments, + pds.GetLocation(), + prop.Name, + DiagnosticSeverity.Error); + + context.ReportDiagnostic(error); + } + } + } +} diff --git a/CSharp/TfsCmdlets.SourceGenerators/DiagnosticDescriptors.cs b/CSharp/TfsCmdlets.SourceGenerators/DiagnosticDescriptors.cs index 286661a1..e6900b57 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/DiagnosticDescriptors.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/DiagnosticDescriptors.cs @@ -34,5 +34,12 @@ internal static class DiagnosticDescriptors "CodeGen", DiagnosticSeverity.Error, true); + + internal static DiagnosticDescriptor ParameterMustHaveComments { get; } = new DiagnosticDescriptor( + "TFS005", + "Parameter must have XML comments", "Parameter '{0}' must have at least XML comments to enable command-line help", + "CodeGen", + DiagnosticSeverity.Error, + true); } } diff --git a/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs b/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs index ac4195fc..3759d88f 100644 --- a/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs +++ b/CSharp/TfsCmdlets.SourceGenerators/Extensions.cs @@ -220,5 +220,29 @@ public static IEnumerable GetMembersRecursively(this ITypeSymbol type, type = type.BaseType; } } + + public static bool HasXmlCommentTag(this PropertyDeclarationSyntax propertyDeclaration, string tagName) + { + // Obtém as trivia que precedem a declaração da propriedade + var leadingTrivia = propertyDeclaration.GetLeadingTrivia(); + + // Encontra a trivia que contém o comentário de documentação XML + var xmlCommentTrivia = leadingTrivia + .Select(trivia => trivia.GetStructure()) + .OfType() + .FirstOrDefault(); + + if (xmlCommentTrivia == null) + { + return false; // Nenhum comentário XML encontrado + } + + // Verifica se há um elemento no comentário XML + var hasSummaryTag = xmlCommentTrivia.Content + .OfType() + .Any(element => element.StartTag.Name.LocalName.Text == tagName); + + return hasSummaryTag; + } } } From 68bfb45e84067cd2d6c3cce1d4217ae17054044b Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 9 Aug 2024 08:51:58 -0300 Subject: [PATCH 56/90] Add missing comments --- .../ExtensionManagement/GetExtension.cs | 6 ++ .../Cmdlets/Git/Commit/GetGitCommit.cs | 76 ++++++++++++------- .../TfsCmdlets/Cmdlets/Git/Item/GetGitItem.cs | 15 ++++ .../ReleaseManagement/GetReleaseDefinition.cs | 3 + .../Team/Board/SetTeamBoardCardRule.cs | 23 +++++- .../NewTeamProjectCollection.cs | 30 ++++++++ .../RemoveTeamProjectCollection.cs | 6 ++ .../WorkItem/History/GetWorkItemHistory.cs | 3 + .../WorkItem/Linking/AddWorkItemLink.cs | 19 +++++ .../Linking/GetWorkItemLinkEndType.cs | 4 + .../WorkItemType/ImportWorkItemType.cs | 6 ++ 11 files changed, 163 insertions(+), 28 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/GetExtension.cs b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/GetExtension.cs index 8765a72f..16a53238 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/GetExtension.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ExtensionManagement/GetExtension.cs @@ -31,9 +31,15 @@ partial class GetExtension [Parameter] public SwitchParameter IncludeDisabledExtensions { get; set; } + /// + /// Specifies whether to include errors in the result. + /// [Parameter] public SwitchParameter IncludeErrors { get; set; } + /// + /// Specifies whether to include installation issues in the result. + /// [Parameter] public SwitchParameter IncludeInstallationIssues { get; set; } } diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs index 77730961..0c2378e6 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs @@ -8,24 +8,26 @@ namespace TfsCmdlets.Cmdlets.Git.Commit [TfsCmdlet(CmdletScope.Project, NoAutoPipeline = true, DefaultParameterSetName = "Search commits", OutputType = typeof(GitCommitRef))] partial class GetGitCommit { - /* ParameterSetName: Get by commit SHA */ - + /// + /// Specifies the hash (SHA) of the commit to return. + /// [Parameter(ParameterSetName = "Get by commit SHA", Mandatory = true, Position = 0)] public object Commit { get; set; } - /* ParameterSetName: Get by tag */ - + /// + /// Specifies the tag name of the commit to return. + /// [Parameter(ParameterSetName = "Get by tag", Mandatory = true)] public string Tag { get; set; } - /* ParameterSetName: Get by branch */ - + /// + /// Specifies the branch name of the commit to return. + /// [Parameter(ParameterSetName = "Get by branch", Mandatory = true)] public string Branch { get; set; } - /* ParameterSetName: Search commits */ - /// + /// Limits the search to commits authored by this user. /// [Parameter(ParameterSetName = "Get by tag")] [Parameter(ParameterSetName = "Get by branch")] @@ -33,6 +35,7 @@ partial class GetGitCommit public string Author { get; set; } /// + /// Limits the search to commits committed by this user. /// [Parameter(ParameterSetName = "Get by tag")] [Parameter(ParameterSetName = "Get by branch")] @@ -40,6 +43,7 @@ partial class GetGitCommit public string Committer { get; set; } /// + /// /// [Parameter(ParameterSetName = "Get by tag")] [Parameter(ParameterSetName = "Get by branch")] @@ -47,6 +51,7 @@ partial class GetGitCommit public GitVersionDescriptor CompareVersion { get; set; } /// + /// Specifies the "commit-ish" to start the search from. /// [Parameter(ParameterSetName = "Get by tag")] [Parameter(ParameterSetName = "Get by branch")] @@ -54,6 +59,7 @@ partial class GetGitCommit public string FromCommit { get; set; } /// + /// Specifies the date and time of the commit to start the search from. /// [Parameter(ParameterSetName = "Get by tag")] [Parameter(ParameterSetName = "Get by branch")] @@ -61,6 +67,7 @@ partial class GetGitCommit public DateTime FromDate { get; set; } /// + /// Limits the search to commits that affect this path. /// [Parameter(ParameterSetName = "Get by tag")] [Parameter(ParameterSetName = "Get by branch")] @@ -68,6 +75,7 @@ partial class GetGitCommit public string ItemPath { get; set; } /// + /// Specifies the "commit-ish" to end the search at. /// [Parameter(ParameterSetName = "Get by tag")] [Parameter(ParameterSetName = "Get by branch")] @@ -75,62 +83,76 @@ partial class GetGitCommit public string ToCommit { get; set; } /// + /// Specifies the date and time of the commit to end the search at. /// [Parameter(ParameterSetName = "Get by tag")] [Parameter(ParameterSetName = "Get by branch")] [Parameter(ParameterSetName = "Search commits")] public DateTime ToDate { get; set; } - /* Shared Parameters */ - /// + /// Sorts the results from oldest to newest commit. /// - [Parameter()] - public SwitchParameter ExcludeDeletes { get; set; } + [Parameter(ParameterSetName = "Get by tag")] + [Parameter(ParameterSetName = "Get by branch")] + [Parameter(ParameterSetName = "Search commits")] + public SwitchParameter ShowOldestCommitsFirst { get; set; } /// + /// /// - [Parameter()] - public SwitchParameter IncludeLinks { get; set; } + [Parameter(ParameterSetName = "Get by tag")] + [Parameter(ParameterSetName = "Get by branch")] + [Parameter(ParameterSetName = "Search commits")] + public int Skip { get; set; } /// + /// Specifies the maximum number of commits to return. /// - [Parameter()] - public SwitchParameter IncludePushData { get; set; } + [Parameter(ParameterSetName = "Get by tag")] + [Parameter(ParameterSetName = "Get by branch")] + [Parameter(ParameterSetName = "Search commits")] + public int Top { get; set; } /// + /// Prevents deleted items from being included in the results. /// - [Parameter()] - public SwitchParameter IncludeUserImageUrl { get; set; } + [Parameter(ParameterSetName = "Get by tag")] + [Parameter(ParameterSetName = "Get by branch")] + [Parameter(ParameterSetName = "Search commits")] + public SwitchParameter ExcludeDeletes { get; set; } /// + /// Includes links to related resources (such as work items) in the results. /// [Parameter()] - public SwitchParameter ShowOldestCommitsFirst { get; set; } + public SwitchParameter IncludeLinks { get; set; } /// + /// Includes push data in the results. /// [Parameter()] - public int Skip { get; set; } + public SwitchParameter IncludePushData { get; set; } /// + /// Includes the user's image URL in the results. /// [Parameter()] - public int Top { get; set; } - + public SwitchParameter IncludeUserImageUrl { get; set; } + /// /// HELP_PARAM_GIT_REPOSITORY /// [Parameter(ValueFromPipeline = true)] public object Repository { get; set; } - } + } - [CmdletController(typeof(GitCommitRef), Client=typeof(IGitHttpClient))] + [CmdletController(typeof(GitCommitRef), Client = typeof(IGitHttpClient))] partial class GetGitCommitController { protected override IEnumerable Run() { - var repository = GetItem(new { Repository = Has_Repository? Repository: Project.Name }); + var repository = GetItem(new { Repository = Has_Repository ? Repository : Project.Name }); string commitSha; if (Has_Commit) @@ -165,7 +187,7 @@ protected override IEnumerable Run() } GitVersionDescriptor itemVersion; - + if (Has_Tag) { itemVersion = new GitVersionDescriptor() @@ -184,7 +206,7 @@ protected override IEnumerable Run() } else { - if(repository.DefaultBranch == null) + if (repository.DefaultBranch == null) { Logger.LogError($"Repository '{repository.Name}' has no default branch set. Please specify a different repository or branch."); yield break; diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Item/GetGitItem.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Item/GetGitItem.cs index 85cea232..ed6ad909 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Item/GetGitItem.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Item/GetGitItem.cs @@ -19,18 +19,33 @@ partial class GetGitItem [SupportsWildcards] public object Item { get; set; } = "/*"; + /// + /// Specifies the hash (SHA) representing the version of the item(s) to retrieve. + /// [Parameter(ParameterSetName = "Get by commit SHA")] public object Commit { get; set; } + /// + /// Specifies the tag representing the version of the item(s) to retrieve. + /// [Parameter(ParameterSetName = "Get by tag", Mandatory = true)] public string Tag { get; set; } + /// + /// Specifies the branch name representing the version of the item(s) to retrieve. + /// [Parameter(ParameterSetName = "Get by branch", Mandatory = true)] public string Branch { get; set; } + /// + /// Returns the content of the item(s) in addition to metadata. + /// [Parameter] public SwitchParameter IncludeContent { get; set; } + /// + /// Returns metadata about the item(s) + /// [Parameter] public SwitchParameter IncludeMetadata { get; set; } diff --git a/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinition.cs index 14cae846..db5a99dd 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinition.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Pipeline/ReleaseManagement/GetReleaseDefinition.cs @@ -10,6 +10,9 @@ namespace TfsCmdlets.Cmdlets.Pipeline.ReleaseManagement [TfsCmdlet(CmdletScope.Project, OutputType = typeof(ReleaseDefinition))] partial class GetReleaseDefinition { + /// + /// Specifies the release definition to get information from. + /// [Parameter(Position = 0)] [SupportsWildcards()] [Alias("Name")] diff --git a/CSharp/TfsCmdlets/Cmdlets/Team/Board/SetTeamBoardCardRule.cs b/CSharp/TfsCmdlets/Cmdlets/Team/Board/SetTeamBoardCardRule.cs index 93b47756..3dcf28f2 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Team/Board/SetTeamBoardCardRule.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Team/Board/SetTeamBoardCardRule.cs @@ -18,24 +18,45 @@ partial class SetTeamBoardCardRule [SupportsWildcards()] public object WebApiBoard { get; set; } + /// + /// Specifies the card rule settings as a single object. This way, all rules can be set at once. + /// [Parameter(ParameterSetName = "Bulk set")] public BoardCardRuleSettings Rules { get; set; } + /// + /// Specifies the name of the card style rule. + /// [Parameter(ParameterSetName = "Set individual rules")] public string CardStyleRuleName { get; set; } - + + /// + /// Specifies the filter for the card style rule. + /// [Parameter(ParameterSetName = "Set individual rules")] public string CardStyleRuleFilter { get; set; } + /// + /// Specifies the settings for the card style rule. + /// [Parameter(ParameterSetName = "Set individual rules")] public Hashtable CardStyleRuleSettings { get; set; } + /// + /// Specifies the name of the tag style rule. + /// [Parameter(ParameterSetName = "Set individual rules")] public string TagStyleRuleName { get; set; } + /// + /// Specifies the filter for the tag style rule. + /// [Parameter(ParameterSetName = "Set individual rules")] public string TagStyleRuleFilter { get; set; } + /// + /// Specifies the settings for the tag style rule. + /// [Parameter(ParameterSetName = "Set individual rules")] public Hashtable TagStyleRuleSettings { get; set; } } diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/NewTeamProjectCollection.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/NewTeamProjectCollection.cs index 45c5eee8..bf3daed0 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/NewTeamProjectCollection.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/NewTeamProjectCollection.cs @@ -17,36 +17,66 @@ namespace TfsCmdlets.Cmdlets.TeamProjectCollection [TfsCmdlet(CmdletScope.Server, DesktopOnly = true, SupportsShouldProcess = true, OutputType = typeof(Models.Connection))] partial class NewTeamProjectCollection { + /// + /// Specifies the name of the team project collection to create. + /// [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] [Alias("Name")] public object Collection { get; set; } + /// + /// Specifies the description of the team project collection. + /// [Parameter] public string Description { get; set; } + /// + /// Specifies the name of the SQL Server for hosting the team project collection database. + /// [Parameter(ParameterSetName = "Use database server", Mandatory = true)] public string DatabaseServer { get; set; } + /// + /// Specifies the name of the team project collection database. + /// [Parameter(ParameterSetName = "Use database server")] public string DatabaseName { get; set; } + /// + /// Specifies the connection string for the team project collection. + /// [Parameter(ParameterSetName = "Use connection string", Mandatory = true)] public string ConnectionString { get; set; } + /// + /// Specifies whether new the team project collection should become the default collection. + /// [Parameter] public SwitchParameter Default { get; set; } + /// + /// Specifies whether to use an existing database for the team project collection. + /// [Parameter] public SwitchParameter UseExistingDatabase { get; set; } + /// + /// Specifies the initial state of the team project collection. This allows you to create a collection in a stopped state when needed. + /// [Parameter] [ValidateSet("Started", "Stopped")] public string InitialState { get; set; } = "Started"; + /// + /// Specifies the frequency (in seconds) at which the command will check whether the creation is completed. + /// [Parameter] [ValidateRange(5, 60)] public int PollingInterval { get; set; } = 5; + /// + /// Specifies the timeout to wait for the operation to complete. When omitted, will wait indefinitely until the operation completes. + /// [Parameter] public TimeSpan Timeout { get; set; } = TimeSpan.MaxValue; } diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/RemoveTeamProjectCollection.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/RemoveTeamProjectCollection.cs index 6673a545..56247719 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/RemoveTeamProjectCollection.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProjectCollection/RemoveTeamProjectCollection.cs @@ -8,10 +8,16 @@ namespace TfsCmdlets.Cmdlets.TeamProjectCollection [TfsCmdlet(CmdletScope.Server, SupportsShouldProcess = true, DesktopOnly = true)] partial class RemoveTeamProjectCollection { + /// + /// Specifies the collection to be removed. Wildcards are supported. + /// [Parameter(Position = 0, ValueFromPipeline = true)] [SupportsWildcards] public object Collection { get; set; } + /// + /// Sets the timeout for the operation to complete. When omitted, will wait indefinitely until the operation completes. + /// [Parameter] public TimeSpan Timeout { get; set; } = TimeSpan.MaxValue; } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/History/GetWorkItemHistory.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/History/GetWorkItemHistory.cs index b91cd5e3..5d3c86d2 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/History/GetWorkItemHistory.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/History/GetWorkItemHistory.cs @@ -10,6 +10,9 @@ namespace TfsCmdlets.Cmdlets.WorkItem.History [TfsCmdlet(CmdletScope.Collection, NoAutoPipeline = true, OutputType = typeof(Models.WorkItemHistoryEntry))] partial class GetWorkItemHistory { + /// + /// The work item to retrieve the history for. + /// [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] [Alias("id")] [ValidateNotNull()] diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/AddWorkItemLink.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/AddWorkItemLink.cs index d0baf2ce..34c70e8d 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/AddWorkItemLink.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/AddWorkItemLink.cs @@ -11,16 +11,25 @@ namespace TfsCmdlets.Cmdlets.WorkItem.Linking [TfsCmdlet(CmdletScope.Collection)] partial class AddWorkItemLink { + /// + /// Specifies the work item to link from. + /// [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] [Alias("Id", "From")] [ValidateNotNull()] public object WorkItem { get; set; } + /// + /// Specifies the work item to link to. + /// [Parameter(Position = 1, Mandatory = true, ParameterSetName = "Link to work item")] [Alias("To")] [ValidateNotNull()] public object TargetWorkItem { get; set; } + /// + /// Specifies the type of the link to create. + /// [Parameter(Position = 2, Mandatory = true, ParameterSetName = "Link to work item")] [Alias("EndLinkType", "Type")] public WorkItemLinkType LinkType { get; set; } @@ -32,12 +41,22 @@ partial class AddWorkItemLink [Parameter] public SwitchParameter Passthru { get; set; } + /// + /// Bypasses any rule validation when saving the work item. Use it with caution, as this + /// may leave the work item in an invalid state. + /// [Parameter] public SwitchParameter BypassRules { get; set; } + /// + /// Do not fire any notifications for this change. Useful for bulk operations and automated processes. + /// [Parameter] public SwitchParameter SuppressNotifications { get; set; } + /// + /// Defines a comment to add to the link. + /// [Parameter] public string Comment { get; set; } } diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/GetWorkItemLinkEndType.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/GetWorkItemLinkEndType.cs index ec62b38a..3f53eb76 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/GetWorkItemLinkEndType.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Linking/GetWorkItemLinkEndType.cs @@ -10,6 +10,10 @@ namespace TfsCmdlets.Cmdlets.WorkItem.Linking [TfsCmdlet(CmdletScope.Collection)] partial class GetWorkItemLinkType { + /// + /// Specifies the name of the link type. Wildcards are supported. + /// When omitted, returns all link types. + /// [Parameter(Position = 0)] [SupportsWildcards] [Alias("Name", "EndLinkType", "Type", "Link")] diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/ImportWorkItemType.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/ImportWorkItemType.cs index 69346742..398f11cc 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/ImportWorkItemType.cs +++ b/CSharp/TfsCmdlets/Cmdlets/WorkItem/WorkItemType/ImportWorkItemType.cs @@ -9,10 +9,16 @@ namespace TfsCmdlets.Cmdlets.WorkItem.WorkItemType [TfsCmdlet(CmdletScope.Project, DesktopOnly = true)] partial class ImportWorkItemType { + /// + /// Specifies the XML definition of the work item type to import. + /// [Parameter(Position = 0, ValueFromPipeline = true, Mandatory = true, ParameterSetName = "Import from XML")] [ValidateNotNull] public string Xml { get; set; } + /// + /// Specifies the path to the file containing the XML definition of the work item type to import. + /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = "Import from file")] [ValidateNotNull] public string Path { get; set; } From e85cd3593249ac3f8949dc30fea0fe9d3e5e90f6 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Fri, 9 Aug 2024 08:52:08 -0300 Subject: [PATCH 57/90] Add new tests --- .../Git/Commit/GetTfsGitCommit.Tests.ps1 | 110 +++++++++++++++++- 1 file changed, 107 insertions(+), 3 deletions(-) diff --git a/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 b/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 index ba690284..5db71a74 100644 --- a/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 +++ b/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 @@ -2,7 +2,111 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" - } + $commitSha = '' + + Context 'Get by commit SHA' { + # Get-TfsGitCommit + # [-Commit] + # [-IncludeLinks] + # [-IncludePushData] + # [-IncludeUserImageUrl] + # [-Repository ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should return a commit object' { + $commit = Get-TfsGitCommit -Commit $commitSha -Repository 'PartsUnlimited' -Project $tfsProject + $commit | Should -BeOfType [Microsoft.TeamFoundation.SourceControl.WebApi.GitCommitRef] + } + + It 'Should include links' { + $commit = Get-TfsGitCommit -Commit $commitSha -Repository 'PartsUnlimited' -Project $tfsProject -IncludeLinks + $commit | Should -BeOfType [Microsoft.TeamFoundation.SourceControl.WebApi.GitCommitRef] + $commit.Links | Should -Not -BeNullOrEmpty + } + + It 'Should include push data' { + $commit = Get-TfsGitCommit -Commit $commitSha -Repository 'PartsUnlimited' -Project $tfsProject -IncludePushData + $commit | Should -BeOfType [Microsoft.TeamFoundation.SourceControl.WebApi.GitCommitRef] + $commit.Push | Should -Not -BeNullOrEmpty + } + + It 'Should include user image URL' { + $commit = Get-TfsGitCommit -Commit $commitSha -Repository 'PartsUnlimited' -Project $tfsProject -IncludeUserImageUrl + $commit | Should -BeOfType [Microsoft.TeamFoundation.SourceControl.WebApi.GitCommitRef] + $commit.Author.ImageUrl | Should -Not -BeNullOrEmpty + } + } + + Context 'Search commits' { + # Get-TfsGitCommit + # [-Author ] + # [-Committer ] + # [-CompareVersion ] + # [-FromCommit ] + # [-FromDate ] + # [-ItemPath ] + # [-ToCommit ] + # [-ToDate ] + # [-ExcludeDeletes] + # [-IncludeLinks] + # [-IncludePushData] + # [-IncludeUserImageUrl] + # [-ShowOldestCommitsFirst] + # [-Skip ] + # [-Top ] + # [-Repository ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + } + + Context 'Get by tag' { + # Get-TfsGitCommit + # -Tag + # [-Author ] + # [-Committer ] + # [-CompareVersion ] + # [-FromCommit ] + # [-FromDate ] + # [-ItemPath ] + # [-ToCommit ] + # [-ToDate ] + # [-ExcludeDeletes] + # [-IncludeLinks] + # [-IncludePushData] + # [-IncludeUserImageUrl] + # [-ShowOldestCommitsFirst] + # [-Skip ] + # [-Top ] + # [-Repository ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + } + + Context 'Get by branch' { + # Get-TfsGitCommit + # -Branch + # [-Author ] + # [-Committer ] + # [-CompareVersion ] + # [-FromCommit ] + # [-FromDate ] + # [-ItemPath ] + # [-ToCommit ] + # [-ToDate ] + # [-ExcludeDeletes] + # [-IncludeLinks] + # [-IncludePushData] + # [-IncludeUserImageUrl] + # [-ShowOldestCommitsFirst] + # [-Skip ] + # [-Top ] + # [-Repository ] + # [-Project ] + # [-Collection ] + # [-Server ] [] + } } \ No newline at end of file From 9bfc8a0276d7a61fba4f2162fba4af2a22d5c5b5 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 12 Aug 2024 19:59:40 -0300 Subject: [PATCH 58/90] Add star & funding information --- .github/FUNDING.yml | 1 + Assets/star.png | Bin 0 -> 1152 bytes README.md | 10 ++++++++++ 3 files changed, 11 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 Assets/star.png diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..17c949e4 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: igoravl diff --git a/Assets/star.png b/Assets/star.png new file mode 100644 index 0000000000000000000000000000000000000000..82a3ba57668fd4347db50fdfee9370ecc287c059 GIT binary patch literal 1152 zcmV-`1b_R9P)Gml!%ZK2000CZNklc#I^W(UL#iwBJntV#IJSuZxtzZ4WE2!?dxB-#Hd=e=YltYN zDDzpYSp7a#l~pWy@^Rv+Okeu-g`vRGTKDl15KxAJWtzCQ!P#?dY}~kss_IG}p8X&d z<^4$KG+JxKE2txY>vM68LeXS8gJ6Joi2MuQg|?_r$0ey8(7&XJQi@nCMptJCtJXA9 zJ7zT9-QAd`$>N0zs2((gKp=pKAb`*3(thS7dk-AN2oC1nX;Y~5n`HAIhN55uu6V5I zIr{QTs(_Rp1^g~6SFL8+jQe<_;T;g++4`q>{<(TWp%AYvX<+i*Q`z*{W`Y41*L7$; z{v9vAvXs{DFlSDkrYG6MM{D0@@6VkC11^?jq6`zi&qV=_>)_ZH($}|mEGZ5~1nqfD zns^60cJ5^M>{-m8JBLI%M=;orrfpkTxu%i!_7296t0R-sShmT9vn_-outM0wq_kE!_VXg<}q_(Es1olAj$dmZiyz+ z5{@M$97~Fb$blb^$%MP6%E80Oq=@Z1zmhxenISD_eitnw;aF0liHyW!z4F2GSEX*^ zbXoiPw-S$c$`f-Qk`J3sNVK<8nw$4XJT;$|jKs-w)c(fW!@tgGh)V45~ zB@Yt{4`j-%{fS1R43$rzs-!gTNq_5X22!)nB+AHn7`sjzRs=zz<}Z9k-h68*cHi9pPbdYpZL_DjnLh!?J0w|+ S*w{n>0000 Date: Mon, 12 Aug 2024 20:01:11 -0300 Subject: [PATCH 59/90] Adjust image size --- Assets/star.png | Bin 1152 -> 1408 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Assets/star.png b/Assets/star.png index 82a3ba57668fd4347db50fdfee9370ecc287c059..861c77822edfe7cada9bf1f7db6a44ec55b0b717 100644 GIT binary patch delta 1394 zcmV-&1&#WE34jYBiBL{Q4GJ0x0000DNk~Le0000d0000G2nGNE0F~TkAdw*^$odA{FczO=kuPd@cD_31Q05P-yWVSo<_-AEW? zu#pJ+_wUDao$c0bvu1)^mB9NTK|JPRfQla?>AHD063&T|AL6CcX`CD`bRQ%}mDuSs z=Q!Ef#q#B?I0=RS<0~`}jaY0D_<*iE%xKb%oj>QHhachenYT&CqaTuee*=dE2rE@z zR2q|;A(4nPDH-RcM2ur^b#nZ~3Fh51mtDI$Fi?#p<9HEo!rn-B94=Y#bOK&4$-#iEBxW*LkMV^J)2G6+{nior&C*(qU+Qt zUO#e_?c1NCp`n2V^XK7*5Oc#kv1tQcm6<&JwJ%d+ERA>+M)mIaIDn)pYk_{o=r1 z^xEM)TC?T>wX9g9f1SU7L;1{rHm!X?-&ns<8-DnV26CZB3MFMnipq`@MbCvn+Z&3I7UfqA! z?fUwUJJfe6t8A{Q>?n^+t{@9A{-s0b$Q6B-Eo)_RLjzZ?X7LP!p(BxqapA&6+E%V) za(x~7e4#29L8;{P@p(&l@#TX&vg9_les>E;`}0grPolPd3bjd(H(&cBzuEHwr~9+S zW0s&?CU)~wf9_lS1*SLDje7MxSTWHds4CLFzJ5@($UM5`2eh=V;NZc-%$n86*>mUc zN1ZJJ#^NeD-aK}aVUwa|c?-$n0B3pzaY4Y9D;ci#{heoay~L9BkF$E=ofPv0oFE88 zf^r$(xevJ2V_cORLo^zpufLzO=gzWi+fSH3|6W$Lf3M`1&ptE@aGD)p}@& zTAxF|-oYEa!>C;1&ZTQsZ`j!`BD5z z<=x|~e=$@_1v10eFkX^dr%u86Wn>JQ%R>;aBUNJRzM`;5s6ea7={6% z8@<(N1Q&)l44$=w&S8xq2tz#2!;R&RV(^b07*qoM6N<$g1sWN A0ssI2 delta 1136 zcmV-$1dscG3xEkBiBL{Q4GJ0x0000DNk~Le0000Y0000E2nGNE07@y&kC7oEe-<$@ z!%ZK2000CZNkl^fn}Pww!ztRZEW1QiK^;K9-jRm73KX%=QLVt#4D&Hfa`N{e~d!WWIBUj zfOv@f3*Lpcs8GixsT|P1q=-_ASS&_YX9ug+G*UZeG~M0Zn5N0%g$t-2G=xAPfQTS~ z&*##9<|KO$9K{F@=H6*jsPvm;^B#txU<9sstmrxV@=L0KlpY2AE-P29X4;JVc%$JR z5aHSSr+NOldP1QPuPtd{fAZZ^+4R|Ff&mxTb!a{Q9WTDJl-BMrXHK1_C)vYCYu{z> z&z%GVE|z7Y3=_Z4MFEcM;Mf+@*SB~qDGo*i?RiX^cn3Rn?qv4tSw)5%cE!4DRe;FEhgacnU@zdE( z#!Y{eS>tM0wq_kE!_VXg<}q_(Es1olAj$dmZiyz+5{@M$97~Fb$blb^$%MP6%E80O zq=@Z1zmhxenISD_eitnw;aF0liHyW!z4F2GSEX*^bXoiPw-S$c$`f-Qk`J3sNVK<8 znw$4XDGXA!)vZ3ja^h6U9O{7YFjlcLtAeu;1S2vcSH8te&n%1@p zbYAKqlg&~)W(>7AjpTAX1%S@y5x0Vu8dkIYlLmHu{2sfGw^3D5&ZyC&`Qzjvnwk%S z(;vTMlFg(UJmO|1PpYHLwbA;DORTHs1YnvrZEfxJhI`rif5lc#|MDw7pTqEB!|CX_ z$fal;$F|X0@#j_%|86X&_1+QhJIY}3KW$JzbmHijh+6ABMx%B}s0Mxx~Nn%Lzi z$y6Hcvncqpk4RC8V!$vA!jTB!a0F!-tY6n z7`sjzRs=zz<}Z9k-h68*cHi9pPbdYpZL_DjnLh!?J0w|+*w{n>0000 Date: Mon, 12 Aug 2024 20:02:21 -0300 Subject: [PATCH 60/90] Adjust Star button link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb172d86..791e1f31 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Import-Module TfsCmdlets ## How can I help? -If you enjoy using TfsCmdlets, please don't forget to ![](Assets/star.png) it! +If you enjoy using TfsCmdlets, please don't forget to [![Star](Assets/star.png)](stargazers) it! Should you encounter bugs or if you have feature requests, please feel free to [open an issue](issues) if one doesn't already exist. From 910ed109440b524599bf7ef2a9256b077fbaecea Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 12 Aug 2024 20:04:40 -0300 Subject: [PATCH 61/90] Make links absolute --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 791e1f31..be3e41f5 100644 --- a/README.md +++ b/README.md @@ -158,10 +158,10 @@ Import-Module TfsCmdlets ## How can I help? -If you enjoy using TfsCmdlets, please don't forget to [![Star](Assets/star.png)](stargazers) it! +If you enjoy using TfsCmdlets, please don't forget to [![Star](Assets/star.png)](https://github.com/igoravl/TfsCmdlets/stargazers) it! -Should you encounter bugs or if you have feature requests, please feel free to [open an issue](issues) if one doesn't already exist. +Should you encounter bugs or if you have feature requests, please feel free to [open an issue](https://github.com/igoravl/TfsCmdlets/issues) if one doesn't already exist. -[Pull requests](pulls) are also very welcome, since I can't always get around to fixing all bugs myself. This is a personal passion project, so my time is limited. +[Pull requests](https://github.com/igoravl/TfsCmdlets/pulls) are also very welcome, since I can't always get around to fixing all bugs myself. This is a personal passion project, so my time is limited. Another way to help out is to [sponsor me](https://github.com/sponsors/igoravl) on GitHub. From b48008089e87f8ae770a0934fc1a808edfb4a4f8 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 12 Aug 2024 20:05:28 -0300 Subject: [PATCH 62/90] Fix issues links --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index be3e41f5..771807d0 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ Import-Module TfsCmdlets If you enjoy using TfsCmdlets, please don't forget to [![Star](Assets/star.png)](https://github.com/igoravl/TfsCmdlets/stargazers) it! -Should you encounter bugs or if you have feature requests, please feel free to [open an issue](https://github.com/igoravl/TfsCmdlets/issues) if one doesn't already exist. +Should you encounter bugs or if you have feature requests, please feel free to [open an issue](https://github.com/igoravl/TfsCmdlets/issues/new) if one doesn't already [exist](https://github.com/igoravl/TfsCmdlets/issues). [Pull requests](https://github.com/igoravl/TfsCmdlets/pulls) are also very welcome, since I can't always get around to fixing all bugs myself. This is a personal passion project, so my time is limited. From 319a26d2756ac5bacc41b327952dc54c806eb82f Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 12 Aug 2024 22:36:04 -0300 Subject: [PATCH 63/90] Update README --- README.md | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 771807d0..b39f7099 100644 --- a/README.md +++ b/README.md @@ -146,22 +146,17 @@ Import-Module TfsCmdlets - Close all PowerShell windows where you were using TfsCmdlets (to free files in use); - Delete the `TfsCmdlets` folder from the `[Documents]\[Windows]PowerShell\Modules` folder. -## Contribution Guidelines - -- [Contributor Guide](CONTRIBUTING.md) -- [Code of Conduct](CODE_OF_CONDUCT.md) - -## Additional Information - -- [Online Documentation](https://tfscmdlets.dev/) -- [Release Notes](https://github.com/igoravl/TfsCmdlets/blob/master/RELEASENOTES.md) - ## How can I help? If you enjoy using TfsCmdlets, please don't forget to [![Star](Assets/star.png)](https://github.com/igoravl/TfsCmdlets/stargazers) it! Should you encounter bugs or if you have feature requests, please feel free to [open an issue](https://github.com/igoravl/TfsCmdlets/issues/new) if one doesn't already [exist](https://github.com/igoravl/TfsCmdlets/issues). -[Pull requests](https://github.com/igoravl/TfsCmdlets/pulls) are also very welcome, since I can't always get around to fixing all bugs myself. This is a personal passion project, so my time is limited. +[Pull requests](https://github.com/igoravl/TfsCmdlets/pulls) are also very welcome, since I can't always get around to fixing all bugs myself. This is a personal passion project, so my time is limited. Learn more about how to contribute in our [Contributor Guide](CONTRIBUTING.md). Finally, to ensure a safe and pleasant experience for all contributors it's recommended that you get familiar with the [Code of Conduct](CODE_OF_CONDUCT.md). Another way to help out is to [sponsor me](https://github.com/sponsors/igoravl) on GitHub. + +## Additional Information + +- [Online Documentation](https://tfscmdlets.dev/) +- [Release Notes](https://github.com/igoravl/TfsCmdlets/blob/master/RELEASENOTES.md) From 3db5a9bf15d8482dacea6ba83d6de59d41284e12 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 12 Aug 2024 22:39:18 -0300 Subject: [PATCH 64/90] Update GetGitCommit.cs to make Repository parameter mandatory --- .../Cmdlets/Git/Commit/GetGitCommit.cs | 2 +- .../Git/Commit/GetTfsGitCommit.Tests.ps1 | 31 +++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs index 0c2378e6..83a0e75f 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs @@ -143,7 +143,7 @@ partial class GetGitCommit /// /// HELP_PARAM_GIT_REPOSITORY /// - [Parameter(ValueFromPipeline = true)] + [Parameter(ValueFromPipeline = true, Mandatory = true)] public object Repository { get; set; } } diff --git a/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 b/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 index 5db71a74..4512ea4e 100644 --- a/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 +++ b/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 @@ -1,9 +1,11 @@ & "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" -Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - - $commitSha = '' +$repoName = 'PartsUnlimited' +$global:commitSha = 'd682d84dc3a35101d66455bfe1e33fd23cb4369c' +$commitComment = 'Merged PR 35: code correction' +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + Context 'Get by commit SHA' { # Get-TfsGitCommit # [-Commit] @@ -15,27 +17,30 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { # [-Collection ] # [-Server ] [] + iT 'Should throw on missing required parameters' { + { Get-TfsGitCommit -Commit $commitSha -Project $tfsProject } | Should -Throw # Missing Repository + { Get-TfsGitCommit -Commit $commitSha -Repository $repoName } | Should -Throw # Missing Project + } + It 'Should return a commit object' { - $commit = Get-TfsGitCommit -Commit $commitSha -Repository 'PartsUnlimited' -Project $tfsProject + $commit = Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject $commit | Should -BeOfType [Microsoft.TeamFoundation.SourceControl.WebApi.GitCommitRef] + $commit.Comment | Should -Be $commitComment } It 'Should include links' { - $commit = Get-TfsGitCommit -Commit $commitSha -Repository 'PartsUnlimited' -Project $tfsProject -IncludeLinks - $commit | Should -BeOfType [Microsoft.TeamFoundation.SourceControl.WebApi.GitCommitRef] - $commit.Links | Should -Not -BeNullOrEmpty + (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject).Links | Should -BeNullOrEmpty + (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject -IncludeLinks).Links | Should -Not -BeNullOrEmpty } It 'Should include push data' { - $commit = Get-TfsGitCommit -Commit $commitSha -Repository 'PartsUnlimited' -Project $tfsProject -IncludePushData - $commit | Should -BeOfType [Microsoft.TeamFoundation.SourceControl.WebApi.GitCommitRef] - $commit.Push | Should -Not -BeNullOrEmpty + (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject).Push | Should -BeNullOrEmpty + (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject -IncludePushData).Push | Should -Not -BeNullOrEmpty } It 'Should include user image URL' { - $commit = Get-TfsGitCommit -Commit $commitSha -Repository 'PartsUnlimited' -Project $tfsProject -IncludeUserImageUrl - $commit | Should -BeOfType [Microsoft.TeamFoundation.SourceControl.WebApi.GitCommitRef] - $commit.Author.ImageUrl | Should -Not -BeNullOrEmpty + (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject).Author.ImageUrl | Should -BeNullOrEmpty + (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject -IncludeUserImageUrl).Author.ImageUrl | Should -Not -BeNullOrEmpty } } From 67db652ef1258451308d5a91a656c5d4ef500290 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Mon, 12 Aug 2024 22:43:42 -0300 Subject: [PATCH 65/90] Update the Contributor Guide --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 798abbfd..7e135c68 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,6 +23,10 @@ Pull requests are the best way to propose changes to the codebase (we use [Githu 5. Make sure your code lints. 6. Issue that pull request! +## Is this your first pull request to a GitHub-hosted project? + +If so, then you might want to read this [beginner's guide to GitHub Pull Requests](https://github.blog/developer-skills/github/beginners-guide-to-github-creating-a-pull-request/). + ## Any contributions you make will be under the MIT Software License In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. From b5c5490f0f87c91c33d95f7231727dd59d3b638b Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 13 Aug 2024 01:49:52 -0300 Subject: [PATCH 66/90] Add IncludeWorkItems parameter --- .../Cmdlets/Git/Commit/GetGitCommit.cs | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs b/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs index 83a0e75f..748f301f 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Git/Commit/GetGitCommit.cs @@ -1,4 +1,6 @@ using Microsoft.TeamFoundation.SourceControl.WebApi; +using Microsoft.VisualStudio.Services.ServiceEndpoints.WebApi; +using Parameter = TfsCmdlets.Models.Parameter; namespace TfsCmdlets.Cmdlets.Git.Commit { @@ -123,21 +125,33 @@ partial class GetGitCommit public SwitchParameter ExcludeDeletes { get; set; } /// - /// Includes links to related resources (such as work items) in the results. + /// Includes links to related resources in the results. /// - [Parameter()] + [Parameter(ParameterSetName = "Get by tag")] + [Parameter(ParameterSetName = "Get by branch")] + [Parameter(ParameterSetName = "Search commits")] public SwitchParameter IncludeLinks { get; set; } /// - /// Includes push data in the results. + /// Includes links to related work items in the results. /// [Parameter()] + public SwitchParameter IncludeWorkItems { get; set; } + + /// + /// Includes push data in the results. + /// + [Parameter(ParameterSetName = "Get by tag")] + [Parameter(ParameterSetName = "Get by branch")] + [Parameter(ParameterSetName = "Search commits")] public SwitchParameter IncludePushData { get; set; } /// /// Includes the user's image URL in the results. /// - [Parameter()] + [Parameter(ParameterSetName = "Get by tag")] + [Parameter(ParameterSetName = "Get by branch")] + [Parameter(ParameterSetName = "Search commits")] public SwitchParameter IncludeUserImageUrl { get; set; } /// @@ -180,8 +194,20 @@ protected override IEnumerable Run() } } - yield return Client.GetCommitAsync(repository.ProjectReference.Id.ToString(), commitSha, repository.Id.ToString()) + var partialCommit = Client.GetCommitAsync(repository.ProjectReference.Id.ToString(), commitSha, repository.Id.ToString()) .GetResult($"Error getting commit '{commitSha}' in repository '{repository.Name}'"); + + if(IncludeWorkItems) + { + var commitWis = Data + .GetItems(new { Commit = Parameter.Missing, FromCommit = commitSha, ToCommit = commitSha, Repository, Project, IncludeWorkItems }) + .FirstOrDefault()? + .WorkItems; + + partialCommit.WorkItems = commitWis; + } + + yield return partialCommit; } yield break; } @@ -234,6 +260,7 @@ protected override IEnumerable Run() IncludePushData = IncludePushData, IncludeUserImageUrl = IncludeUserImageUrl, ShowOldestCommitsFirst = ShowOldestCommitsFirst, + IncludeWorkItems = IncludeWorkItems, ToCommitId = ToCommit, Skip = Skip, Top = Top == 0 ? null : Top, From 2b74eb52cf0be9b0bddd5373a6105c4534a7ae48 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 13 Aug 2024 01:50:05 -0300 Subject: [PATCH 67/90] Add support for Missing parameter --- CSharp/TfsCmdlets/Models/Parameter.cs | 22 +++++++++++++++++++ .../Services/Impl/ParameterManagerImpl.cs | 18 +++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 CSharp/TfsCmdlets/Models/Parameter.cs diff --git a/CSharp/TfsCmdlets/Models/Parameter.cs b/CSharp/TfsCmdlets/Models/Parameter.cs new file mode 100644 index 00000000..3ce418c7 --- /dev/null +++ b/CSharp/TfsCmdlets/Models/Parameter.cs @@ -0,0 +1,22 @@ +namespace TfsCmdlets.Models +{ + public class Parameter + { + public static Parameter Missing { get; } = new Parameter(null, null); + + public static bool IsMissing(Parameter parameter) + { + return parameter == Missing; + } + + public string Name { get; set; } + + public object Value { get; set; } + + private Parameter(string name, object value) + { + Name = name; + Value = value; + } + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Services/Impl/ParameterManagerImpl.cs b/CSharp/TfsCmdlets/Services/Impl/ParameterManagerImpl.cs index a200ff39..f14f78c6 100644 --- a/CSharp/TfsCmdlets/Services/Impl/ParameterManagerImpl.cs +++ b/CSharp/TfsCmdlets/Services/Impl/ParameterManagerImpl.cs @@ -1,5 +1,6 @@ using System.Management.Automation; using System.Reflection; +using TfsCmdlets.Models; using TfsCmdlets.Util; namespace TfsCmdlets.Services.Impl @@ -56,13 +57,14 @@ public T Get(string name, T defaultValue = default) var val = _parameterValues[name] switch { + Parameter p when Parameter.IsMissing(p) => null, PSObject obj => obj.BaseObject, IEnumerable objs => objs.Select(o => o.BaseObject).ToList(), SwitchParameter sw => sw.ToBool(), _ => _parameterValues[name] }; - return (val is T tVal)? tVal : defaultValue; + return (val is T tVal) ? tVal : defaultValue; } /// @@ -175,7 +177,19 @@ public void Remove(string name) => _parameterValues.Remove(name); public bool HasParameter(string parameter) - => _boundParameters.Contains(parameter, StringComparer.OrdinalIgnoreCase); + { + if (!_boundParameters.Contains(parameter, StringComparer.OrdinalIgnoreCase)) + { + return false; + } + + if(_parameterValues[parameter] is Parameter p) + { + return !Parameter.IsMissing(p); + } + + return true; + } public IEnumerable Keys => _parameterValues.Keys; From 6a5b4cece92b092e1cfff3a5169d727927a36146 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 13 Aug 2024 01:50:13 -0300 Subject: [PATCH 68/90] Update tests --- .../Git/Commit/GetTfsGitCommit.Tests.ps1 | 28 ++++++++--------- PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 | 6 ++-- .../History/GetTfsWorkItemHistory.Tests.ps1 | 2 +- .../Tagging/GetTfsWorkItemTag.Tests.ps1 | 30 +++++++++---------- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 b/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 index 4512ea4e..fcb4bf7d 100644 --- a/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 +++ b/PS/_Tests/Git/Commit/GetTfsGitCommit.Tests.ps1 @@ -1,23 +1,22 @@ & "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" -$repoName = 'PartsUnlimited' -$global:commitSha = 'd682d84dc3a35101d66455bfe1e33fd23cb4369c' -$commitComment = 'Merged PR 35: code correction' - Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + BeforeAll { + $repoName = 'PartsUnlimited' + $commitSha = 'd682d84dc3a35101d66455bfe1e33fd23cb4369c' + $commitComment = 'Merged PR 35: code correction' + } + Context 'Get by commit SHA' { # Get-TfsGitCommit # [-Commit] - # [-IncludeLinks] - # [-IncludePushData] - # [-IncludeUserImageUrl] # [-Repository ] # [-Project ] # [-Collection ] # [-Server ] [] - iT 'Should throw on missing required parameters' { + It 'Should throw on missing required parameters' { { Get-TfsGitCommit -Commit $commitSha -Project $tfsProject } | Should -Throw # Missing Repository { Get-TfsGitCommit -Commit $commitSha -Repository $repoName } | Should -Throw # Missing Project } @@ -29,18 +28,19 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { } It 'Should include links' { - (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject).Links | Should -BeNullOrEmpty - (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject -IncludeLinks).Links | Should -Not -BeNullOrEmpty + (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject).Links | Should -Not -BeNullOrEmpty } It 'Should include push data' { - (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject).Push | Should -BeNullOrEmpty - (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject -IncludePushData).Push | Should -Not -BeNullOrEmpty + (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject).Push | Should -Not -BeNullOrEmpty + } + + It 'Should include work items' { + (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject -IncludeWorkItems).WorkItems | Should -Not -BeNullOrEmpty } It 'Should include user image URL' { - (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject).Author.ImageUrl | Should -BeNullOrEmpty - (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject -IncludeUserImageUrl).Author.ImageUrl | Should -Not -BeNullOrEmpty + (Get-TfsGitCommit -Commit $commitSha -Repository $repoName -Project $tfsProject).Author.ImageUrl | Should -Not -BeNullOrEmpty } } diff --git a/PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 b/PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 index c425ef97..a9216f51 100644 --- a/PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 +++ b/PS/_Tests/WorkItem/GetTfsWorkItem.Tests.ps1 @@ -6,17 +6,17 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { It 'Should get by ID and revision' { (Get-TfsWorkItem -ID 150).Id | Should -Be 150 - (Get-TfsWorkItem -ID 150).Rev | Should -Be 6 + (Get-TfsWorkItem -ID 150).Rev | Should -Be 7 (Get-TfsWorkItem -ID 150 -Revision 2).Rev | Should -Be 2 } It 'Should get by Where' { (Get-TfsWorkItem -Where '[System.Id] = 150').Id | Should -Be 150 - (Get-TfsWorkItem -Where '[System.Id] = 150').Rev | Should -Be 6 + (Get-TfsWorkItem -Where '[System.Id] = 150').Rev | Should -Be 7 } It 'Should support ASOF when getting by ID' { - (Get-TfsWorkItem 150 -AsOf (Get-Date)).Rev | Should -Be 6 + (Get-TfsWorkItem 150 -AsOf (Get-Date)).Rev | Should -Be 7 (Get-TfsWorkItem 150 -AsOf (Get-Date '2022-01-21')).Rev | Should -Be 3 } diff --git a/PS/_Tests/WorkItem/History/GetTfsWorkItemHistory.Tests.ps1 b/PS/_Tests/WorkItem/History/GetTfsWorkItemHistory.Tests.ps1 index 8e5ef08b..2bbb971f 100644 --- a/PS/_Tests/WorkItem/History/GetTfsWorkItemHistory.Tests.ps1 +++ b/PS/_Tests/WorkItem/History/GetTfsWorkItemHistory.Tests.ps1 @@ -5,7 +5,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { It 'Should get all revisions' { - (Get-TfsWorkItemHistory 150).Count | Should -Be 6 + (Get-TfsWorkItemHistory 150).Count | Should -Be 7 } } diff --git a/PS/_Tests/WorkItem/Tagging/GetTfsWorkItemTag.Tests.ps1 b/PS/_Tests/WorkItem/Tagging/GetTfsWorkItemTag.Tests.ps1 index 2f7e80b0..f4dc461e 100644 --- a/PS/_Tests/WorkItem/Tagging/GetTfsWorkItemTag.Tests.ps1 +++ b/PS/_Tests/WorkItem/Tagging/GetTfsWorkItemTag.Tests.ps1 @@ -12,32 +12,32 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { It 'Should return all tags' { $tags = Get-TfsWorkItemTag -Project $tfsProject - $tags.Name | Sort-Object | Should -Be @('MyTag1', 'MyTag2') - } - - It 'Should return all tags including inactive' { - $tags = Get-TfsWorkItemTag -IncludeInactive -Project $tfsProject $tags.Name | Sort-Object | Should -Be @('MyTag1', 'MyTag2', 'MyTag3') } + # It 'Should return all tags including inactive' { + # $tags = Get-TfsWorkItemTag -IncludeInactive -Project $tfsProject + # $tags.Name | Sort-Object | Should -Be @('MyTag1', 'MyTag2', 'MyTag3') + # } + It 'Should return a tag' { $tag = Get-TfsWorkItemTag -Tag 'MyTag1' -Project $tfsProject $tag.Name | Should -Be 'MyTag1' } - It 'Should return a tag including inactive' { - $tag = Get-TfsWorkItemTag -Tag 'MyTag3' -IncludeInactive -Project $tfsProject - $tag.Name | Should -Be 'MyTag3' - } + # It 'Should return a tag including inactive' { + # $tag = Get-TfsWorkItemTag -Tag 'MyTag3' -IncludeInactive -Project $tfsProject + # $tag.Name | Should -Be 'MyTag3' + # } It 'Should return multiple tags from a list' { - $tags = Get-TfsWorkItemTag 'MyTag1', 'MyTag2' -Project $tfsProject - $tags.Name | Sort-Object | Should -Be @('MyTag1', 'MyTag2') - } - - It 'Should return multiple tags from a list including inactive' { - $tags = Get-TfsWorkItemTag 'MyTag1', 'MyTag3' -Project $tfsProject -IncludeInactive + $tags = Get-TfsWorkItemTag 'MyTag1', 'MyTag3' -Project $tfsProject $tags.Name | Sort-Object | Should -Be @('MyTag1', 'MyTag3') } + + # It 'Should return multiple tags from a list including inactive' { + # $tags = Get-TfsWorkItemTag 'MyTag1', 'MyTag3' -Project $tfsProject -IncludeInactive + # $tags.Name | Sort-Object | Should -Be @('MyTag1', 'MyTag3') + # } } } \ No newline at end of file From 23b4f459abc236aeba319144b9d52a438647314f Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 13 Aug 2024 01:55:31 -0300 Subject: [PATCH 69/90] Write test path to log warning --- PS/_Tests/Artifact/GetTfsArtifactFeedView.Tests.ps1 | 2 +- PS/_Tests/Artifact/GetTfsArtifactVersion.Tests.ps1 | 2 +- PS/_Tests/Git/Item/GetTfsGitItem.Tests.ps1 | 2 +- PS/_Tests/Identity/GetTfsIdentity.Tests.ps1 | 2 +- PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 | 2 +- PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 | 2 +- PS/_Tests/Identity/User/GetTfsUser.Tests.ps1 | 2 +- PS/_Tests/Organization/GetTfsOrganization.Tests.ps1 | 2 +- PS/_Tests/ServiceHook/GetTfsServiceHookConsumer.Tests.ps1 | 2 +- .../ServiceHook/GetTfsServiceHookNotificationHistory.Tests.ps1 | 2 +- PS/_Tests/ServiceHook/GetTfsServiceHookPublisher.Tests.ps1 | 2 +- PS/_Tests/ServiceHook/GetTfsServiceHookSubscription.Tests.ps1 | 2 +- PS/_Tests/Team/Backlog/GetTfsTeamBacklogLevel.Tests.ps1 | 2 +- PS/_Tests/Team/Board/GetTfsTeamBoardCardRule.Tests.ps1 | 2 +- .../TeamProjectCollection/GetTfsTeamProjectCollection.Tests.ps1 | 2 +- PS/_Tests/TestManagement/GetTfsTestPlan.Tests.ps1 | 2 +- PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 | 2 +- PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 | 2 +- PS/_Tests/WorkItem/Linking/GetTfsWorkItemLink.Tests.ps1 | 2 +- PS/_Tests/WorkItem/Linking/GetTfsWorkItemLinkEndType.Tests.ps1 | 2 +- .../WorkItem/Query/GetTfsWorkItemQueryItemController.Tests.ps1 | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/PS/_Tests/Artifact/GetTfsArtifactFeedView.Tests.ps1 b/PS/_Tests/Artifact/GetTfsArtifactFeedView.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/Artifact/GetTfsArtifactFeedView.Tests.ps1 +++ b/PS/_Tests/Artifact/GetTfsArtifactFeedView.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/Artifact/GetTfsArtifactVersion.Tests.ps1 b/PS/_Tests/Artifact/GetTfsArtifactVersion.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/Artifact/GetTfsArtifactVersion.Tests.ps1 +++ b/PS/_Tests/Artifact/GetTfsArtifactVersion.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/Git/Item/GetTfsGitItem.Tests.ps1 b/PS/_Tests/Git/Item/GetTfsGitItem.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/Git/Item/GetTfsGitItem.Tests.ps1 +++ b/PS/_Tests/Git/Item/GetTfsGitItem.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/Identity/GetTfsIdentity.Tests.ps1 b/PS/_Tests/Identity/GetTfsIdentity.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/Identity/GetTfsIdentity.Tests.ps1 +++ b/PS/_Tests/Identity/GetTfsIdentity.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 b/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 +++ b/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 b/PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 +++ b/PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/Identity/User/GetTfsUser.Tests.ps1 b/PS/_Tests/Identity/User/GetTfsUser.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/Identity/User/GetTfsUser.Tests.ps1 +++ b/PS/_Tests/Identity/User/GetTfsUser.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/Organization/GetTfsOrganization.Tests.ps1 b/PS/_Tests/Organization/GetTfsOrganization.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/Organization/GetTfsOrganization.Tests.ps1 +++ b/PS/_Tests/Organization/GetTfsOrganization.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/ServiceHook/GetTfsServiceHookConsumer.Tests.ps1 b/PS/_Tests/ServiceHook/GetTfsServiceHookConsumer.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/ServiceHook/GetTfsServiceHookConsumer.Tests.ps1 +++ b/PS/_Tests/ServiceHook/GetTfsServiceHookConsumer.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/ServiceHook/GetTfsServiceHookNotificationHistory.Tests.ps1 b/PS/_Tests/ServiceHook/GetTfsServiceHookNotificationHistory.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/ServiceHook/GetTfsServiceHookNotificationHistory.Tests.ps1 +++ b/PS/_Tests/ServiceHook/GetTfsServiceHookNotificationHistory.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/ServiceHook/GetTfsServiceHookPublisher.Tests.ps1 b/PS/_Tests/ServiceHook/GetTfsServiceHookPublisher.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/ServiceHook/GetTfsServiceHookPublisher.Tests.ps1 +++ b/PS/_Tests/ServiceHook/GetTfsServiceHookPublisher.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/ServiceHook/GetTfsServiceHookSubscription.Tests.ps1 b/PS/_Tests/ServiceHook/GetTfsServiceHookSubscription.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/ServiceHook/GetTfsServiceHookSubscription.Tests.ps1 +++ b/PS/_Tests/ServiceHook/GetTfsServiceHookSubscription.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/Team/Backlog/GetTfsTeamBacklogLevel.Tests.ps1 b/PS/_Tests/Team/Backlog/GetTfsTeamBacklogLevel.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/Team/Backlog/GetTfsTeamBacklogLevel.Tests.ps1 +++ b/PS/_Tests/Team/Backlog/GetTfsTeamBacklogLevel.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/Team/Board/GetTfsTeamBoardCardRule.Tests.ps1 b/PS/_Tests/Team/Board/GetTfsTeamBoardCardRule.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/Team/Board/GetTfsTeamBoardCardRule.Tests.ps1 +++ b/PS/_Tests/Team/Board/GetTfsTeamBoardCardRule.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/TeamProjectCollection/GetTfsTeamProjectCollection.Tests.ps1 b/PS/_Tests/TeamProjectCollection/GetTfsTeamProjectCollection.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/TeamProjectCollection/GetTfsTeamProjectCollection.Tests.ps1 +++ b/PS/_Tests/TeamProjectCollection/GetTfsTeamProjectCollection.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/TestManagement/GetTfsTestPlan.Tests.ps1 b/PS/_Tests/TestManagement/GetTfsTestPlan.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/TestManagement/GetTfsTestPlan.Tests.ps1 +++ b/PS/_Tests/TestManagement/GetTfsTestPlan.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 b/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 +++ b/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 b/PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 +++ b/PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLink.Tests.ps1 b/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLink.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLink.Tests.ps1 +++ b/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLink.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLinkEndType.Tests.ps1 b/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLinkEndType.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLinkEndType.Tests.ps1 +++ b/PS/_Tests/WorkItem/Linking/GetTfsWorkItemLinkEndType.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file diff --git a/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryItemController.Tests.ps1 b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryItemController.Tests.ps1 index ba690284..5aeb56f9 100644 --- a/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryItemController.Tests.ps1 +++ b/PS/_Tests/WorkItem/Query/GetTfsWorkItemQueryItemController.Tests.ps1 @@ -3,6 +3,6 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context 'Integration Tests' { - Write-Warning "${$}: Not implemented" + Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" } } \ No newline at end of file From 020d00ed97117593b29595d8ba4827f1e624a4bf Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 13 Aug 2024 12:12:25 -0300 Subject: [PATCH 70/90] Add new tests --- .../Identity/Group/GetTfsGroup.Tests.ps1 | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 b/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 index 5aeb56f9..59df1690 100644 --- a/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 +++ b/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 @@ -2,7 +2,32 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" + BeforeAll { + $serverGroups = @('[TEAM FOUNDATION]\Enterprise Invited Users', '[TEAM FOUNDATION]\Enterprise Service Accounts', '[TEAM FOUNDATION]\Team Foundation Proxy Service Accounts') + $collectionGroups = @('Project Collection Administrators', 'Project Collection Build Administrators', 'Project Collection Build Service Accounts', 'Project Collection Proxy Service Accounts', 'Project Collection Service Accounts', 'Project Collection Test Service Accounts', 'Project Collection Valid Users', 'Project-Scoped Users', 'Security Service Group') + $projectGroups = @('Build Administrators', 'Contributors', 'Endpoint Administrators', 'Endpoint Creators', 'Project Administrators', 'Project Valid Users', 'PUL', 'PUL-DB', 'Readers', 'Release Administrators', 'TestProject Team') + } + + Context '__AllParameterSets' { + # Get-TfsGroup + # [[-Group] ] + # [-Scope ] + # [-Recurse] + # [-Project ] + # [-Collection ] + # [-Server ] [] + + It 'Should get all server-level groups' { + Get-TfsGroup -Scope Server | Select-Object -ExpandProperty PrincipalName | Sort-Object | Should -Be $serverGroups + } + + It 'Should get all collection-level groups' { + Get-TfsGroup | Select-Object -ExpandProperty DisplayName | Sort-Object | Should -Be $collectionGroups + Get-TfsGroup -Scope Collection | Select-Object -ExpandProperty DisplayName | Sort-Object | Should -Be $collectionGroups + } + + It 'Should get all project-level groups' { + Get-TfsGroup -Scope Project -Project $tfsProject | Select-Object -ExpandProperty DisplayName | Sort-Object | Should -Be $projectGroups + } } -} \ No newline at end of file +} From 00418e397b31eeeaefc26cfc5b626b4327445ad6 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 13 Aug 2024 13:33:42 -0300 Subject: [PATCH 71/90] Remove obsolete files --- .../TfsCmdlets/Services/IDescriptorService.cs | 9 --------- .../Services/Impl/DescriptorServiceImpl.cs | 17 ----------------- 2 files changed, 26 deletions(-) delete mode 100644 CSharp/TfsCmdlets/Services/IDescriptorService.cs delete mode 100644 CSharp/TfsCmdlets/Services/Impl/DescriptorServiceImpl.cs diff --git a/CSharp/TfsCmdlets/Services/IDescriptorService.cs b/CSharp/TfsCmdlets/Services/IDescriptorService.cs deleted file mode 100644 index 2793df10..00000000 --- a/CSharp/TfsCmdlets/Services/IDescriptorService.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.VisualStudio.Services.Graph.Client; - -namespace TfsCmdlets.Services -{ - public interface IDescriptorService - { - GraphDescriptorResult GetDescriptor(Guid storageKey); - } -} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Services/Impl/DescriptorServiceImpl.cs b/CSharp/TfsCmdlets/Services/Impl/DescriptorServiceImpl.cs deleted file mode 100644 index 266fe444..00000000 --- a/CSharp/TfsCmdlets/Services/Impl/DescriptorServiceImpl.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.VisualStudio.Services.Graph.Client; - -namespace TfsCmdlets.Services.Impl -{ - [Export(typeof(IDescriptorService)), Shared] - internal class DescriptorServiceImpl: IDescriptorService - { - [Import] - private IGraphHttpClient Client { get; set; } - - public GraphDescriptorResult GetDescriptor(Guid storageKey) - { - return Client.GetDescriptorAsync(storageKey) - .GetResult("Error getting descriptor"); - } - } -} \ No newline at end of file From 51cc9feab07cf4aa472e90f6843fa9b379ae18ab Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 13 Aug 2024 13:34:23 -0300 Subject: [PATCH 72/90] Replace DescriptorService with GraphHttpClient --- CSharp/TfsCmdlets/Cmdlets/Identity/Group/GetGroup.cs | 5 +++-- CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/GetGroup.cs b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/GetGroup.cs index ca271da1..f8408751 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/GetGroup.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/GetGroup.cs @@ -35,7 +35,7 @@ partial class GetGroup partial class GetGroupController { [Import] - private IDescriptorService DescriptorService { get; set; } + private IGraphHttpClient GraphClient { get; set; } protected override IEnumerable Run() { @@ -108,7 +108,8 @@ protected override IEnumerable Run() case GroupScope.Project: { var tp = Data.GetProject(); - var descriptor = DescriptorService.GetDescriptor(tp.Id); + var descriptor = GraphClient.GetDescriptorAsync(tp.Id) + .GetResult($"Error getting descriptor for project '{tp.Name}'"); do { diff --git a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs index 5d6cd719..79db9f4b 100644 --- a/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs +++ b/CSharp/TfsCmdlets/Cmdlets/Identity/Group/NewGroup.cs @@ -33,7 +33,7 @@ partial class NewGroup partial class NewGroupController { [Import] - private IDescriptorService DescriptorService { get; set; } + private IGraphHttpClient GraphClient { get; set; } protected override IEnumerable Run() { @@ -66,7 +66,8 @@ protected override IEnumerable Run() case GroupScope.Project: { var tp = Data.GetProject(); - var descriptor = DescriptorService.GetDescriptor(tp.Id); + var descriptor = GraphClient.GetDescriptorAsync(tp.Id) + .GetResult($"Error getting descriptor for project '{tp.Name}'"); if(!PowerShell.ShouldProcess(tp, $"Create group {group}")) yield break; From 7fa5bb8b7645975bc7c3b84390abc70a014ae98e Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 13 Aug 2024 15:37:41 -0300 Subject: [PATCH 73/90] Add new tests --- .../Group/GetTfsGroupMember.Tests.ps1 | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 b/PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 index 5aeb56f9..981e4f7c 100644 --- a/PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 +++ b/PS/_Tests/Identity/Group/GetTfsGroupMember.Tests.ps1 @@ -2,7 +2,40 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" + BeforeAll { + $collectionName = (Get-TfsTeamProjectCollection -Current).Name + } + + Context '__AllParameterSets' { + # Get-TfsGroupMember + # [-Group] + # [[-Member] ] + # [-Recurse] + # [-Collection ] + # [-Server ] [] + + It 'Should get all members of a server-level group' { + $members = Get-TfsGroupMember -Group '[TEAM FOUNDATION]\Enterprise Service Accounts' + $members | Should -BeOfType [Microsoft.VisualStudio.Services.Identity.Identity] + $members.AccountName | Should -Be 'TeamFoundationService (TEAM FOUNDATION)' + } + + It 'Should get all members of a collection-level group' { + $members = Get-TfsGroupMember -Group "[$collectionName]\Project Collection Service Accounts" + $members | Should -BeOfType [Microsoft.VisualStudio.Services.Identity.Identity] + $members.AccountName | Should -Be 'Enterprise Service Accounts' + } + + It 'Should get all members of a project-level group' { + $members = Get-TfsGroupMember -Group "[$tfsProject]\Endpoint Administrators" + $members | Should -BeOfType [Microsoft.VisualStudio.Services.Identity.Identity] + $members.AccountName | Should -Be 'Project Administrators' + } + + It 'Should get all members of a group recursively' { + $members = Get-TfsGroupMember -Group "[$tfsProject]\Endpoint Administrators" -Recurse + $members | Should -BeOfType [Microsoft.VisualStudio.Services.Identity.Identity] + $members.AccountName | Sort-Object | Should -Be @('igor@tshooter.com.br', 'Project Administrators') + } } } \ No newline at end of file From f92d211873057e60fda653198e7f2214efbed500 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 13 Aug 2024 23:08:11 -0300 Subject: [PATCH 74/90] Add IsLikeGlob --- CSharp/TfsCmdlets/Extensions/StringExtensions.cs | 12 ++++++++++++ CSharp/TfsCmdlets/Models/ClassificationNode.cs | 2 +- CSharp/TfsCmdlets/TfsCmdlets.csproj | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CSharp/TfsCmdlets/Extensions/StringExtensions.cs b/CSharp/TfsCmdlets/Extensions/StringExtensions.cs index 81790974..72b91a67 100644 --- a/CSharp/TfsCmdlets/Extensions/StringExtensions.cs +++ b/CSharp/TfsCmdlets/Extensions/StringExtensions.cs @@ -1,10 +1,13 @@ using System.Management.Automation; +using DotNet.Globbing; using Newtonsoft.Json.Linq; namespace TfsCmdlets.Extensions { public static class StringExtensions { + private static readonly GlobOptions _defaultGlobOptions = new GlobOptions { Evaluation = { CaseInsensitive = true } }; + public static bool IsLike(this string input, string pattern) { if(string.IsNullOrEmpty(pattern)) return false; @@ -14,6 +17,15 @@ public static bool IsLike(this string input, string pattern) .IsMatch(input); } + public static bool IsLikeGlob(this string input, string pattern) + { + if(string.IsNullOrEmpty(pattern)) return false; + + var g = Glob.Parse(pattern, _defaultGlobOptions); + + return g.IsMatch(input); + } + public static bool IsWildcard(this string input) { return WildcardPattern.ContainsWildcardCharacters(input); diff --git a/CSharp/TfsCmdlets/Models/ClassificationNode.cs b/CSharp/TfsCmdlets/Models/ClassificationNode.cs index 4926d8cf..f72e8467 100644 --- a/CSharp/TfsCmdlets/Models/ClassificationNode.cs +++ b/CSharp/TfsCmdlets/Models/ClassificationNode.cs @@ -70,7 +70,7 @@ private IEnumerable GetNodesRecursively(ClassificationNode n foreach (var c in node.Children.Select(n => new ClassificationNode(n, ProjectName, _client))) { - if(c.Path.IsLike(pattern)) yield return c; + if(c.Path.IsLikeGlob(pattern)) yield return c; foreach (var n in GetNodesRecursively(c, pattern)) { diff --git a/CSharp/TfsCmdlets/TfsCmdlets.csproj b/CSharp/TfsCmdlets/TfsCmdlets.csproj index f5aef48d..41fc1d5f 100644 --- a/CSharp/TfsCmdlets/TfsCmdlets.csproj +++ b/CSharp/TfsCmdlets/TfsCmdlets.csproj @@ -36,6 +36,7 @@ + From 6b0d597af7546631c342b4995b3611320f79b03f Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 13 Aug 2024 23:28:43 -0300 Subject: [PATCH 75/90] Update tests --- .../Identity/Group/GetTfsGroup.Tests.ps1 | 2 +- .../AreasIterations/GetTfsArea.Tests.ps1 | 27 ++++++++++++++++--- .../AreasIterations/GetTfsIteration.Tests.ps1 | 9 +++++-- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 b/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 index 59df1690..2d5176b3 100644 --- a/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 +++ b/PS/_Tests/Identity/Group/GetTfsGroup.Tests.ps1 @@ -10,7 +10,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { Context '__AllParameterSets' { # Get-TfsGroup - # [[-Group] ] + # [[-Group] ] # Pipeline input # [-Scope ] # [-Recurse] # [-Project ] diff --git a/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 b/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 index 5aeb56f9..1f676b94 100644 --- a/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 +++ b/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 @@ -2,7 +2,28 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { - Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" + Context '__AllParameterSets' { + # Get-TfsArea + # [[-Node] ] + # [-Project ] # Pipeline input + # [-Collection ] + # [-Server ] [] + + It 'Should get all areas' { + $nodes = Get-TfsArea -Project $tfsProject + $nodes | Should -BeOfType [Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemClassificationNode] + $nodes | Select-Object -ExpandProperty RelativePath | Sort-Object ` + | Should -Be @('PUL', 'PUL-DB', 'PUL-DB\Migrations', 'PUL\App', 'PUL\Web', 'PUL\Web\Backend', 'PUL\Web\Frontend') + } + + It 'Should return a single level' { + Get-TfsArea 'PUL/*' -Project $tfsProject | Select-Object -ExpandProperty RelativePath | Sort-Object ` + | Should -Be @('PUL\App', 'PUL\Web') + } + + It 'Should return an entire branch' { + Get-TfsArea 'PUL\**' -Project $tfsProject | Select-Object -ExpandProperty RelativePath | Sort-Object ` + | Should -Be @('PUL\App', 'PUL\Web', 'PUL\Web\Backend', 'PUL\Web\Frontend') + } } -} \ No newline at end of file +} diff --git a/PS/_Tests/WorkItem/AreasIterations/GetTfsIteration.Tests.ps1 b/PS/_Tests/WorkItem/AreasIterations/GetTfsIteration.Tests.ps1 index 03bee780..e452d8af 100644 --- a/PS/_Tests/WorkItem/AreasIterations/GetTfsIteration.Tests.ps1 +++ b/PS/_Tests/WorkItem/AreasIterations/GetTfsIteration.Tests.ps1 @@ -2,7 +2,12 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - Context 'Integration Tests' { + Context '__AllParameterSets' { + # Get-TfsIteration + # [[-Node] ] + # [-Project ] # Pipeline input + # [-Collection ] + # [-Server ] [] It 'Should throw on parameterless invocation' { { Get-TfsIteration } | Should -Throw @@ -18,7 +23,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { It 'Should get by name pattern' { Get-TfsIteration '*1' -Project $tfsProject | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('Sprint 1') - Get-TfsIteration '*ele*' -Project $tfsProject | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('Release 2', 'Sprint 7') + Get-TfsIteration '*ele*' -Project $tfsProject | Select-Object -ExpandProperty Name | Sort-Object | Should -Be @('Release 2') } } From f48ba3895fdb9f07ecccfe13ed7931335ce6dacf Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Tue, 13 Aug 2024 23:39:26 -0300 Subject: [PATCH 76/90] Fix test --- PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 b/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 index 1f676b94..7f7bf43b 100644 --- a/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 +++ b/PS/_Tests/WorkItem/AreasIterations/GetTfsArea.Tests.ps1 @@ -13,7 +13,7 @@ Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { $nodes = Get-TfsArea -Project $tfsProject $nodes | Should -BeOfType [Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemClassificationNode] $nodes | Select-Object -ExpandProperty RelativePath | Sort-Object ` - | Should -Be @('PUL', 'PUL-DB', 'PUL-DB\Migrations', 'PUL\App', 'PUL\Web', 'PUL\Web\Backend', 'PUL\Web\Frontend') + | Should -Be (@('PUL', 'PUL-DB', 'PUL-DB\Migrations', 'PUL\App', 'PUL\Web', 'PUL\Web\Backend', 'PUL\Web\Frontend') | Sort-Object) } It 'Should return a single level' { From a9c62d497af7926de75f48af8426467b13351d4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:05:08 -0300 Subject: [PATCH 77/90] Bump actions/download-artifact from 3 to 4 (#219) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 9e556717ba3e1deebd387ebc597109c81820b2b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:05:58 -0300 Subject: [PATCH 78/90] Bump actions/checkout from 3 to 4 (#215) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 44c6cc6d3bb76896677cdd8e82b7c8afec7c6a86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:06:50 -0300 Subject: [PATCH 79/90] Bump actions/upload-artifact from 3 to 4 (#216) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Igor Abade From ec96411cfea0727ec78ca5b097b47fd8f5b43227 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:07:30 -0300 Subject: [PATCH 80/90] Bump nuget/setup-nuget from 1 to 2 (#217) Bumps [nuget/setup-nuget](https://github.com/nuget/setup-nuget) from 1 to 2. - [Release notes](https://github.com/nuget/setup-nuget/releases) - [Commits](https://github.com/nuget/setup-nuget/compare/v1...v2) --- updated-dependencies: - dependency-name: nuget/setup-nuget dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Igor Abade From 3d88aa7cc791d78e523118e412ead8c159f45a12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:09:07 -0300 Subject: [PATCH 81/90] Bump styfle/cancel-workflow-action from 0.10.0 to 0.12.1 (#218) Bumps [styfle/cancel-workflow-action](https://github.com/styfle/cancel-workflow-action) from 0.10.0 to 0.12.1. - [Release notes](https://github.com/styfle/cancel-workflow-action/releases) - [Commits](https://github.com/styfle/cancel-workflow-action/compare/0.10.0...0.12.1) --- updated-dependencies: - dependency-name: styfle/cancel-workflow-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Igor Abade From edd9e2cdb6a72d6bce4af5493526c89e12bf5278 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Wed, 14 Aug 2024 18:22:40 -0300 Subject: [PATCH 82/90] Update release notes --- Docs/ReleaseNotes/2.9.0.md | 23 +++++++++++++---------- RELEASENOTES.md | 23 +++++++++++++---------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/Docs/ReleaseNotes/2.9.0.md b/Docs/ReleaseNotes/2.9.0.md index 381822bd..9323e361 100644 --- a/Docs/ReleaseNotes/2.9.0.md +++ b/Docs/ReleaseNotes/2.9.0.md @@ -1,25 +1,28 @@ # TfsCmdlets Release Notes -## Version 2.9.0 (_03/Aug/2024_) +## Version 2.9.0 (14/Aug/2024_) This release adds new process-related cmdlets along with some fixes, enhancements and a couple minor but potentially breaking changes. ## New cmdlets -- `Get-TfsWorkItemField`: Gets information from one or more organization-wide work item fields -- `New-TfsWorkItemField`: Creates a new organization-wide work item field -- `Remove-TfsWorkItemField`: Removes an organization-wide work item field +- **`Get-TfsWorkItemField`**: Gets information from one or more organization-wide work item fields +- **`New-TfsWorkItemField`**: Creates a new organization-wide work item field +- **`Remove-TfsWorkItemField`**: Removes an organization-wide work item field ## Fixes -- Fixed an issue with `Get-TfsArtifact` where it wasn't listing deleted packages. -- Fixed an issue with `Get-TfsArtifactFeed` where it would ignore the -Project argument and thus not filter feeds by project. -- Fixed an issue with `Get-TfsWorkItemTag` where it would fail when given a list of tags as input. -- Fixed an issue with `Get-TfsWorkItemType` where it would throw a "Parameter count mismatch" error when trying to get the work item type of a given work item. +- **`Get-TfsArtifact`**: Fixed an issue where it wouldn't list deleted packages. +- **`Get-TfsArtifactFeed`**: Fixed an issue where it would ignore the -Project argument and thus not filter feeds by project. +- **`Get-TfsWorkItemTag`**: Fixed an issue where it would fail when given a list of tags as input. +- **`Get-TfsWorkItemType`**: Fixed an issue where it would throw a "Parameter count mismatch" error when trying to get the work item type of a given work item. ## Changes and enhancements - **`Get-TfsGitBranch`**: Added a new `-Compare` argument to to get the "Compare" (base) branch of a given repository. -- **`Get-TfsGitBranch` (_BREAKING_)**: `-Repository` parameter is now mandatory. This is to reduce the ambiguity of the command when omitting that argument. Scripts that rely on the old behavior will need to be updated. -- **`Get-TfsGitBranchPolicy` (_BREAKING_)**: Both `-Branch` and `-Repository` parameters are now mandatory. This is to reduce the ambiguity of the command when omitting those arguments. Scripts that rely on the old behavior will need to be updated. - **`Connect-TfsTeamProjectCollection`, `Connect-TfsOrganization`**: Now it throws an error when trying to connect with invalid credentials instead of silently going into "anonymous mode". That help preventing subtle script errors where the lack of authorization would only be noticed later in the script, when trying to actually perform some command that required valid credentials. Now you get the warning that something is wrong as early in the script as possible. + +### Breaking changes + +- **`Get-TfsGitBranch`**: `-Repository` parameter is now mandatory. This is to reduce the ambiguity of the command when omitting that argument. Scripts that rely on the old behavior may need to be updated. +- **`Get-TfsGitBranchPolicy`**: Both `-Branch` and `-Repository` parameters are now mandatory. This is to reduce the ambiguity of the command when omitting those arguments. Scripts that rely on the old behavior may need to be updated. diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 70c21894..aa108135 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,29 +1,32 @@ # TfsCmdlets Release Notes -## Version 2.9.0 (_03/Aug/2024_) +## Version 2.9.0 (14/Aug/2024_) This release adds new process-related cmdlets along with some fixes, enhancements and a couple minor but potentially breaking changes. ## New cmdlets -- `Get-TfsWorkItemField`: Gets information from one or more organization-wide work item fields -- `New-TfsWorkItemField`: Creates a new organization-wide work item field -- `Remove-TfsWorkItemField`: Removes an organization-wide work item field +- **`Get-TfsWorkItemField`**: Gets information from one or more organization-wide work item fields +- **`New-TfsWorkItemField`**: Creates a new organization-wide work item field +- **`Remove-TfsWorkItemField`**: Removes an organization-wide work item field ## Fixes -- Fixed an issue with `Get-TfsArtifact` where it wasn't listing deleted packages. -- Fixed an issue with `Get-TfsArtifactFeed` where it would ignore the -Project argument and thus not filter feeds by project. -- Fixed an issue with `Get-TfsWorkItemTag` where it would fail when given a list of tags as input. -- Fixed an issue with `Get-TfsWorkItemType` where it would throw a "Parameter count mismatch" error when trying to get the work item type of a given work item. +- **`Get-TfsArtifact`**: Fixed an issue where it wouldn't list deleted packages. +- **`Get-TfsArtifactFeed`**: Fixed an issue where it would ignore the -Project argument and thus not filter feeds by project. +- **`Get-TfsWorkItemTag`**: Fixed an issue where it would fail when given a list of tags as input. +- **`Get-TfsWorkItemType`**: Fixed an issue where it would throw a "Parameter count mismatch" error when trying to get the work item type of a given work item. ## Changes and enhancements - **`Get-TfsGitBranch`**: Added a new `-Compare` argument to to get the "Compare" (base) branch of a given repository. -- **`Get-TfsGitBranch` (_BREAKING_)**: `-Repository` parameter is now mandatory. This is to reduce the ambiguity of the command when omitting that argument. Scripts that rely on the old behavior will need to be updated. -- **`Get-TfsGitBranchPolicy` (_BREAKING_)**: Both `-Branch` and `-Repository` parameters are now mandatory. This is to reduce the ambiguity of the command when omitting those arguments. Scripts that rely on the old behavior will need to be updated. - **`Connect-TfsTeamProjectCollection`, `Connect-TfsOrganization`**: Now it throws an error when trying to connect with invalid credentials instead of silently going into "anonymous mode". That help preventing subtle script errors where the lack of authorization would only be noticed later in the script, when trying to actually perform some command that required valid credentials. Now you get the warning that something is wrong as early in the script as possible. +### Breaking changes + +- **`Get-TfsGitBranch`**: `-Repository` parameter is now mandatory. This is to reduce the ambiguity of the command when omitting that argument. Scripts that rely on the old behavior may need to be updated. +- **`Get-TfsGitBranchPolicy`**: Both `-Branch` and `-Repository` parameters are now mandatory. This is to reduce the ambiguity of the command when omitting those arguments. Scripts that rely on the old behavior may need to be updated. + ----------------------- ## Previous Versions From 96d1adbbd67725f8f2161d7f7117f435966a6a54 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 15 Aug 2024 00:50:06 -0300 Subject: [PATCH 83/90] Disambiguate imports --- .../Cmdlets/ProcessTemplate/GetProcessTemplate.cs | 6 +++--- CSharp/TfsCmdlets/Cmdlets/TeamProject/NewTeamProject.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/GetProcessTemplate.cs b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/GetProcessTemplate.cs index dbf8d5d7..38c92e16 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/GetProcessTemplate.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/GetProcessTemplate.cs @@ -47,14 +47,14 @@ protected override IEnumerable Run() } case Guid g: { - yield return TaskExtensions.GetResult(Client.GetProcessByIdAsync(g), $"Error getting process template '{process}'"); + yield return TaskExtensions.GetResult(Client.GetProcessByIdAsync(g), $"Error getting process template '{process}'"); yield break; } case null when Default: case { } when Default: { - foreach (var proc in TaskExtensions.GetResult>(Client.GetProcessesAsync(), $"Error getting process templates") + foreach (var proc in TaskExtensions.GetResult>(Client.GetProcessesAsync(), $"Error getting process templates") .Where(p => p.IsDefault)) { yield return proc; @@ -64,7 +64,7 @@ protected override IEnumerable Run() } case string s: { - foreach (var proc in TaskExtensions.GetResult>(Client.GetProcessesAsync(), $"Error getting process template '{process}'") + foreach (var proc in TaskExtensions.GetResult>(Client.GetProcessesAsync(), $"Error getting process template '{process}'") .Where(p => p.Name.IsLike(s))) { yield return proc; diff --git a/CSharp/TfsCmdlets/Cmdlets/TeamProject/NewTeamProject.cs b/CSharp/TfsCmdlets/Cmdlets/TeamProject/NewTeamProject.cs index c19c587d..f06f5af7 100644 --- a/CSharp/TfsCmdlets/Cmdlets/TeamProject/NewTeamProject.cs +++ b/CSharp/TfsCmdlets/Cmdlets/TeamProject/NewTeamProject.cs @@ -55,9 +55,9 @@ protected override IEnumerable Run() var template = ProcessTemplate switch { - Process p => p, - string s => Data.GetItem(new { Process = s }), - null => GetItem(new { Default = true }), + WebApiProcess p => p, + string s => Data.GetItem(new { Process = s }), + null => GetItem(new { Default = true }), _ => throw new ArgumentException($"Invalid or non-existent process template '{ProcessTemplate}'") }; From 0dfb94033a65f074b8623627bf4d73f0edd687df Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 15 Aug 2024 00:50:14 -0300 Subject: [PATCH 84/90] Rename cmdlets --- .../Field/GetProcessFieldDefinition.cs} | 43 +++-- .../Field/NewProcessFieldDefinition.cs | 154 ++++++++++++++++++ .../WorkItem/Field/NewWorkItemField.cs | 86 ---------- 3 files changed, 187 insertions(+), 96 deletions(-) rename CSharp/TfsCmdlets/Cmdlets/{WorkItem/Field/GetWorkItemField.cs => ProcessTemplate/Field/GetProcessFieldDefinition.cs} (62%) create mode 100644 CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/NewProcessFieldDefinition.cs delete mode 100644 CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/NewWorkItemField.cs diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/GetProcessFieldDefinition.cs similarity index 62% rename from CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs rename to CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/GetProcessFieldDefinition.cs index 8b5348f2..89823978 100644 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/GetWorkItemField.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/GetProcessFieldDefinition.cs @@ -2,16 +2,16 @@ using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; using TfsCmdlets.HttpClients; -namespace TfsCmdlets.Cmdlets.WorkItem.Field +namespace TfsCmdlets.Cmdlets.Process.Field { /// /// Gets information from one or more organization-wide work item fields. /// [TfsCmdlet(CmdletScope.Collection, OutputType = typeof(WorkItemField))] - partial class GetWorkItemField + partial class GetProcessFieldDefinition { /// - /// Specifies the name or the reference name of the field(s) to be returned. Wildcards are supported. + /// Specifies the name of the field(s) to be returned. Wildcards are supported. /// When omitted, all fields in the given organization are returned. /// [Parameter(Position = 0)] @@ -19,6 +19,12 @@ partial class GetWorkItemField [SupportsWildcards()] public object Field { get; set; } = "*"; + /// + /// Specifies the reference name of the field(s) to be returned. Wildcards are supported. + /// + [Parameter()] + public string ReferenceName { get; set; } + /// /// Limits the search to the specified project. /// @@ -41,16 +47,17 @@ partial class GetWorkItemField // Controller [CmdletController] - partial class GetWorkItemFieldController + partial class GetProcessFieldDefinitionController { [Import] private IWorkItemTrackingHttpClient Client { get; set; } - + protected override IEnumerable Run() { string tpName; - if(Has_Project) { + if (Has_Project) + { var tp = Data.GetProject(); tpName = tp.Name; } @@ -59,23 +66,39 @@ protected override IEnumerable Run() { switch (f) { - case string s when s.IsWildcard(): + case WorkItemField wif: + { + yield return wif; + yield break; + } + case string s when s.IsWildcard() && string.IsNullOrEmpty(ReferenceName): { var expand = GetFieldsExpand.None | (IncludeExtensionFields ? GetFieldsExpand.ExtensionFields : GetFieldsExpand.None) | (IncludeDeleted ? GetFieldsExpand.IncludeDeleted : GetFieldsExpand.None); yield return Client.GetFieldsAsync(expand) - .GetResult($"Error getting field '{s}'") - .Where(field => field.Name.IsLike(s) || field.ReferenceName.IsLike(s)); + .GetResult($"Error getting fields '{s}'") + .Where(field => field.Name.IsLike(s)); break; } - case string s when !string.IsNullOrEmpty(s): + case string s when !string.IsNullOrEmpty(s) && string.IsNullOrEmpty(ReferenceName): { yield return Client.GetFieldAsync(fieldNameOrRefName: s) .GetResult($"Error getting field '{s}'"); break; } + case { } when !string.IsNullOrEmpty(ReferenceName): + { + var expand = GetFieldsExpand.None | + (IncludeExtensionFields ? GetFieldsExpand.ExtensionFields : GetFieldsExpand.None) | + (IncludeDeleted ? GetFieldsExpand.IncludeDeleted : GetFieldsExpand.None); + + yield return Client.GetFieldsAsync(expand) + .GetResult($"Error getting field with reference name '{ReferenceName}'") + .Where(field => field.ReferenceName.IsLike(ReferenceName)); + break; + } default: { throw new ArgumentException($"Invalid or non-existent field '{f}'"); diff --git a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/NewProcessFieldDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/NewProcessFieldDefinition.cs new file mode 100644 index 00000000..8a547aaf --- /dev/null +++ b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/NewProcessFieldDefinition.cs @@ -0,0 +1,154 @@ +using Microsoft.TeamFoundation.WorkItemTracking.Process.WebApi.Models; +using Microsoft.TeamFoundation.WorkItemTracking.WebApi; +using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; +using ProcessFieldType = Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.FieldType; + +namespace TfsCmdlets.Cmdlets.Process.Field +{ + /// + /// Gets information from one or more process templates. + /// + [TfsCmdlet(CmdletScope.Collection, SupportsShouldProcess = true, OutputType = typeof(WorkItemField))] + partial class NewProcessFieldDefinition + { + /// + /// Specifies the name of the field. + /// + [Parameter(Position = 0, Mandatory = true)] + [Alias("Name")] + public string Field { get; set; } + + /// + /// Specifies the reference name of the field. It should contain only letters, numbers, dots and underscores. + /// + [Parameter(Position = 1, Mandatory = true)] + public string ReferenceName { get; set; } + + /// + /// Specifies the description of the field. + /// + [Parameter] + public string Description { get; set; } + + /// + /// Specifies the type of the field. + /// + [Parameter] + public ProcessFieldType Type { get; set; } = ProcessFieldType.String; + + /// + /// Specifies whether the field is read-only. + /// + [Parameter] + public bool ReadOnly { get; set; } + + /// + /// Specifies whether the field is sortable in server queries. + /// + [Parameter] + public bool CanSortBy { get; set; } + + /// + /// Specifies whether the field can be queried in the server. + /// + [Parameter] + public bool IsQueryable { get; set; } + + /// + /// Specifies whether the field is an identity field. + /// + [Parameter] + public SwitchParameter IsIdentity { get; set; } + + /// + /// Specifies the contents of the picklist. Supplying values to this parameter will automatically + /// turn the field into a picklist. + /// + [Parameter] + public object[] PicklistItems { get; set; } + + /// + /// Specifies whether the user can enter a custom value in the picklist, making it a list of suggested values instead of allowed values. + /// + [Parameter] + public SwitchParameter PicklistSuggested { get; set; } + } + + // Controller + + [CmdletController(Client = typeof(IWorkItemTrackingHttpClient))] + partial class NewProcessFieldDefinitionController + { + + [Import] + private IWorkItemTrackingProcessHttpClient ProcessClient { get; set; } + + protected override IEnumerable Run() + { + var isPicklist = (PicklistItems != null) || + Type == ProcessFieldType.PicklistString || + Type == ProcessFieldType.PicklistInteger || + Type == ProcessFieldType.PicklistDouble; + + var fieldType = Type switch + { + ProcessFieldType.String or ProcessFieldType.PicklistString => ProcessFieldType.String, + ProcessFieldType.Integer or ProcessFieldType.PicklistInteger => ProcessFieldType.Integer, + ProcessFieldType.Double or ProcessFieldType.PicklistDouble => ProcessFieldType.Double, + _ when isPicklist => throw new Exception("Picklist fields must be of type string, integer or double."), + _ => Type + }; + + PickList picklist = null; + + if (isPicklist) + { + if (!PowerShell.ShouldProcess(Collection, $"Create picklist for field {ReferenceName} with items [{string.Join(", ", PicklistItems.Select(i => i.ToString()))}]")) + { + yield break; + } + + if ((PicklistItems?.Length ?? 0) == 0) + { + throw new ArgumentException("Picklist fields must contain at least one item. Use the PicklistItems parameter to specify the items."); + } + + picklist = CreatePicklist(fieldType, PicklistItems, PicklistSuggested); + } + + if (!PowerShell.ShouldProcess(Collection, $"Create process field {ReferenceName} ('{Field}'), type {fieldType}")) + { + yield break; + } + + yield return Client.CreateFieldAsync(new WorkItemField + { + Name = Field, + ReferenceName = ReferenceName, + Description = Description, + Type = fieldType, + ReadOnly = ReadOnly, + CanSortBy = CanSortBy, + IsQueryable = IsQueryable, + IsIdentity = IsIdentity, + PicklistId = picklist?.Id + }).GetResult("Error creating field"); + } + + private PickList CreatePicklist(ProcessFieldType type, object[] picklistItems, bool picklistSuggested) + { + var fieldType = type.ToString().Substring(0, 1).ToUpper() + type.ToString().Substring(1).ToLower(); + + var picklist = ProcessClient.CreateListAsync(new PickList + { + Id = Guid.Empty, + Name = "picklist_" + Guid.NewGuid().ToString(), + Type = fieldType, + Items = PicklistItems.Select(i => i.ToString()).ToList(), + IsSuggested = PicklistSuggested + }).GetResult("Error creating picklist"); + + return picklist; + } + } +} \ No newline at end of file diff --git a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/NewWorkItemField.cs b/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/NewWorkItemField.cs deleted file mode 100644 index abbf22a0..00000000 --- a/CSharp/TfsCmdlets/Cmdlets/WorkItem/Field/NewWorkItemField.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Microsoft.TeamFoundation.WorkItemTracking.WebApi; -using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; - -namespace TfsCmdlets.Cmdlets.WorkItem.Field -{ - /// - /// Gets information from one or more process templates. - /// - [TfsCmdlet(CmdletScope.Collection, OutputType = typeof(WorkItemField))] - partial class NewWorkItemField - { - /// - /// Specifies the name of the field. - /// - [Parameter(Position = 0, Mandatory = true)] - [Alias("Name")] - public string Field { get; set; } - - /// - /// Specifies the reference name of the field. It should contain only letters, numbers, dots and underscores. - /// - [Parameter(Position = 1)] - [Alias("Name")] - public string ReferenceName { get; set; } - - /// - /// Specifies the description of the field. - /// - [Parameter] - public string Description { get; set; } - - /// - /// Specifies the type of the field. - /// - [Parameter] - public FieldType Type { get; set; } = FieldType.String; - - /// - /// Specifies whether the field is read-only. - /// - [Parameter] - public bool ReadOnly { get; set; } - - /// - /// Specifies whether the field is sortable in server queries. - /// - [Parameter] - public bool CanSortBy { get; set; } - - /// - /// Specifies whether the field can be queried in the server. - /// - [Parameter] - public bool IsQueryable { get; set; } - - /// - /// Specifies whether the field is an identity field. - /// - [Parameter] - public SwitchParameter IsIdentity { get; set; } - - /// - /// Specifies the contents of the picklist. Supplying values to this parameter will automatically - /// turn the field into a picklist. - /// - [Parameter] - public object[] PicklistItems { get; set; } - - /// - /// Specifies whether the user can enter a custom value in the picklist,making it a list of suggested values instead of allowed values. - /// - [Parameter] - public SwitchParameter PicklistSuggested { get; set; } - } - - // Controller - - [CmdletController] - partial class NewWorkItemFieldController - { - protected override IEnumerable Run() - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file From 41a16774eec3ba5be131836ceb94ca9edce79b11 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 15 Aug 2024 00:53:03 -0300 Subject: [PATCH 85/90] Rename fiile --- .../GetTfsProcessFieldDefinition.Tests.ps1 | 17 +++++++++++++++++ .../Field/GetTfsWorkItemField.Tests.ps1 | 8 -------- 2 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 PS/_Tests/ProcessTemplate/GetTfsProcessFieldDefinition.Tests.ps1 delete mode 100644 PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 diff --git a/PS/_Tests/ProcessTemplate/GetTfsProcessFieldDefinition.Tests.ps1 b/PS/_Tests/ProcessTemplate/GetTfsProcessFieldDefinition.Tests.ps1 new file mode 100644 index 00000000..370671fa --- /dev/null +++ b/PS/_Tests/ProcessTemplate/GetTfsProcessFieldDefinition.Tests.ps1 @@ -0,0 +1,17 @@ +& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" + +Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { + + Context '__AllParameterSets' { + # Get-TfsProcessFieldDefinition + # [[-Field] ] + # [-ReferenceName ] + # [-Project ] + # [-IncludeExtensionFields] + # [-IncludeDeleted] + # [-Collection ] # Pipeline input + # [-Server ] [] + + + } +} \ No newline at end of file diff --git a/PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 b/PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 deleted file mode 100644 index 5aeb56f9..00000000 --- a/PS/_Tests/WorkItem/Field/GetTfsWorkItemField.Tests.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -& "$(($PSScriptRoot -split '_Tests')[0])/_Tests/_TestSetup.ps1" - -Describe (($MyInvocation.MyCommand.Name -split '\.')[-3]) { - - Context 'Integration Tests' { - Write-Warning "$(Split-Path $PSCommandPath -Leaf): Test not implemented" - } -} \ No newline at end of file From 1159f2c2e51d9b1fec33e96b6ec542f4ffc20c14 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 15 Aug 2024 11:40:00 -0300 Subject: [PATCH 86/90] Update release notes --- Docs/ReleaseNotes/2.9.0.md | 8 ++++---- RELEASENOTES.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Docs/ReleaseNotes/2.9.0.md b/Docs/ReleaseNotes/2.9.0.md index 9323e361..fe0c0ff3 100644 --- a/Docs/ReleaseNotes/2.9.0.md +++ b/Docs/ReleaseNotes/2.9.0.md @@ -1,14 +1,14 @@ # TfsCmdlets Release Notes -## Version 2.9.0 (14/Aug/2024_) +## Version 2.9.0 (15/Aug/2024_) This release adds new process-related cmdlets along with some fixes, enhancements and a couple minor but potentially breaking changes. ## New cmdlets -- **`Get-TfsWorkItemField`**: Gets information from one or more organization-wide work item fields -- **`New-TfsWorkItemField`**: Creates a new organization-wide work item field -- **`Remove-TfsWorkItemField`**: Removes an organization-wide work item field +- **`Get-ProcessFieldDefinition`**: Gets information from one or more organization-wide work item fields +- **`New-ProcessFieldDefinition`**: Creates a new organization-wide work item field +- **`Remove-ProcessFieldDefinition`**: Removes an organization-wide work item field ## Fixes diff --git a/RELEASENOTES.md b/RELEASENOTES.md index aa108135..a67c56f8 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,14 +1,14 @@ # TfsCmdlets Release Notes -## Version 2.9.0 (14/Aug/2024_) +## Version 2.9.0 (15/Aug/2024_) This release adds new process-related cmdlets along with some fixes, enhancements and a couple minor but potentially breaking changes. ## New cmdlets -- **`Get-TfsWorkItemField`**: Gets information from one or more organization-wide work item fields -- **`New-TfsWorkItemField`**: Creates a new organization-wide work item field -- **`Remove-TfsWorkItemField`**: Removes an organization-wide work item field +- **`Get-ProcessFieldDefinition`**: Gets information from one or more organization-wide work item fields +- **`New-ProcessFieldDefinition`**: Creates a new organization-wide work item field +- **`Remove-ProcessFieldDefinition`**: Removes an organization-wide work item field ## Fixes From 882dcf3d52fb67edbf4cb13994f59d84b235e666 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 15 Aug 2024 12:51:12 -0300 Subject: [PATCH 87/90] Change ReferenceName to string[] --- .../Field/GetProcessFieldDefinition.cs | 91 ++++++++++--------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/GetProcessFieldDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/GetProcessFieldDefinition.cs index 89823978..0bed32fb 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/GetProcessFieldDefinition.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/GetProcessFieldDefinition.cs @@ -14,7 +14,7 @@ partial class GetProcessFieldDefinition /// Specifies the name of the field(s) to be returned. Wildcards are supported. /// When omitted, all fields in the given organization are returned. /// - [Parameter(Position = 0)] + [Parameter(Position = 0, ParameterSetName = "By Name")] [Alias("Name")] [SupportsWildcards()] public object Field { get; set; } = "*"; @@ -22,8 +22,8 @@ partial class GetProcessFieldDefinition /// /// Specifies the reference name of the field(s) to be returned. Wildcards are supported. /// - [Parameter()] - public string ReferenceName { get; set; } + [Parameter(ParameterSetName = "By Reference Name", Mandatory = true)] + public string[] ReferenceName { get; set; } /// /// Limits the search to the specified project. @@ -46,12 +46,9 @@ partial class GetProcessFieldDefinition // Controller - [CmdletController] + [CmdletController(Client = typeof(IWorkItemTrackingHttpClient))] partial class GetProcessFieldDefinitionController { - [Import] - private IWorkItemTrackingHttpClient Client { get; set; } - protected override IEnumerable Run() { string tpName; @@ -62,48 +59,58 @@ protected override IEnumerable Run() tpName = tp.Name; } - foreach (var f in Field) - { - switch (f) - { - case WorkItemField wif: - { - yield return wif; - yield break; - } - case string s when s.IsWildcard() && string.IsNullOrEmpty(ReferenceName): - { - var expand = GetFieldsExpand.None | - (IncludeExtensionFields ? GetFieldsExpand.ExtensionFields : GetFieldsExpand.None) | - (IncludeDeleted ? GetFieldsExpand.IncludeDeleted : GetFieldsExpand.None); + var expand = GetFieldsExpand.None | + (IncludeExtensionFields ? GetFieldsExpand.ExtensionFields : GetFieldsExpand.None) | + (IncludeDeleted ? GetFieldsExpand.IncludeDeleted : GetFieldsExpand.None); - yield return Client.GetFieldsAsync(expand) - .GetResult($"Error getting fields '{s}'") - .Where(field => field.Name.IsLike(s)); - break; - } - case string s when !string.IsNullOrEmpty(s) && string.IsNullOrEmpty(ReferenceName): + switch (ParameterSetName) + { + case "By Name": + { + foreach (var f in Field) { - yield return Client.GetFieldAsync(fieldNameOrRefName: s) - .GetResult($"Error getting field '{s}'"); - break; + switch (f) + { + case WorkItemField wif: + { + yield return wif; + yield break; + } + case string s when s.IsWildcard(): + { + yield return Client.GetFieldsAsync(expand) + .GetResult($"Error getting fields '{s}'") + .Where(field => field.Name.IsLike(s)); + break; + } + case string s when !string.IsNullOrEmpty(s): + { + yield return Client.GetFieldAsync(fieldNameOrRefName: s) + .GetResult($"Error getting field '{s}'"); + break; + } + default: + { + throw new ArgumentException($"Invalid or non-existent field '{f}'"); + } + } } - case { } when !string.IsNullOrEmpty(ReferenceName): + break; + } + case "By Reference Name": + { + foreach (var refName in ReferenceName) { - var expand = GetFieldsExpand.None | - (IncludeExtensionFields ? GetFieldsExpand.ExtensionFields : GetFieldsExpand.None) | - (IncludeDeleted ? GetFieldsExpand.IncludeDeleted : GetFieldsExpand.None); - yield return Client.GetFieldsAsync(expand) .GetResult($"Error getting field with reference name '{ReferenceName}'") - .Where(field => field.ReferenceName.IsLike(ReferenceName)); - break; - } - default: - { - throw new ArgumentException($"Invalid or non-existent field '{f}'"); + .Where(field => field.ReferenceName.IsLike(refName)); } - } + break; + } + default: + { + throw new ArgumentException($"Unknown parameter set '{ParameterSetName}'"); + } } } } From 902f0908f825c3471a8781dfd201ab9859556bca Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 15 Aug 2024 12:51:21 -0300 Subject: [PATCH 88/90] Add new cmdlet --- .../Field/RemoveProcessFieldDefinition.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/RemoveProcessFieldDefinition.cs diff --git a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/RemoveProcessFieldDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/RemoveProcessFieldDefinition.cs new file mode 100644 index 00000000..8b9827a8 --- /dev/null +++ b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/RemoveProcessFieldDefinition.cs @@ -0,0 +1,46 @@ +using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; + +namespace TfsCmdlets.Cmdlets.Git +{ + /// + /// Deletes one or more Git fldsitories from a team project. + /// + [TfsCmdlet(CmdletScope.Collection, SupportsShouldProcess = true)] + partial class RemoveProcessFieldDefinition + { + /// + /// Specifies the name of the field(s) to be removed. Wildcards are supported. + /// + [Parameter(Position = 0, ParameterSetName = "By Name")] + [Alias("Name")] + public object Field { get; set; } = "*"; + + /// + /// Specifies the reference name of the field(s) to be removed. Wildcards are supported. + /// + [Parameter(ParameterSetName = "By Reference Name", Mandatory = true)] + public string[] ReferenceName { get; set; } + + /// + /// HELP_PARAM_FORCE_REMOVE + /// + [Parameter] + public SwitchParameter Force { get; set; } + } + + [CmdletController(typeof(WorkItemField), Client = typeof(IWorkItemTrackingHttpClient))] + partial class RemoveProcessFieldDefinitionController + { + protected override IEnumerable Run() + { + foreach (var fld in Items) + { + if (!PowerShell.ShouldProcess(Collection, $"Remove field {fld.ReferenceName} ('{fld.Name}')")) continue; + + Client.DeleteFieldAsync(fld.ReferenceName).Wait(); + } + + return null; + } + } +} \ No newline at end of file From 6d292ac0122ded364b9e0f053508002016971cb2 Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 15 Aug 2024 13:47:11 -0300 Subject: [PATCH 89/90] Fix CmdletController attribute --- .../ProcessTemplate/Field/GetProcessFieldDefinition.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/GetProcessFieldDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/GetProcessFieldDefinition.cs index 0bed32fb..fb92b0bc 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/GetProcessFieldDefinition.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/GetProcessFieldDefinition.cs @@ -1,6 +1,4 @@ -using Microsoft.TeamFoundation.WorkItemTracking.WebApi; using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; -using TfsCmdlets.HttpClients; namespace TfsCmdlets.Cmdlets.Process.Field { @@ -46,7 +44,7 @@ partial class GetProcessFieldDefinition // Controller - [CmdletController(Client = typeof(IWorkItemTrackingHttpClient))] + [CmdletController(typeof(WorkItemField), Client = typeof(IWorkItemTrackingHttpClient))] partial class GetProcessFieldDefinitionController { protected override IEnumerable Run() From 5d3a3ea49dc2f294e189e60941c4e7da65eb8f7a Mon Sep 17 00:00:00 2001 From: Igor Abade Date: Thu, 15 Aug 2024 13:47:29 -0300 Subject: [PATCH 90/90] Add ShouldContinue --- .../Field/RemoveProcessFieldDefinition.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/RemoveProcessFieldDefinition.cs b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/RemoveProcessFieldDefinition.cs index 8b9827a8..cbcc3b00 100644 --- a/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/RemoveProcessFieldDefinition.cs +++ b/CSharp/TfsCmdlets/Cmdlets/ProcessTemplate/Field/RemoveProcessFieldDefinition.cs @@ -1,11 +1,11 @@ using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; -namespace TfsCmdlets.Cmdlets.Git +namespace TfsCmdlets.Cmdlets.Process.Field { /// - /// Deletes one or more Git fldsitories from a team project. + /// Deletes one or more work item field definitions from a collection. /// - [TfsCmdlet(CmdletScope.Collection, SupportsShouldProcess = true)] + [TfsCmdlet(CmdletScope.Collection, SupportsShouldProcess = true, OutputType = typeof(WorkItemField))] partial class RemoveProcessFieldDefinition { /// @@ -37,6 +37,10 @@ protected override IEnumerable Run() { if (!PowerShell.ShouldProcess(Collection, $"Remove field {fld.ReferenceName} ('{fld.Name}')")) continue; + if (!Force && !PowerShell.ShouldContinue($"Are you sure you want to delete field {fld.ReferenceName} ('{fld.Name}')? " + + "You will lose all data stored in this field from all work items that are using it. " + + "Other undesired consequences may include breaking queries and/or dashboard widgets that reference it.")) continue; + Client.DeleteFieldAsync(fld.ReferenceName).Wait(); }