/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Collections.Generic; using System.ComponentModel; using System.Reflection; using System.Text; using System.Windows.Forms; using JetBrains.Omea.Jiffa.JiraSoap; using JetBrains.Omea.OpenAPI; using JetBrains.Omea.OpenApiEx; namespace JetBrains.Omea.Jiffa { public class JiraServer : ResourceObject { /// /// The JIRA SOAP interface. /// private JiraSoapServiceService _service = null; public static void Register(IPlugin owner) { string sNameName = Core.ResourceStore.PropTypes[Core.Props.Name].Name; Core.ResourceStore.ResourceTypes.Register(Types.JiraServer, Types.JiraServer, sNameName, ResourceTypeFlags.ResourceContainer, owner); Core.ResourceStore.ResourceTypes.Register(Types.JiraProject, Types.JiraProject, sNameName, ResourceTypeFlags.ResourceContainer, owner); Core.ResourceStore.ResourceTypes.Register(Types.JiraComponent, Types.JiraComponent, sNameName, ResourceTypeFlags.ResourceContainer, owner); Core.ResourceStore.ResourceTypes.Register(Types.JiraIssueType, Types.JiraIssueType, sNameName, ResourceTypeFlags.Normal, owner); Core.ResourceStore.ResourceTypes.Register(Types.JiraStatus, Types.JiraStatus, sNameName, ResourceTypeFlags.Normal, owner); Core.ResourceStore.ResourceTypes.Register(Types.JiraPriority, Types.JiraPriority, sNameName, ResourceTypeFlags.Normal, owner); Core.ResourceStore.ResourceTypes.Register(Types.JiraCustomField, Types.JiraCustomField, sNameName, ResourceTypeFlags.Normal, owner); Core.ResourceStore.ResourceTypes.Register(Types.JiraUser, Types.JiraUser, sNameName, ResourceTypeFlags.Normal, owner); Core.ResourceIconManager.RegisterResourceIconProvider(new string[] {Type, JiraProject.Type, JiraComponent.Type, Types.JiraIssueType, Types.JiraPriority, Types.JiraStatus}, new JiffaIconProvider()); /* workspaceMgr.RegisterWorkspaceType(_newsGroup, new int[] { -_propTo }, WorkspaceResourceType.Container); workspaceMgr.RegisterWorkspaceFolderType(_newsServer, _newsGroup, new int[] { -Core.Props.Parent }); workspaceMgr.RegisterWorkspaceFolderType(_newsFolder, _newsGroup, new int[] { -Core.Props.Parent }); workspaceMgr.RegisterWorkspaceType(_newsArticle, new int[] { -_propAttachment }, WorkspaceResourceType.None);*/ foreach(string sPropName in Props.StringProps) Core.ResourceStore.PropTypes.Register(sPropName, PropDataType.String, PropTypeFlags.Normal, owner); } /// /// Downloads all the projects, components, etc from the server and creates resources for them. /// public void Sync() { List> arSyncActions = new List>(); // Prepare the list of sync actions arSyncActions.Add(new KeyValuePair("JIRA Projects", delegate { new ServerChildHelper(this, Types.JiraProject).SyncAll(Service.getProjectsNoSchemes); })); arSyncActions.Add(new KeyValuePair("JIRA Issue Types", delegate { new ServerChildHelper(this, Types.JiraIssueType).SyncAll(Service.getIssueTypes); })); arSyncActions.Add(new KeyValuePair("JIRA Issue Statuses", delegate { new ServerChildHelper(this, Types.JiraStatus).SyncAll(Service.getStatuses); })); arSyncActions.Add(new KeyValuePair("JIRA Issue Priorities", delegate { new ServerChildHelper(this, Types.JiraPriority).SyncAll(Service.getPriorities); })); //arSyncActions.Add(new KeyValuePair("JIRA Issue Custom Fields", delegate { new ServerChildHelper(this, Types.JiraCustomField).SyncAll(Service.getCustomFields); })); //arSyncActions.Add(new KeyValuePair("JIRA Users", delegate { new ServerChildHelper(this, Types.JiraUser).SyncAll(Service.getUser); })); // Invoke the syncers StringBuilder sb = new StringBuilder(); foreach(KeyValuePair pair in arSyncActions) { try { pair.Value(); // Exec! } catch(Exception ex) { sb.AppendFormat("Failed to sync {0}. {1}", pair.Key, ex.Message); sb.AppendLine(); } } // Show the errors, if any if(sb.Length > 0) Core.UserInterfaceAP.QueueJob(Jiffa.Name + " Sync Error Message", ((MethodInvoker)delegate { MessageBox.Show(Core.MainWindow, string.Format("There were errors syncing to the “{0}” JIRA server.\n\n{1}", Name, sb), Jiffa.Name, MessageBoxButtons.OK, MessageBoxIcon.Information); })); } /* protected void SyncProjects() { // Create missing projects, update existing projects Dictionary mapLiveProjects = new Dictionary(); // Hash-set for the projects that are still alive in JIRA foreach(RemoteProject projectJira in Service.getProjects(GetSignInToken())) { JiraProject projectLocal = FindProjectByJiraId(projectJira.id); if(projectLocal == null) projectLocal = CreateProject(projectJira); else projectLocal.Undelete(); projectLocal.Async = false; projectLocal.Sync(projectJira); mapLiveProjects[projectLocal.Resource] = true; } // Mark those projects no more in JIRA as deleted foreach(JiraProject projectLocal in Projects) { if(!mapLiveProjects.ContainsKey(projectLocal.Resource)) projectLocal.Delete(); } }*/ /* protected void SyncChildren(GetChildrenFromJiraDelegate delegateGetChildren, FindChildByJiraId delegateFindChild) where TJira : AbstractNamedRemoteEntity where TLocal : ResourceObject { // Create missing items, update existing items Dictionary mapLiving = new Dictionary(); // Hash-set for the items that are still alive in JIRA foreach(TJira itemJira in delegateGetChildren(GetSignInToken())) { TLocal itemLocal = delegateFindChild(itemJira.id); if(itemLocal == null) itemLocal = CreateComponent(componentJira); else itemLocal.Undelete(); itemLocal.Async = false; itemLocal.Sync(componentJira); mapLiving[itemLocal.Resource] = true; } // Mark those projects no more in JIRA as deleted foreach(JiraComponent componentLocal in Components) { if(!mapLiving.ContainsKey(componentLocal.Resource)) componentLocal.Delete(); } }*/ /// /// Gets or sets the username for this server connection. /// [Browsable(true)] [Category("Credentials")] [Description("Username for JIRA sign-in.")] public string Username { get { return Resource.GetPropText(Props.Username); } set { if(value == null) throw new ArgumentNullException(); WriteProp(Props.Username, value); } } /// /// Gets or sets the password for this server connection. /// Note: due to security reasons, the password returned is always a mock string. /// [Browsable(true)] [Category("Credentials")] [Description("Password for JIRA sign-in.")] public string Password { get { return "•••••"; } set { if(value == null) throw new ArgumentNullException(); WriteProp(Props.Password, value); } } /// /// Gets the password, as it's stored in the database. /// protected string GetPasswordRaw() { return Resource.GetPropText(Props.Password); } /// /// Gets or sets the Web Service URI of this JIRA server. /// [Browsable(true)] [Category("General")] [Description("URI of the JIRA server SOAP interface.")] public string Uri { get { return Resource.GetPropText(Props.Uri); } set { if(value == null) throw new ArgumentNullException(); if(value == Uri) return; WriteProp(Props.Uri, value); _service = null; // Recreate the service for the new URI } } /// /// Gets or sets the JIRA server user-readable name. /// [Browsable(true)] [Category("General")] [Description("Display name for the server.")] public string Name { get { return Resource.GetPropText(Core.Props.Name); } set { if(value == null) throw new ArgumentNullException(); WriteProp(Core.Props.Name, value); } } [Browsable(false)] public JiraSoapServiceService Service { get { if(_service == null) { _service = new JiraSoapServiceService(); _service.Url = Uri; } return _service ?? (_service = new JiraSoapServiceService()); } } public JiraServer(IResource resource) : base(resource) { } public JiraProject FindProjectByJiraId(string id) { IResourceList resources = Core.ResourceStore.FindResources(Types.JiraProject, Props.JiraId, id); resources = resources.Intersect(Resource.GetLinksTo(Types.JiraProject, Core.Props.Parent), true); if(resources.Count > 0) return GetProject(resources[0]); else return null; } public JiraProject GetProject(IResource resource) { return new JiraProject(this, resource); } /// /// Gets a list of all the JIRA projects under this JIRA server. /// [Browsable(false)] public IList Projects { get { return new ServerChildHelper(this, Types.JiraProject).GetList(); } } /// /// Gets a list of all the JIRA projects under this JIRA server. /// [Browsable(false)] public IList IssueTypes { get { return new ServerChildHelper(this, Types.JiraIssueType).GetList(); } } /// /// Gets a list of all the JIRA projects under this JIRA server. /// [Browsable(false)] public IList Statuses { get { return new ServerChildHelper(this, Types.JiraStatus).GetList(); } } /// /// Gets a list of all the JIRA projects under this JIRA server. /// [Browsable(false)] public IList Priorities { get { return new ServerChildHelper(this, Types.JiraPriority).GetList(); } } /// /// While syncing to JIRA, creates a new Omea resource for the locally-missing project. /// /// protected JiraProject CreateProject(RemoteProject projectJira) { ResourceProxy proxy = ResourceProxy.BeginNewResource(Types.JiraProject); proxy.AsyncPriority = JobPriority.Normal; proxy.SetProp(Props.JiraId, projectJira.id); proxy.AddLink(Core.Props.Parent, Resource); proxy.EndUpdate(); return GetProject(proxy.Resource); } /// /// Gets a sign-in token for working with the . /// /// A string to be passed into various 's methods. public string GetSignInToken() { return Service.login(Username, GetPasswordRaw()); } /// /// Creates a new JIRA server resource. /// public static JiraServer CreateNew() { ResourceProxy proxy = ResourceProxy.BeginNewResource(Type); proxy.AddLink(Core.Props.Parent, RootResource); proxy.EndUpdate(); JiraServer retval = new JiraServer(proxy.Resource); retval.Name = Jiffa.GetRandomName(); return retval; } /// /// Resource type for the wrappee resources. /// public static string Type { get { return Types.JiraServer; } } /// /// Gets the root resource to which all of the resources should be linked as children with a parent link. /// public static IResource RootResource { get { return Core.ResourceTreeManager.GetRootForType(Type); } } /// /// Displays a property sheet window for editing the server properties. /// public void ShowPropertySheet() { new ServerPropertiesSheet(this).Show(Core.MainWindow); } } public class ServerChildHelper : IResourceObjectFactory where TResourceObject : class, IResourceObject where TJira : AbstractNamedRemoteEntity { private readonly JiraServer _server; private readonly string _restype; public ServerChildHelper(JiraServer server, string restype) { _server = server; _restype = restype; } public IList GetList() { return new ResourceObjectsList(_server.Resource.GetLinksTo(_restype, Core.Props.Parent), this); } public delegate TJira[] GetChildrenFromJiraDelegate(string token); public void SyncAll(GetChildrenFromJiraDelegate delegateGetChildren) { // Create missing items, update existing items Dictionary mapLiving = new Dictionary(); // Hash-set for the items that are still alive in JIRA foreach(TJira itemJira in delegateGetChildren(_server.GetSignInToken())) { TResourceObject itemLocal = FindByJiraId(itemJira.id); if(itemLocal == null) itemLocal = CreateNewResource(itemJira); else itemLocal.Undelete(); itemLocal.Async = false; ((ISyncableTo)itemLocal).Sync(itemJira); mapLiving[itemLocal.Resource] = true; } // Mark those projects no more in JIRA as deleted foreach(TResourceObject componentLocal in GetList()) { if(!mapLiving.ContainsKey(componentLocal.Resource)) componentLocal.Delete(); } } private TResourceObject CreateNewResource(TJira itemJira) { ResourceProxy proxy = ResourceProxy.BeginNewResource(_restype); proxy.SetProp(Props.JiraId, itemJira.id); proxy.AddLink(Core.Props.Parent, _server.Resource); proxy.EndUpdate(); return CreateResourceObject(proxy.Resource); } public TResourceObject CreateResourceObject(IResource resource) { if(resource == null) throw new ArgumentNullException("resource"); ConstructorInfo ctor = typeof(TResourceObject).GetConstructor(new Type[] {typeof(JiraServer), typeof(IResource)}); return (TResourceObject)ctor.Invoke(new object[] {_server, resource}); } public TResourceObject FindByJiraId(string id) { IResourceList resources = Core.ResourceStore.FindResources(_restype, Props.JiraId, id); resources = resources.Intersect(_server.Resource.GetLinksTo(_restype, Core.Props.Parent), true); if(resources.Count > 0) return CreateResourceObject(resources[0]); else return null; } } }