Skip to content

Commit

Permalink
Fix Set-TfsWorkItem (#175)
Browse files Browse the repository at this point in the history
* Fix #174

* Add handling for System.Tags

* Add handling for identity fields

* Add WorkItemPatchBuilder service

* Add WorkItemFieldAttribute

* Change return type for Team property

* Expose cmdlet

* Add null value handling for bound params

* Add IWorkItemPatchBuilder

* Change GetProject/GetTeam to return only current

* Update release notes

* Add AssignedTo field

* Add logic to ignore WorkItem argument

* Change logic to use WorkItemPatchBuilder

* Finish implementing New/SetWorkItem

* Update release notes
  • Loading branch information
igoravl authored Jul 21, 2022
1 parent 16b4a4f commit 86a036e
Show file tree
Hide file tree
Showing 28 changed files with 356 additions and 295 deletions.
17 changes: 16 additions & 1 deletion CSharp/TfsCmdlets.Common/Attributes.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace TfsCmdlets
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;

namespace TfsCmdlets
{
[AttributeUsage(System.AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class CmdletControllerAttribute: ExportAttribute
Expand Down Expand Up @@ -34,6 +36,19 @@ public ModelAttribute(Type dataType)
}
}

[AttributeUsage(System.AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class WorkItemFieldAttribute: Attribute
{
public string Name { get; }
public FieldType Type { get; }

public WorkItemFieldAttribute(string name, FieldType type)
{
Name = name;
Type = type;
}
}

// [AttributeUsage(System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
// public sealed class DesktopOnlyAttribute : Attribute
// {
Expand Down
2 changes: 1 addition & 1 deletion CSharp/TfsCmdlets.Common/Services/ICurrentConnections.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface ICurrentConnections

WebApiTeamProject Project { get; set; }

WebApiTeam Team { get; set; }
Models.Team Team { get; set; }

T Get<T>(string name);

Expand Down
6 changes: 3 additions & 3 deletions CSharp/TfsCmdlets.Common/Services/IDataManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ public interface IDataManager

bool TryGetCollection(out Models.Connection collection, object overridingParameters = null);

WebApiTeamProject GetProject(object overridingParameters = null, string contextValue = null);
WebApiTeamProject GetProject();

bool TryGetProject(out WebApiTeamProject project, object overridingParameters = null);

Models.Team GetTeam(object overridingParameters = null, string contextValue = null);
Models.Team GetTeam(bool includeSettings = false, bool includeMembers = false);

bool TryGetTeam(out WebApiTeam team, object overridingParameters = null);
bool TryGetTeam(out WebApiTeam returnTeam, bool includeSettings = false, bool includeMembers = false);

T GetClient<T>(object overridingParameters = null);

Expand Down
3 changes: 3 additions & 0 deletions CSharp/TfsCmdlets.Common/Services/IPowerShellService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using Microsoft.TeamFoundation.Core.WebApi;
using TfsCmdlets.Cmdlets;
using TfsCmdlets.Models;

namespace TfsCmdlets.Services
Expand Down Expand Up @@ -35,6 +36,8 @@ public interface IPowerShellService

string CurrentCommand {get;}

CmdletBase CurrentCmdlet { get; }

string CurrentCommandLine {get;}

bool ShouldProcess(string target, string action);
Expand Down
9 changes: 9 additions & 0 deletions CSharp/TfsCmdlets.Common/Services/IWorkItemPatchBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;

namespace TfsCmdlets.Services
{
public interface IWorkItemPatchBuilder
{
JsonPatchDocument GetJson(WebApiWorkItem wi);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class CurrentConnectionsImpl: ICurrentConnections

public WebApiTeamProject Project {get;set;}

public WebApiTeam Team {get;set;}
public Models.Team Team {get;set;}

public T Get<T>(string name)
{
Expand Down
19 changes: 12 additions & 7 deletions CSharp/TfsCmdlets.Common/Services/Impl/DataManagerImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ public bool TryGetCollection(out Models.Connection collection, object overriding
return collection != null;
}

public WebApiTeamProject GetProject(object overridingParameters = null, string contextValue = null)
=> GetItems<WebApiTeamProject>(overridingParameters).FirstOrDefault() ??
public WebApiTeamProject GetProject()
=> GetItems<WebApiTeamProject>().FirstOrDefault() ??
throw new ArgumentException("No team project information available. Either supply a valid -Project argument or use Connect-TfsTeamProject prior to invoking this cmdlet.");

public bool TryGetProject(out WebApiTeamProject project, object overridingParameters = null)
Expand All @@ -130,15 +130,20 @@ public bool TryGetProject(out WebApiTeamProject project, object overridingParame
return project != null;
}

public Models.Team GetTeam(object overridingParameters = null, string contextValue = null)
=> GetItems<Models.Team>(overridingParameters).FirstOrDefault() ??
public Models.Team GetTeam(bool includeSettings = false, bool includeMembers = false)
=> GetItems<Models.Team>(new {
Team = Parameters.Get<object>("Team", string.Empty),
IncludeSettings = includeSettings,
IncludeMembers = includeMembers,
Default = false
}).FirstOrDefault() ??
throw new ArgumentException("No team information available. Either supply a valid -Team argument or use Connect-TfsTeam prior to invoking this cmdlet.");

public bool TryGetTeam(out WebApiTeam team, object overridingParameters = null)
public bool TryGetTeam(out WebApiTeam team, bool includeSettings = false, bool includeMembers = false)
{
team = null;

try { team = GetItem<Models.Team>(overridingParameters); }
try { team = GetTeam(includeSettings, includeMembers); }
catch { }

return team != null;
Expand Down Expand Up @@ -331,4 +336,4 @@ public DataManagerImpl(
CurrentConnections = currentConnections;
}
}
}
}
11 changes: 4 additions & 7 deletions CSharp/TfsCmdlets.Common/Services/Impl/ParameterManagerImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void Initialize(Cmdlet cmdlet)
var value = prop.GetValue(cmdlet);
value = value is PSObject psObject ? psObject.BaseObject : value;

if (value != null) _parameterValues.Add(name, value);
if (value != null || _boundParameters.Contains(name)) _parameterValues.Add(name, value);
}

if (cmdlet is PSCmdlet psCmdlet)
Expand Down Expand Up @@ -62,7 +62,7 @@ public T Get<T>(string name, T defaultValue = default)
_ => _parameterValues[name]
};

return (T)val;
return (val is T tVal)? tVal : defaultValue;
}

/// <summary>
Expand Down Expand Up @@ -148,11 +148,8 @@ public void PopContext(string contextName = null)
var value = prop.GetValue(overridingParameters);
value = value is PSObject psObject ? psObject.BaseObject : value;

if (value != null)
{
overridden[name] = value;
if (!boundParams.Contains(name)) boundParams.Add(name);
}
overridden[name] = value;
if (!boundParams.Contains(name)) boundParams.Add(name);
}
break;
}
Expand Down
34 changes: 17 additions & 17 deletions CSharp/TfsCmdlets.Common/Services/Impl/PowerShellServiceImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class PowerShellServiceImpl : IPowerShellService
private static PropertyInfo _CurrentCommandProcessor;
private static PropertyInfo _Command;

