From de099bd3b50b8ea52b212b8d322626582c2648be Mon Sep 17 00:00:00 2001 From: Roy Ben-Shabat Date: Sun, 22 Apr 2018 13:35:22 +0300 Subject: Implemented new TangoIOC container & TangoMessenger. Got rid of MVVMLite libs ! Got rid of IShutdownRequestBlocker, IShutdownListener, IModuleRequestListener. Implemented IStudioViewModel & StudioViewModel. --- .../MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views') diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml.cs index df1f97f23..1e894f3f2 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml.cs @@ -1,5 +1,4 @@ -using Microsoft.Practices.ServiceLocation; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -27,6 +26,7 @@ using Tango.Core.Helpers; using Tango.SharedUI.Helpers; using Tango.Logging; using static Tango.SharedUI.Controls.NavigationControl; +using Tango.Core.DI; namespace Tango.MachineStudio.UI.Views { @@ -41,7 +41,7 @@ namespace Tango.MachineStudio.UI.Views { InitializeComponent(); - _loader = ServiceLocator.Current.GetInstance() as DefaultStudioModuleLoader; + _loader = TangoIOC.Default.GetInstance() as DefaultStudioModuleLoader; _loader.ModulesLoaded += Loader_ModulesLoaded; } @@ -51,7 +51,7 @@ namespace Tango.MachineStudio.UI.Views { LogManager.Default.Log("Loading modules views..."); - var item = ServiceLocator.Current.GetInstance().PushTaskItem("Loading Modules..."); + var item = TangoIOC.Default.GetInstance().PushTaskItem("Loading Modules..."); var modules = _loader.UserModules.ToList(); -- cgit v1.3.1 From fa5e1c45ab2da4e988cf256c0e5ec521288e537f Mon Sep 17 00:00:00 2001 From: Roy Ben-Shabat Date: Sun, 22 Apr 2018 15:58:38 +0300 Subject: Implemented some UI test automation for Job create/delete. --- Software/DB/Tango.mdf | Bin 75497472 -> 75497472 bytes Software/DB/Tango_log.ldf | Bin 1572864 -> 1572864 bytes .../Views/JobView.xaml | 13 +-- .../Views/MachineJobSelectionView.xaml | 9 +- .../Automation/Developer.cs | 22 +++++ .../Tango.MachineStudio.Common/Automation/UI.cs | 13 +++ .../Tango.MachineStudio.Common.csproj | 2 + .../Tango.MachineStudio.UI/Views/LoginView.xaml | 3 +- .../Tango.UnitTesting/DependencyInjection_TST.cs | 9 ++ .../Tango.UnitTesting/MachineStudio_TST.cs | 104 ++++++++++++++++++++- .../Tango.UnitTesting/Tango.UnitTesting.csproj | 4 + 11 files changed, 163 insertions(+), 16 deletions(-) create mode 100644 Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Automation/Developer.cs create mode 100644 Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Automation/UI.cs (limited to 'Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views') diff --git a/Software/DB/Tango.mdf b/Software/DB/Tango.mdf index 285e43436..3cacb7854 100644 Binary files a/Software/DB/Tango.mdf and b/Software/DB/Tango.mdf differ diff --git a/Software/DB/Tango_log.ldf b/Software/DB/Tango_log.ldf index 04cb5a2a6..b324efdb7 100644 Binary files a/Software/DB/Tango_log.ldf and b/Software/DB/Tango_log.ldf differ diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/Views/JobView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/Views/JobView.xaml index c575ef2a3..99b3a9712 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/Views/JobView.xaml +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/Views/JobView.xaml @@ -6,6 +6,7 @@ xmlns:global="clr-namespace:Tango.MachineStudio.Developer" xmlns:dragAndDrop="clr-namespace:Tango.DragAndDrop;assembly=Tango.DragAndDrop" xmlns:mahapps="http://metro.mahapps.com/winfx/xaml/controls" + xmlns:automation="clr-namespace:Tango.MachineStudio.Common.Automation;assembly=Tango.MachineStudio.Common" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:techViews="clr-namespace:Tango.MachineStudio.Technician.Views;assembly=Tango.MachineStudio.Technician" xmlns:colorPicker="clr-namespace:Tango;assembly=Tango.ColorPicker" @@ -233,7 +234,7 @@ Remove - diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml new file mode 100644 index 000000000..f2e3038b9 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + Report Issue + + + + + + + + + + + + Tags + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * + + + + + + + diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml.cs new file mode 100644 index 000000000..91a288d3e --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Tango.MachineStudio.UI.Views +{ + /// + /// Interaction logic for ReportIssueView.xaml + /// + public partial class ReportIssueView : UserControl + { + public ReportIssueView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs b/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs index dff85a011..dfbc893ee 100644 --- a/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs +++ b/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs @@ -13,7 +13,7 @@ namespace Tango.CodeGeneration /// /// Contains several code generation helper methods. /// - internal static class Helper + public static class Helper { /// /// Gets a code template by the code object type. @@ -55,6 +55,17 @@ namespace Tango.CodeGeneration return Engine.Razor.RunCompile(template, Guid.NewGuid().ToString(), model.GetType(), model).Replace("
", "").Replace("
", ""); } + /// + /// Parses the specified text. + /// + /// The text. + /// The model. + /// + public static String Parse(String text,object model) + { + return Engine.Razor.RunCompile(text, Guid.NewGuid().ToString(), model.GetType(), model); + } + /// /// Indents the c sharp code. /// diff --git a/Software/Visual_Studio/Tango.Core/ExtendedObject.cs b/Software/Visual_Studio/Tango.Core/ExtendedObject.cs index be6302a4b..8ed7c5ecd 100644 --- a/Software/Visual_Studio/Tango.Core/ExtendedObject.cs +++ b/Software/Visual_Studio/Tango.Core/ExtendedObject.cs @@ -1,4 +1,5 @@ -using System; +using Newtonsoft.Json; +using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; @@ -28,6 +29,7 @@ namespace Tango.Core /// /// Gets the default log manager. /// + [JsonIgnore] public LogManager LogManager { get { return LogManager.Default; } @@ -82,6 +84,7 @@ namespace Tango.Core /// Gets a value indicating whether we are currently in VS design mode. ///
[NotMapped] + [JsonIgnore] public bool DesignMode { get { return (DesignerProperties.GetIsInDesignMode(new DependencyObject())); } diff --git a/Software/Visual_Studio/Tango.Core/ExtensionMethods/BitmapExtensions.cs b/Software/Visual_Studio/Tango.Core/ExtensionMethods/BitmapExtensions.cs new file mode 100644 index 000000000..3cca605e9 --- /dev/null +++ b/Software/Visual_Studio/Tango.Core/ExtensionMethods/BitmapExtensions.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.Linq; +using System.Threading.Tasks; + + +public static class BitmapExtensions +{ + /// + /// Saves the specified bitmap as a JPEG file with the specified quality (0-100). + /// + /// The bitmap. + /// The file path. + /// The quality. + public static void SaveJpeg(this Bitmap bitmap, String filePath, int quality) + { + var encoder = ImageCodecInfo.GetImageEncoders().First(c => c.FormatID == ImageFormat.Jpeg.Guid); + var encParams = new EncoderParameters() { Param = new[] { new EncoderParameter(Encoder.Quality, quality) } }; + bitmap.Save(filePath, encoder, encParams); + } +} + diff --git a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj index e7172cc21..a9196237b 100644 --- a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj +++ b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj @@ -70,6 +70,7 @@ + diff --git a/Software/Visual_Studio/Tango.Logging/FileLogger.cs b/Software/Visual_Studio/Tango.Logging/FileLogger.cs index 215b1d9ac..4be7ab5c2 100644 --- a/Software/Visual_Studio/Tango.Logging/FileLogger.cs +++ b/Software/Visual_Studio/Tango.Logging/FileLogger.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -58,7 +59,14 @@ namespace Tango.Logging /// The output. public void OnLog(LogItemBase output) { - File.AppendAllText(LogFile, output.ToString() + Environment.NewLine); + try + { + File.AppendAllText(LogFile, output.ToString() + Environment.NewLine); + } + catch + { + Debug.WriteLine("Error Writing To Log File!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + } } private bool _isEnabled; diff --git a/Software/Visual_Studio/Tango.SharedUI/ExtensionMethods/FrameworkElementExtensions.cs b/Software/Visual_Studio/Tango.SharedUI/ExtensionMethods/FrameworkElementExtensions.cs new file mode 100644 index 000000000..422b078a4 --- /dev/null +++ b/Software/Visual_Studio/Tango.SharedUI/ExtensionMethods/FrameworkElementExtensions.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media.Imaging; +using Tango.SharedUI.Rendering; + +public static class FrameworkElementExtensions +{ + public static void RenderToFile(this FrameworkElement element, String filePath, ImageFormat format, Size renderSize, int quality) + { + RenderWindow renderer = new RenderWindow(element); + renderer.RenderToFileSynced(new Size(element.Width, element.Height), renderSize, filePath, format, quality, null); + } +} diff --git a/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml b/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml new file mode 100644 index 000000000..51a8b78ea --- /dev/null +++ b/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml.cs b/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml.cs new file mode 100644 index 000000000..638bf7267 --- /dev/null +++ b/Software/Visual_Studio/Tango.SharedUI/Rendering/RenderWindow.xaml.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace Tango.SharedUI.Rendering +{ + /// + /// Represents an on window chart renderer. + /// + public partial class RenderWindow : Window + { + private Action _fileCallback; + private Action _bitmapCallback; + private Size _renderSize; + private ImageFormat _format; + private int _quality; + private String _filePath; + private FrameworkElement _element; + private bool renderToFile; + + /// + /// Initializes a new instance of the class. + /// + /// The element. + public RenderWindow(FrameworkElement element) + { + InitializeComponent(); + element.HorizontalAlignment = System.Windows.HorizontalAlignment.Left; + element.VerticalAlignment = System.Windows.VerticalAlignment.Top; + _element = element; + grid.Children.Add(_element); + } + + /// + /// Occurs when the window is completely loaded. + /// + /// The sender. + /// The instance containing the event data. + private void RenderWindow_ContentRendered(object sender, EventArgs e) + { + RenderTargetBitmap bmp = CreateBitmap(grid, new Size(_renderSize.Width, _renderSize.Height)); + + if (renderToFile) + { + using (FileStream fs = new FileStream(_filePath, FileMode.Create)) + { + BitmapEncoder encoder = GetEncoderByImageFormat(_format, _quality); + + encoder.Frames.Add(BitmapFrame.Create(bmp)); + encoder.Save(fs); + } + + if (_fileCallback != null) + { + _fileCallback(_filePath); + } + } + else + { + if (_bitmapCallback != null) + { + _bitmapCallback(bmp); + } + } + + this.Close(); + } + + /// + /// Renders to file. + /// + /// Size of the design. + /// Size of the render. + /// The file path. + /// The format. + /// The ready callback. + public void RenderToFile(Size designSize, Size renderSize, String filePath, ImageFormat format, Action readyCallback = null) + { + renderToFile = true; + _renderSize = renderSize; + _filePath = filePath; + _format = format; + _fileCallback = readyCallback; + + double scaleX = renderSize.Width / designSize.Width; + double scaleY = scaleX; + + this.WindowStyle = WindowStyle.None; + this.AllowsTransparency = true; + this.Opacity = 0.001; + this.ShowInTaskbar = false; + this.ShowActivated = false; + this.Width = 1; + this.Height = 1; + _element.Width = designSize.Width; + _element.Height = designSize.Height; + + + grid.LayoutTransform = new ScaleTransform(scaleX, scaleY, 0, 0); + + this.ContentRendered += RenderWindow_ContentRendered; + + this.Show(); + } + + /// + /// Renders to file. + /// + /// Size of the design. + /// Size of the render. + /// The file path. + /// The format. + /// The ready callback. + public void RenderToFileSynced(Size designSize, Size renderSize, String filePath, ImageFormat format, int quality, Action readyCallback = null) + { + renderToFile = true; + _renderSize = renderSize; + _filePath = filePath; + _format = format; + _quality = quality; + _fileCallback = readyCallback; + + double scaleX = renderSize.Width / designSize.Width; + double scaleY = scaleX; + + this.WindowStyle = WindowStyle.None; + this.AllowsTransparency = true; + this.Opacity = 0.001; + this.ShowInTaskbar = false; + this.ShowActivated = false; + this.Width = 1; + this.Height = 1; + _element.Width = designSize.Width; + _element.Height = designSize.Height; + + + grid.LayoutTransform = new ScaleTransform(scaleX, scaleY, 0, 0); + + this.ContentRendered += RenderWindow_ContentRendered; + + this.ShowDialog(); + } + + /// + /// Renders to bitmap. + /// + /// Size of the design. + /// Size of the render. + /// The ready callback. + public void RenderToBitmap(Size designSize, Size renderSize, Action readyCallback = null) + { + _renderSize = renderSize; + _bitmapCallback = readyCallback; + + double scaleX = renderSize.Width / designSize.Width; + double scaleY = scaleX; + + this.WindowStyle = WindowStyle.None; + this.AllowsTransparency = true; + this.Opacity = 0.001; + this.ShowInTaskbar = false; + this.ShowActivated = false; + this.Width = 1; + this.Height = 1; + _element.Width = designSize.Width; + _element.Height = designSize.Height; + + + grid.LayoutTransform = new ScaleTransform(scaleX, scaleY, 0, 0); + + this.ContentRendered += RenderWindow_ContentRendered; + + this.Show(); + } + + /// + /// Creates the bitmap. + /// + /// The o visual. + /// Size of the o. + /// + private RenderTargetBitmap CreateBitmap(Visual oVisual, Size oSize) + { + int nWidth = (int)Math.Ceiling(oSize.Width); + int nHeight = (int)Math.Ceiling(oSize.Height); + + RenderTargetBitmap oTargetBitmap = new RenderTargetBitmap( + nWidth, + nHeight, + 96, + 96, + PixelFormats.Pbgra32 + ); + + DrawingVisual oDrawingVisual = new DrawingVisual(); + + using (DrawingContext oDrawingContext = oDrawingVisual.RenderOpen()) + { + VisualBrush oVisualBrush = new VisualBrush(oVisual) { Stretch = Stretch.Fill }; + + oDrawingContext.DrawRectangle( + oVisualBrush, + null, + new Rect( + new Point(), + new Size(nWidth, nHeight) + ) + ); + + oDrawingContext.Close(); + oTargetBitmap.Render(oDrawingVisual); + } + + return oTargetBitmap; + } + + /// + /// Gets the encoder by image format. + /// + /// The format. + /// + private static BitmapEncoder GetEncoderByImageFormat(ImageFormat format, int quality) + { + BitmapEncoder encoder = null; + + if (format == ImageFormat.Png) + { + encoder = new PngBitmapEncoder(); + } + else if (format == ImageFormat.Jpeg) + { + encoder = new JpegBitmapEncoder(); + (encoder as JpegBitmapEncoder).QualityLevel = quality; + } + else + { + encoder = new BmpBitmapEncoder(); + } + + return encoder; + } + } +} diff --git a/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj b/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj index eeae87a91..fa5d02617 100644 --- a/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj +++ b/Software/Visual_Studio/Tango.SharedUI/Tango.SharedUI.csproj @@ -108,6 +108,7 @@ ParameterizedEditor.xaml + @@ -125,6 +126,9 @@ Settings.settings True + + RenderWindow.xaml + @@ -160,6 +164,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + diff --git a/Software/Visual_Studio/Tango.SharedUI/ViewModel.cs b/Software/Visual_Studio/Tango.SharedUI/ViewModel.cs index 13b9206cd..4418ca437 100644 --- a/Software/Visual_Studio/Tango.SharedUI/ViewModel.cs +++ b/Software/Visual_Studio/Tango.SharedUI/ViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -32,6 +33,16 @@ namespace Tango.SharedUI set { _hasErrors = value; RaisePropertyChangedAuto(); } } + private ObservableCollection _validationErrors; + /// + /// Gets or sets the validation errors. + /// + public ObservableCollection ValidationErrors + { + get { return _validationErrors; } + private set { _validationErrors = value; RaisePropertyChangedAuto(); } + } + /// /// Occurs when the validation errors have changed for a property or for the entire entity. /// @@ -69,6 +80,13 @@ namespace Tango.SharedUI HasErrors = false; _currentErrors.Clear(); + if (ValidationErrors == null) + { + ValidationErrors = new ObservableCollection(); + } + + ValidationErrors.Clear(); + foreach (var prop in this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { foreach (var validation in prop.GetCustomAttributes()) @@ -77,6 +95,7 @@ namespace Tango.SharedUI { HasErrors = true; _currentErrors.Add(new KeyValuePair(prop.Name, validation.ErrorMessage)); + ValidationErrors.Add(validation.ErrorMessage); RaiseError(prop.Name); } } @@ -90,7 +109,7 @@ namespace Tango.SharedUI /// protected virtual void OnValidating() { - + } /// diff --git a/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs b/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs index 0f8ae5e82..51394f663 100644 --- a/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs +++ b/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs @@ -6,14 +6,40 @@ using System.Threading.Tasks; namespace Tango.TFS { + /// + /// Represents a VSTS (Visual Studio On-line) client implementation. + /// public interface ITeamFoundationServiceClient { + /// + /// Gets a team project by the specified name. + /// + /// The name. + /// Task GetProject(String name); + /// + /// Uploads a work item to the specified team project. + /// + /// The project. + /// The work item. + /// Task UploadWorkItem(Project project, WorkItem workItem); + /// + /// Gets a work item by the specified team project and work item id. + /// + /// The project. + /// The identifier. + /// Task GetWorkItem(Project project, int id); + /// + /// Deletes the specified work item. + /// + /// The project. + /// The identifier. + /// Task DeleteWorkItem(Project project, int id); } } diff --git a/Software/Visual_Studio/Tango.TFS/Priority.cs b/Software/Visual_Studio/Tango.TFS/Priority.cs index 88e2a0ca5..8a1e57b88 100644 --- a/Software/Visual_Studio/Tango.TFS/Priority.cs +++ b/Software/Visual_Studio/Tango.TFS/Priority.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -8,9 +9,13 @@ namespace Tango.TFS { public enum Priority { + [Description("Critical")] Priority1 = 1, + [Description("High")] Priority2 = 2, + [Description("Medium")] Priority3 = 3, + [Description("Low")] Priority4 = 4, } } diff --git a/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs b/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs index 6bcea336e..e6390260e 100644 --- a/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs +++ b/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs @@ -12,11 +12,18 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Core; namespace Tango.TFS { - public class TeamFoundationServiceClient : ITeamFoundationServiceClient + /// + /// Represents a VSTS (Visual Studio On-line) client implementation. + /// + /// + public class TeamFoundationServiceClient : ExtendedObject, ITeamFoundationServiceClient { + #region Extension Fields Constants + private class ExtensionFields { public const String FOUND_IN_BUILD = "Microsoft.VSTS.Build.FoundIn"; @@ -26,12 +33,35 @@ namespace Tango.TFS public const String SYSTEM_INFO = "Microsoft.VSTS.TCM.SystemInfo"; } + #endregion + + #region Properties + + /// + /// Gets the default team collection URL. + /// public String CollectionURL { get; private set; } + /// + /// Gets the personal token used to authenticate against the service. https://docs.microsoft.com/en-us/vsts/accounts/use-personal-access-tokens-to-authenticate?view=vsts + /// public String PersonalToken { get; private set; } + /// + /// Gets the VSTS user name (optional). + /// public String UserName { get; private set; } + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The collection URL. + /// Name of the user. + /// The personal token. public TeamFoundationServiceClient(String collectionURL, String userName, String personalToken) { CollectionURL = collectionURL; @@ -39,6 +69,15 @@ namespace Tango.TFS PersonalToken = personalToken; } + #endregion + + #region Public Methods + + /// + /// Gets a team project by the specified name. + /// + /// The name. + /// public Task GetProject(string name) { return Task.Factory.StartNew(() => @@ -141,15 +180,18 @@ namespace Tango.TFS }); } + /// + /// Uploads a work item to the specified team project. + /// + /// The project. + /// The work item. + /// public Task UploadWorkItem(Project project, WorkItem workItem) { return Task.Factory.StartNew(() => { var connection = CreateConnection(); - //var projCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(CollectionURL)); - //WorkItemStore s = new WorkItemStore(projCollection, WorkItemStoreFlags.BypassRules); - WorkItemTrackingHttpClient witClient = connection.GetClient(); var patchDocument = new JsonPatchDocument(); @@ -157,56 +199,59 @@ namespace Tango.TFS patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.Title), + Path = GetFieldNameForWrite(CoreField.Title), Value = workItem.Title, }); - patchDocument.Add(new JsonPatchOperation + if (!String.IsNullOrWhiteSpace(workItem.Description)) { - Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.Description), - Value = workItem.Description, - }); + patchDocument.Add(new JsonPatchOperation + { + Operation = Operation.Add, + Path = GetFieldNameForWrite(CoreField.Description), + Value = workItem.Description, + }); + } patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.IterationPath), + Path = GetFieldNameForWrite(CoreField.IterationPath), Value = workItem.Iteration.Path, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.AreaPath), + Path = GetFieldNameForWrite(CoreField.AreaPath), Value = workItem.Area.Path, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.AssignedTo), + Path = GetFieldNameForWrite(CoreField.AssignedTo), Value = workItem.AssignedTo.AssignName, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.CreatedBy), + Path = GetFieldNameForWrite(CoreField.CreatedBy), Value = workItem.CreatedBy.AssignName, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.ChangedBy), + Path = GetFieldNameForWrite(CoreField.ChangedBy), Value = workItem.ChangedBy.AssignName, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.AuthorizedAs), + Path = GetFieldNameForWrite(CoreField.AuthorizedAs), Value = workItem.AuthorizedAs.AssignName, }); @@ -250,7 +295,7 @@ namespace Tango.TFS patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.Tags), + Path = GetFieldNameForWrite(CoreField.Tags), Value = String.Join("; ", workItem.Tags.Select(x => x.Name)), }); } @@ -260,7 +305,7 @@ namespace Tango.TFS patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetExtensionFieldNameForPut(ExtensionFields.STEPS_TO_REP), + Path = GetExtensionFieldNameForWrite(ExtensionFields.STEPS_TO_REP), Value = workItem.StepsToReproduce, }); } @@ -270,7 +315,7 @@ namespace Tango.TFS patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetExtensionFieldNameForPut(ExtensionFields.FOUND_IN_BUILD), + Path = GetExtensionFieldNameForWrite(ExtensionFields.FOUND_IN_BUILD), Value = workItem.FoundInBuild, }); } @@ -278,28 +323,28 @@ namespace Tango.TFS patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetFieldNameForPut(CoreField.State), + Path = GetFieldNameForWrite(CoreField.State), Value = workItem.State.ToString(), }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetExtensionFieldNameForPut(ExtensionFields.SEVERITY), + Path = GetExtensionFieldNameForWrite(ExtensionFields.SEVERITY), Value = workItem.Severity.ToDescription(), }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetExtensionFieldNameForPut(ExtensionFields.PRIORITY), + Path = GetExtensionFieldNameForWrite(ExtensionFields.PRIORITY), Value = (int)workItem.Priority, }); patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, - Path = GetExtensionFieldNameForPut(ExtensionFields.SYSTEM_INFO), + Path = GetExtensionFieldNameForWrite(ExtensionFields.SYSTEM_INFO), Value = workItem.SystemInformation, }); @@ -312,27 +357,12 @@ namespace Tango.TFS }); } - private VssConnection CreateConnection() - { - VssConnection connection = new VssConnection(new Uri(CollectionURL), new VssBasicCredential(UserName, PersonalToken)); - return connection; - } - - private String GetFieldName(CoreField field) - { - return "System." + field.ToString(); - } - - private String GetFieldNameForPut(CoreField field) - { - return "/fields/System." + field.ToString(); - } - - private String GetExtensionFieldNameForPut(String extensionFieldName) - { - return "/fields/" + extensionFieldName; - } - + /// + /// Gets a work item by the specified team project and work item id. + /// + /// The project. + /// The identifier. + /// public Task GetWorkItem(Project project, int id) { return Task.Factory.StartNew(() => @@ -346,36 +376,36 @@ namespace Tango.TFS var item = witClient.GetWorkItemAsync(id, expand: Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemExpand.All).Result; workItem.ID = item.Id.Value; - workItem.Title = item.Fields[GetFieldName(CoreField.Title)].ToString(); - workItem.Description = TryGetField(item.Fields, GetFieldName(CoreField.Description)); + workItem.Title = item.Fields[GetFieldNameForRead(CoreField.Title)].ToString(); + workItem.Description = TryGetField(item.Fields, GetFieldNameForRead(CoreField.Description)); workItem.Area = new Area() { - Path = item.Fields[GetFieldName(CoreField.AreaPath)].ToString(), - Name = Path.GetFileName(item.Fields[GetFieldName(CoreField.AreaPath)].ToString()), + Path = item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString(), + Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString()), }; workItem.Iteration = new Iteration() { - Path = item.Fields[GetFieldName(CoreField.IterationPath)].ToString(), - Name = Path.GetFileName(item.Fields[GetFieldName(CoreField.IterationPath)].ToString()), + Path = item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString(), + Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString()), }; - workItem.AssignedTo = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldName(CoreField.AssignedTo))); - workItem.CreatedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldName(CoreField.CreatedBy))); - workItem.ChangedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldName(CoreField.ChangedBy))); - workItem.AuthorizedAs = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldName(CoreField.AuthorizedAs))); + workItem.AssignedTo = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AssignedTo))); + workItem.CreatedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.CreatedBy))); + workItem.ChangedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.ChangedBy))); + workItem.AuthorizedAs = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AuthorizedAs))); - workItem.Type = (WorkItemType)Enum.Parse(typeof(WorkItemType), item.Fields[GetFieldName(CoreField.WorkItemType)].ToString()); + workItem.Type = (WorkItemType)Enum.Parse(typeof(WorkItemType), item.Fields[GetFieldNameForRead(CoreField.WorkItemType)].ToString()); workItem.URL = item.Url; - if (item.Fields.ContainsKey(GetFieldName(CoreField.Tags))) + if (item.Fields.ContainsKey(GetFieldNameForRead(CoreField.Tags))) { - List tags = item.Fields[GetFieldName(CoreField.Tags)].ToString().Split(';').Select(x => x.Trim()).ToList(); + List tags = item.Fields[GetFieldNameForRead(CoreField.Tags)].ToString().Split(';').Select(x => x.Trim()).ToList(); workItem.Tags = tags.Select(x => new Tag() { Name = x }).ToList(); } workItem.FoundInBuild = item.Fields[ExtensionFields.FOUND_IN_BUILD].ToString(); - workItem.State = (State)Enum.Parse(typeof(State), item.Fields[GetFieldName(CoreField.State)].ToString()); + workItem.State = (State)Enum.Parse(typeof(State), item.Fields[GetFieldNameForRead(CoreField.State)].ToString()); workItem.Severity = ParseEnumByDescription(item.Fields[ExtensionFields.SEVERITY].ToString()); @@ -389,6 +419,12 @@ namespace Tango.TFS }); } + /// + /// Deletes the specified work item. + /// + /// The project. + /// The identifier. + /// public Task DeleteWorkItem(Project project, int id) { return Task.Factory.StartNew(() => @@ -400,6 +436,56 @@ namespace Tango.TFS }); } + #endregion + + #region Private Methods + + /// + /// Creates connection to VSTS. + /// + /// + private VssConnection CreateConnection() + { + VssConnection connection = new VssConnection(new Uri(CollectionURL), new VssBasicCredential(UserName, PersonalToken)); + return connection; + } + + /// + /// Creates a string representation of a VSTS field when reading. + /// + /// The field. + /// + private String GetFieldNameForRead(CoreField field) + { + return "System." + field.ToString(); + } + + /// + /// Creates a string representation of a VSTS field when writing. + /// + /// The field. + /// + private String GetFieldNameForWrite(CoreField field) + { + return "/fields/System." + field.ToString(); + } + + /// + /// Creates a string representation of a VSTS extension field when writing. + /// + /// The field. + /// + private String GetExtensionFieldNameForWrite(String extensionFieldName) + { + return "/fields/" + extensionFieldName; + } + + /// + /// Returns a value of the specified field key if found. Otherwise return an empty string. + /// + /// The fields. + /// The key. + /// private String TryGetField(IDictionary fields, String key) { if (fields.ContainsKey(key)) @@ -410,6 +496,12 @@ namespace Tango.TFS return String.Empty; } + /// + /// Returns the specified enum type value by it's [Description] attribute. + /// + /// + /// The description. + /// private T ParseEnumByDescription(String description) { Dictionary values = new Dictionary(); @@ -421,5 +513,7 @@ namespace Tango.TFS return values[description]; } + + #endregion } } diff --git a/Software/Visual_Studio/Tango.TFS/TeamMember.cs b/Software/Visual_Studio/Tango.TFS/TeamMember.cs index 0d8d402f8..747d550ca 100644 --- a/Software/Visual_Studio/Tango.TFS/TeamMember.cs +++ b/Software/Visual_Studio/Tango.TFS/TeamMember.cs @@ -19,5 +19,10 @@ namespace Tango.TFS get { return String.Format("{0} <{1}>", DisplayName, UniqueName); } } + public override string ToString() + { + return AssignName; + } + } } diff --git a/Software/Visual_Studio/Tango.TFS/WorkItem.cs b/Software/Visual_Studio/Tango.TFS/WorkItem.cs index 305f7d864..98ac05191 100644 --- a/Software/Visual_Studio/Tango.TFS/WorkItem.cs +++ b/Software/Visual_Studio/Tango.TFS/WorkItem.cs @@ -4,31 +4,141 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Core; namespace Tango.TFS { - public class WorkItem + public class WorkItem : ExtendedObject { - public int ID { get; set; } - public String URL { get; set; } - public WorkItemType Type { get; set; } - public Area Area { get; set; } - public Iteration Iteration { get; set; } - public String Title { get; set; } - public String Description { get; set; } - public State State { get; set; } - public Severity Severity { get; set; } - public Priority Priority { get; set; } - public String StepsToReproduce { get; set; } - public String SystemInformation { get; set; } - public TeamMember AssignedTo { get; set; } - public TeamMember CreatedBy { get; set; } - public TeamMember ChangedBy { get; set; } - public TeamMember AuthorizedAs { get; set; } + private int _id; + public int ID + { + get { return _id; } + internal set { _id = value; RaisePropertyChangedAuto(); } + } + + private String _url; + public String URL + { + get { return _url; } + internal set { _url = value; RaisePropertyChangedAuto(); } + } + + private WorkItemType _type; + public WorkItemType Type + { + get { return _type; } + set { _type = value; RaisePropertyChangedAuto(); } + } + + private Area _area; + public Area Area + { + get { return _area; } + set { _area = value; RaisePropertyChangedAuto(); } + } + + private Iteration _iteration; + public Iteration Iteration + { + get { return _iteration; } + set { _iteration = value; RaisePropertyChangedAuto(); } + } + + private String _title; + public String Title + { + get { return _title; } + set { _title = value; RaisePropertyChangedAuto(); } + } + + private String _description; + public String Description + { + get { return _description; } + set { _description = value; RaisePropertyChangedAuto(); } + } + + private State _state; + public State State + { + get { return _state; } + set { _state = value; RaisePropertyChangedAuto(); } + } + + private Severity _severity; + public Severity Severity + { + get { return _severity; } + set { _severity = value; RaisePropertyChangedAuto(); } + } + + private Priority _priority; + public Priority Priority + { + get { return _priority; } + set { _priority = value; RaisePropertyChangedAuto(); } + } + + private String _stepsToReproduce; + public String StepsToReproduce + { + get { return _stepsToReproduce; } + set { _stepsToReproduce = value; RaisePropertyChangedAuto(); } + } + + private String _systemInformation; + public String SystemInformation + { + get { return _systemInformation; } + set { _systemInformation = value; RaisePropertyChangedAuto(); } + } + + private TeamMember _assignedTo; + public TeamMember AssignedTo + { + get { return _assignedTo; } + set { _assignedTo = value; RaisePropertyChangedAuto(); } + } + + private TeamMember _createdBy; + public TeamMember CreatedBy + { + get { return _createdBy; } + set { _createdBy = value; RaisePropertyChangedAuto(); } + } + + private TeamMember _changedBy; + public TeamMember ChangedBy + { + get { return _changedBy; } + set { _changedBy = value; RaisePropertyChangedAuto(); } + } + + private TeamMember _authorizedAs; + public TeamMember AuthorizedAs + { + get { return _authorizedAs; } + set { _authorizedAs = value; RaisePropertyChangedAuto(); } + } + public List Attachments { get; set; } + public List Tags { get; set; } - public WorkItem UserStory { get; set; } - public String FoundInBuild { get; set; } + + private WorkItem _userStory; + public WorkItem UserStory + { + get { return _userStory; } + set { _userStory = value; RaisePropertyChangedAuto(); } + } + + private String _foundInBuild; + public String FoundInBuild + { + get { return _foundInBuild; } + set { _foundInBuild = value; RaisePropertyChangedAuto(); } + } public WorkItem() { -- cgit v1.3.1