private CmdletBase Cmdlet
public CmdletBase CurrentCmdlet
{
get
{
Expand All @@ -39,22 +39,22 @@ private CmdletBase Cmdlet
}
}

public string CurrentCommand => Cmdlet.DisplayName;
public string CurrentCommand => CurrentCmdlet.DisplayName;

public string CurrentCommandLine => Cmdlet.MyInvocation.Line.Trim();
public string CurrentCommandLine => CurrentCmdlet.MyInvocation.Line.Trim();

public string WindowTitle { get => Cmdlet.Host.UI.RawUI.WindowTitle; set => Cmdlet.Host.UI.RawUI.WindowTitle = value; }
public string WindowTitle { get => CurrentCmdlet.Host.UI.RawUI.WindowTitle; set => CurrentCmdlet.Host.UI.RawUI.WindowTitle = value; }

public PSModuleInfo Module => Cmdlet.MyInvocation.MyCommand.Module;
public PSModuleInfo Module => CurrentCmdlet.MyInvocation.MyCommand.Module;

public void WriteObject(object items, bool enumerateCollection = true)
=> Cmdlet.WriteObject(items, enumerateCollection);
=> CurrentCmdlet.WriteObject(items, enumerateCollection);

public void WriteVerbose(string message)
=> Cmdlet.WriteVerbose(message);
=> CurrentCmdlet.WriteVerbose(message);

public void WriteWarning(string message)
=> Cmdlet.WriteWarning(message);
=> CurrentCmdlet.WriteWarning(message);

public void WriteError(string message)
=> WriteError(new Exception(message));
Expand All @@ -63,19 +63,19 @@ public void WriteError(Exception ex)
=> WriteError(new ErrorRecord(ex, "", ErrorCategory.NotSpecified, null));

public void WriteError(ErrorRecord errorRecord)
=> Cmdlet.WriteError(errorRecord);
=> CurrentCmdlet.WriteError(errorRecord);

public IReadOnlyDictionary<string, object> GetBoundParameters()
=> new Dictionary<string, object>(Cmdlet.MyInvocation.BoundParameters);
=> new Dictionary<string, object>(CurrentCmdlet.MyInvocation.BoundParameters);

public object GetVariableValue(string name)
=> Cmdlet.GetVariableValue(name);
=> CurrentCmdlet.GetVariableValue(name);

// public void SetVariableValue(string name, object value)
// => ...;

public bool ShouldProcess(string target, string action)
=> Cmdlet.ShouldProcess(target, action);
=> CurrentCmdlet.ShouldProcess(target, action);

public bool ShouldProcess(Connection collection, string action)
=> ShouldProcess($"{(collection.IsHosted ? "Organization" : "Team Project Collection")} '{collection.DisplayName}'", action);
Expand All @@ -87,7 +87,7 @@ public bool ShouldProcess(WebApiTeam t, string action)
=> ShouldProcess($"Team '{t.Name}'", action);

public bool ShouldContinue(string query, string caption = null)
=> Cmdlet.ShouldContinue(query, caption);
=> CurrentCmdlet.ShouldContinue(query, caption);

/// <summary>
/// Executes a PowerShell script in the current session context
Expand All @@ -96,7 +96,7 @@ public bool ShouldContinue(string query, string caption = null)
/// <param name="arguments">Arguments passed to the script, represented as an array named <c>$args</c></param>
/// <returns>The output of the script, if any</returns>
public virtual object InvokeScript(string script, params object[] arguments)
=> Cmdlet.InvokeCommand.InvokeScript(script, arguments);
=> CurrentCmdlet.InvokeCommand.InvokeScript(script, arguments);

/// <summary>
/// Executes a PowerShell script in the current session context
Expand All @@ -115,17 +115,17 @@ public virtual object InvokeScript(string script, Dictionary<string, object> var
/// <typeparam name="T">The expected type of the objects outputted by the script</typeparam>
/// <returns>The output of the script, if any</returns>
public T InvokeScript<T>(string script, params object[] arguments)
=> (T)Cmdlet.InvokeCommand.InvokeScript(script, arguments)?.FirstOrDefault()?.BaseObject;
=> (T)CurrentCmdlet.InvokeCommand.InvokeScript(script, arguments)?.FirstOrDefault()?.BaseObject;

public object InvokeScript(string script, bool useNewScope, PipelineResultTypes writeToPipeline, IList input, params object[] args)
=> Cmdlet.InvokeCommand.InvokeScript(script, useNewScope, writeToPipeline, input, args);
=> CurrentCmdlet.InvokeCommand.InvokeScript(script, useNewScope, writeToPipeline, input, args);

/// <summary>
/// Gets the current directory in PowerShell
/// </summary>
public string GetCurrentDirectory()
{
return Cmdlet.SessionState.Path.CurrentFileSystemLocation.Path;
return CurrentCmdlet.SessionState.Path.CurrentFileSystemLocation.Path;
}

/// <summary>
Expand Down
Loading

0 comments on commit 86a036e

Please sign in to comment.