public JiraMapper(JiraProvider jiraProvider, ConfigJson config) : base(jiraProvider?.Settings?.UserMappingFile)
 {
     _jiraProvider         = jiraProvider;
     _config               = config;
     _targetTypes          = InitializeTypeMappings();
     _fieldMappingsPerType = InitializeFieldMappings();
 }
 private static string GetCustomFieldId(string fieldName, JiraProvider jira)
 {
     if (jira.Jira.RestClient.Settings.Cache.CustomFields.TryGetValue(fieldName, out var customField))
     {
         return(customField.Id);
     }
     else
     {
         return(null);
     }
 }
        private static string[] ParseCustomField(string fieldName, JToken value, JiraProvider provider)
        {
            var serializedValue = new string[] { };

            if (provider.Jira.RestClient.Settings.Cache.CustomFields.TryGetValue(fieldName, out var customField) &&
                customField != null &&
                provider.Jira.RestClient.Settings.CustomFieldSerializers.TryGetValue(customField.CustomType, out var serializer))
            {
                serializedValue = serializer.FromJson(value);
            }

            return(serializedValue);
        }
        public static JiraItem CreateFromRest(string issueKey, JiraProvider jiraProvider)
        {
            var remoteIssue = jiraProvider.DownloadIssue(issueKey);

            Logger.Log(LogLevel.Debug, $"Downloaded {issueKey}");

            var jiraItem  = new JiraItem(jiraProvider, remoteIssue);
            var revisions = BuildRevisions(jiraItem, jiraProvider);

            jiraItem.Revisions = revisions;
            Logger.Log(LogLevel.Debug, $"Formed representation of jira item {issueKey}");

            return(jiraItem);
        }
        public static JiraProvider Initialize(JiraSettings settings)
        {
            var provider = new JiraProvider();

            provider.Jira     = ConnectToJira(settings);
            provider.Settings = settings;

            Logger.Log(LogLevel.Info, "Retrieving Jira fields...");
            provider.Jira.Fields.GetCustomFieldsAsync().Wait();
            Logger.Log(LogLevel.Info, "Retrieving Jira link types...");
            provider.LinkTypes = provider.Jira.Links.GetLinkTypesAsync().Result;

            return(provider);
        }
Esempio n. 6
0
        public static JiraItem CreateFromRest(string issueKey, JiraProvider jiraProvider)
        {
            var remoteIssue = jiraProvider.DownloadIssue(issueKey);

            Logger.Log(LogLevel.Debug, $"Downloaded item.");

            var jiraItem  = new JiraItem(jiraProvider, remoteIssue);
            var revisions = BuildRevisions(jiraItem, jiraProvider);

            jiraItem.Revisions = revisions;
            Logger.Log(LogLevel.Debug, $"Created {revisions.Count} history revisions.");

            return(jiraItem);
        }
        private static List <JiraRevision> BuildCommentRevisions(JiraItem jiraItem, JiraProvider jiraProvider)
        {
            var comments = jiraProvider.Jira.Issues.GetCommentsAsync(jiraItem.Key).Result;

            return(comments.Select(c => new JiraRevision(jiraItem)
            {
                Author = c.Author,
                Time = c.CreatedDate.Value,
                Fields = new Dictionary <string, object>()
                {
                    { "comment", c.Body }
                },
                AttachmentActions = new List <RevisionAction <JiraAttachment> >(),
                LinkActions = new List <RevisionAction <JiraLink> >()
            }).ToList());
        }
Esempio n. 8
0
        public static JiraProvider Initialize(JiraSettings settings)
        {
            var provider = new JiraProvider();

            provider.Jira     = ConnectToJira(settings);
            provider.Settings = settings;

            Logger.Log(LogLevel.Info, "Gathering project info...");

            // ensure that Custom fields cache is full
            provider.Jira.Fields.GetCustomFieldsAsync().Wait();
            Logger.Log(LogLevel.Info, "Custom field cache set up.");
            Logger.Log(LogLevel.Info, "Custom parsers set up.");

            provider.LinkTypes = provider.Jira.Links.GetLinkTypesAsync().Result;
            Logger.Log(LogLevel.Info, "Link types cache set up.");

            return(provider);
        }
Esempio n. 9
0
        public static JiraProvider Initialize(JiraSettings settings)
        {
            var provider = new JiraProvider();

            provider.Jira     = ConnectToJira(settings);
            provider.Settings = settings;

            Logger.Log(LogLevel.Info, "Retrieving Jira fields...");
            provider.Jira.Fields.GetCustomFieldsAsync().Wait();
            Logger.Log(LogLevel.Info, "Retrieving Jira link types...");
            try
            {
                provider.LinkTypes = provider.Jira.Links.GetLinkTypesAsync().Result;
            }
            catch (Exception e)
            {
                Logger.Log(e, "Failed to retrive linktypes from Jira");
            }
            return(provider);
        }
        private static List <JiraRevision> BuildCommentRevisions(JiraItem jiraItem, JiraProvider jiraProvider)
        {
            var renderedFields = jiraItem.RemoteIssue.SelectToken("$.renderedFields.comment.comments");
            var comments       = jiraProvider.Jira.Issues.GetCommentsAsync(jiraItem.Key).Result;

            return(comments.Select((c, i) => {
                var rc = renderedFields.SelectToken($"$.[{i}].body");
                return new JiraRevision(jiraItem)
                {
                    Author = c.Author,
                    Time = c.CreatedDate.Value,
                    Fields = new Dictionary <string, object>()
                    {
                        { "comment", c.Body }, { "comment$Rendered", RenderedComment(rc.Value <string>()) }
                    },
                    AttachmentActions = new List <RevisionAction <JiraAttachment> >(),
                    LinkActions = new List <RevisionAction <JiraLink> >()
                };
            }).ToList());
        }
Esempio n. 11
0
        private void ExecuteMigration(CommandOption user, CommandOption password, CommandOption url, CommandOption configFile, bool forceFresh)
        {
            var itemsCount         = 0;
            var exportedItemsCount = 0;
            var sw = new Stopwatch();

            sw.Start();

            try
            {
                string           configFileName   = configFile.Value();
                var              assemblyPath     = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                ConfigReaderJson configReaderJson = new ConfigReaderJson($"{assemblyPath}\\{configFileName}");
                var              config           = configReaderJson.Deserialize();

                InitSession(config);

                // Migration session level settings
                // where the logs and journal will be saved, logs aid debugging, journal is for recovery of interupted process
                string migrationWorkspace = config.Workspace;

                var downloadOptions = (DownloadOptions)config.DownloadOptions;

                var jiraSettings = new JiraSettings(user.Value(), password.Value(), url.Value(), config.SourceProject)
                {
                    BatchSize       = config.BatchSize,
                    UserMappingFile = config.UserMappingFile != null?Path.Combine(assemblyPath, config.UserMappingFile) : string.Empty,
                                          AttachmentsDir = Path.Combine(migrationWorkspace, config.AttachmentsFolder),
                                          JQL            = config.Query
                };

                JiraProvider jiraProvider = JiraProvider.Initialize(jiraSettings);

                itemsCount = jiraProvider.GetItemCount(jiraSettings.JQL);

                BeginSession(configFileName, config, forceFresh, jiraProvider, itemsCount);
                //change to retrive from fields
                jiraSettings.EpicLinkField = jiraProvider.Fields.FirstOrDefault(f => f.Name == "Epic Link")?.Id;
                if (string.IsNullOrEmpty(jiraSettings.EpicLinkField))
                {
                    Logger.Log(LogLevel.Warning, $"Epic link field missing for config field '{config.EpicLinkField}'.");
                }
                jiraSettings.SprintField = jiraProvider.Fields.FirstOrDefault(f => f.Name == "Sprint")?.Id;
                if (string.IsNullOrEmpty(jiraSettings.SprintField))
                {
                    Logger.Log(LogLevel.Warning, $"Sprint link field missing for config field '{config.SprintField}'.");
                }
                jiraSettings.ConfluenceLinkField = config.ConfluenceLinkField;
                if (string.IsNullOrEmpty(jiraSettings.ConfluenceLinkField))
                {
                    Logger.Log(LogLevel.Warning, $"Confluence link field missing for config field '{config.ConfluenceLinkField}'.");
                }

                var mapper        = new JiraMapper(jiraProvider, config);
                var localProvider = new WiItemProvider(migrationWorkspace);
                var exportedKeys  = new HashSet <string>(Directory.EnumerateFiles(migrationWorkspace, "*.json").Select(f => Path.GetFileNameWithoutExtension(f)));
                var skips         = forceFresh ? new HashSet <string>(Enumerable.Empty <string>()) : exportedKeys;

                var query = jiraSettings.JQL;
                //Debugging id ex: query = "project = PL AND id = PL-2449";

                var issues = jiraProvider.EnumerateIssues(query, skips, downloadOptions);

                foreach (var issue in issues)
                {
                    WiItem wiItem = mapper.Map(issue);
                    if (wiItem != null)
                    {
                        localProvider.Save(wiItem);
                        exportedItemsCount++;
                        Logger.Log(LogLevel.Debug, $"Exported as type '{wiItem.Type}'.");
                    }
                }
            }
            catch (CommandParsingException e)
            {
                Logger.Log(LogLevel.Error, $"Invalid command line option(s): {e}");
            }
            catch (Exception e)
            {
                Logger.Log(e, $"Unexpected migration error.");
            }
            finally
            {
                EndSession(itemsCount, sw);
            }
        }
        private void ExecuteMigration(CommandOption user, CommandOption password, CommandOption useOfflineBackup, CommandOption url, CommandOption configFile, bool forceFresh, CommandOption continueOnCritical)
        {
            var itemsCount = 0;
            var exportedItemsCount = 0;
            var sw = new Stopwatch();
            sw.Start();

            try
            {
                string configFileName = configFile.Value();
                ConfigReaderJson configReaderJson = new ConfigReaderJson(configFileName);
                var config = configReaderJson.Deserialize();

                InitSession(config, continueOnCritical.Value());

                // Migration session level settings
                // where the logs and journal will be saved, logs aid debugging, journal is for recovery of interupted process
                string migrationWorkspace = config.Workspace;

                var downloadOptions = (DownloadOptions)config.DownloadOptions;

                var jiraSettings = new JiraSettings(user.Value(), password.Value(), url.Value(), config.SourceProject)
                {
                    BatchSize = config.BatchSize,
                    UserMappingFile = config.UserMappingFile != null ? Path.Combine(migrationWorkspace, config.UserMappingFile) : string.Empty,
                    AttachmentsDir = Path.Combine(migrationWorkspace, config.AttachmentsFolder),
                    JQL = config.Query,
                    UsingJiraCloud = config.UsingJiraCloud
                };
                IJiraProvider jiraProvider = null;
                if (useOfflineBackup.HasValue())
                {
                    jiraProvider = OfflineJiraProvider.Initialize(useOfflineBackup.Value(), jiraSettings);
                }
                else
                {
                    jiraProvider = JiraProvider.Initialize(jiraSettings);
                }

                itemsCount = jiraProvider.GetItemCount(jiraSettings.JQL);

                BeginSession(configFileName, config, forceFresh, jiraProvider, itemsCount);

                jiraSettings.EpicLinkField = jiraProvider.GetCustomId(config.EpicLinkField);
                if (string.IsNullOrEmpty(jiraSettings.EpicLinkField))
                {
                    Logger.Log(LogLevel.Warning, $"Epic link field missing for config field '{config.EpicLinkField}'.");
                }
                jiraSettings.SprintField = jiraProvider.GetCustomId(config.SprintField);
                if (string.IsNullOrEmpty(jiraSettings.SprintField))
                {
                    Logger.Log(LogLevel.Warning, $"Sprint link field missing for config field '{config.SprintField}'.");
                }

                var mapper = new JiraMapper(jiraProvider, config);
                var localProvider = new WiItemProvider(migrationWorkspace);
                var exportedKeys = new HashSet<string>(Directory.EnumerateFiles(migrationWorkspace, "*.json").Select(f => Path.GetFileNameWithoutExtension(f)));
                var skips = forceFresh ? new HashSet<string>(Enumerable.Empty<string>()) : exportedKeys;

                var issues = jiraProvider.EnumerateIssues(jiraSettings.JQL, skips, downloadOptions);

                foreach (var issue in issues)
                {
                    if (issue == null)
                        continue;

                    WiItem wiItem = mapper.Map(issue);
                    if (wiItem != null)
                    {
                        localProvider.Save(wiItem);
                        exportedItemsCount++;
                        Logger.Log(LogLevel.Debug, $"Exported as type '{wiItem.Type}'.");
                    }
                }
            }
            catch (CommandParsingException e)
            {
                Logger.Log(LogLevel.Error, $"Invalid command line option(s): {e}");
            }
            catch (Exception e)
            {
                Logger.Log(e, $"Unexpected migration error.");
            }
            finally
            {
                EndSession(itemsCount, sw);
            }
        }
 private JiraItem(JiraProvider provider, JObject remoteIssue)
 {
     this._provider = provider;
     RemoteIssue    = remoteIssue;
 }
        private static Dictionary <string, object> ExtractFields(string key, JObject remoteFields, JiraProvider jira)
        {
            var fields = new Dictionary <string, object>();

            var extractName = new Func <JToken, object>((t) => t.ExValue <string>("$.name"));

            if (_fieldExtractionMapping == null)
            {
                _fieldExtractionMapping = new Dictionary <string, Func <JToken, object> >()
                {
                    { "priority", extractName },
                    { "labels", t => t.Values <string>().Any() ? string.Join(" ", t.Values <string>()) : null },
                    { "fixversions", t => t.Values <JObject>().Any() ? string.Join(", ", t.Select(st => st.ExValue <string>("$.name")).ToList()) : null },
                    { "versions", t => t.Values <JObject>().Any() ? string.Join(", ", t.Select(st => st.ExValue <string>("$.name")).ToList()) : null },
                    { "components", t => t.Values <JObject>().Any() ? string.Join(", ", t.Select(st => st.ExValue <string>("$.name")).ToList()) : null },
                    { "assignee", extractName },
                    { "creator", extractName },
                    { "reporter", extractName },
                    { jira.Settings.SprintField, t => string.Join(", ", ParseCustomField(jira.Settings.SprintField, t, jira)) },
                    { "status", extractName },
                    { "parent", t => t.ExValue <string>("$.key") }
                };
            }

            foreach (var prop in remoteFields.Properties())
            {
                var    type  = prop.Value.Type;
                var    name  = prop.Name.ToLower();
                object value = null;

                if (_fieldExtractionMapping.TryGetValue(name, out Func <JToken, object> mapping))
                {
                    value = mapping(prop.Value);
                }
                else if (type == JTokenType.String || type == JTokenType.Integer || type == JTokenType.Float)
                {
                    value = prop.Value.Value <string>();
                }
                else if (prop.Value.Type == JTokenType.Date)
                {
                    value = prop.Value.Value <DateTime>();
                }

                if (value != null)
                {
                    fields[name] = value;
                }
            }

            fields["key"]      = key;
            fields["issuekey"] = key;

            return(fields);
        }
        private static List <JiraRevision> BuildRevisions(JiraItem jiraItem, JiraProvider jiraProvider)
        {
            string issueKey    = jiraItem.Key;
            var    remoteIssue = jiraItem.RemoteIssue;
            Dictionary <string, object> fields      = ExtractFields(issueKey, (JObject)remoteIssue.SelectToken("$.fields"), jiraProvider);
            List <JiraAttachment>       attachments = ExtractAttachments(remoteIssue.SelectTokens("$.fields.attachment[*]").Cast <JObject>()) ?? new List <JiraAttachment>();
            List <JiraLink>             links       = ExtractLinks(issueKey, remoteIssue.SelectTokens("$.fields.issuelinks[*]").Cast <JObject>()) ?? new List <JiraLink>();


            var changelog = jiraProvider.DownloadChangelog(issueKey).ToList();

            changelog.Reverse();

            Stack <JiraRevision> revisions = new Stack <JiraRevision>();

            foreach (var change in changelog)
            {
                DateTime created = change.ExValue <DateTime>("$.created");
                string   author  = change.ExValue <string>("$.author.name");

                List <RevisionAction <JiraLink> >       linkChanges       = new List <RevisionAction <JiraLink> >();
                List <RevisionAction <JiraAttachment> > attachmentChanges = new List <RevisionAction <JiraAttachment> >();
                Dictionary <string, object>             fieldChanges      = new Dictionary <string, object>();

                var items = change.SelectTokens("$.items[*]").Cast <JObject>().Select(i => new JiraChangeItem(i));
                foreach (var item in items)
                {
                    if (item.Field == "Link")
                    {
                        var linkChange = TransformLinkChange(item, issueKey, jiraProvider);
                        if (linkChange == null)
                        {
                            continue;
                        }

                        linkChanges.Add(linkChange);

                        UndoLinkChange(linkChange, links);
                    }
                    else if (item.Field == "Attachment")
                    {
                        var attachmentChange = TransformAttachmentChange(item);
                        if (attachmentChange == null)
                        {
                            continue;
                        }

                        attachmentChanges.Add(attachmentChange);

                        UndoAttachmentChange(attachmentChange, attachments);
                    }
                    else
                    {
                        var(fieldref, from, to) = TransformFieldChange(item, jiraProvider);

                        fieldChanges[fieldref] = to;

                        // undo field change
                        if (string.IsNullOrEmpty(from))
                        {
                            fields.Remove(fieldref);
                        }
                        else
                        {
                            fields[fieldref] = from;
                        }
                    }
                }

                var revision = new JiraRevision(jiraItem)
                {
                    Time = created, Author = author, AttachmentActions = attachmentChanges, LinkActions = linkChanges, Fields = fieldChanges
                };
                revisions.Push(revision);
            }

            // what is left after undoing all changes is first revision
            var attActions = attachments.Select(a => new RevisionAction <JiraAttachment>()
            {
                ChangeType = RevisionChangeType.Added, Value = a
            }).ToList();
            var linkActions = links.Select(l => new RevisionAction <JiraLink>()
            {
                ChangeType = RevisionChangeType.Added, Value = l
            }).ToList();
            var fieldActions = fields;

            var reporter  = (string)fields["reporter"];
            var createdOn = (DateTime)fields["created"];

            var firstRevision = new JiraRevision(jiraItem)
            {
                Time = createdOn, Author = reporter, AttachmentActions = attActions, Fields = fieldActions, LinkActions = linkActions
            };

            revisions.Push(firstRevision);
            var listOfRevisions = revisions.ToList();

            List <JiraRevision> commentRevisions = BuildCommentRevisions(jiraItem, jiraProvider);

            listOfRevisions.AddRange(commentRevisions);
            listOfRevisions.Sort();

            foreach (var revAndI in listOfRevisions.Select((r, i) => (r, i)))
            {
                revAndI.Item1.Index = revAndI.Item2;
            }

            return(listOfRevisions);
        }
        private static RevisionAction <JiraLink> TransformLinkChange(JiraChangeItem item, string sourceItemKey, JiraProvider jira)
        {
            string             targetItemKey  = string.Empty;
            string             linkTypeString = string.Empty;
            RevisionChangeType changeType;

            if (item.From == null && item.To != null)
            {
                targetItemKey  = item.To;
                linkTypeString = item.ToString;
                changeType     = RevisionChangeType.Added;
            }
            else if (item.To == null && item.From != null)
            {
                targetItemKey  = item.From;
                linkTypeString = item.FromString;
                changeType     = RevisionChangeType.Removed;
            }
            else
            {
                Logger.Log(LogLevel.Error, $"Link change not handled!");
                return(null);
            }

            var linkType = jira.LinkTypes.FirstOrDefault(lt => linkTypeString.EndsWith(lt.Outward + " " + targetItemKey));

            if (linkType == null)
            {
                Logger.Log(LogLevel.Debug, $"Link with description \"{linkTypeString}\" is either not found or this issue ({sourceItemKey}) is not inward issue.");
                return(null);
            }
            else
            {
                if (linkType.Inward == linkType.Outward && sourceItemKey.CompareTo(targetItemKey) < 0)
                {
                    Logger.Log(LogLevel.Debug, $"Link is non-directional ({linkType.Name}) and sourceItem ({sourceItemKey}) is older then target item ({targetItemKey}). Link change will be part of target item.");
                    return(null);
                }

                return(new RevisionAction <JiraLink>()
                {
                    ChangeType = changeType,
                    Value = new JiraLink()
                    {
                        SourceItem = sourceItemKey,
                        TargetItem = targetItemKey,
                        LinkType = linkType.Name,
                    }
                });
            }
        }
 public WebClientWrapper(JiraProvider jira)
 {
     _jira      = jira;
     _webClient = new WebClient();
     _webClient.DownloadFileCompleted += _webClient_DownloadFileCompleted;
 }
        private static (string, string, string) TransformFieldChange(JiraChangeItem item, JiraProvider jira)
        {
            var objectFields = new HashSet <string>()
            {
                "assignee", "creator", "reporter"
            };
            string from, to = string.Empty;

            string fieldId = item.FieldId ?? GetCustomFieldId(item.Field, jira) ?? item.Field;

            if (objectFields.Contains(fieldId))
            {
                from = item.From;
                to   = item.To;
            }
            else
            {
                from = item.FromString;
                to   = item.ToString;
            }

            return(fieldId, from, to);
        }
 public JiraMapper(JiraProvider jiraProvider, ConfigJson config) : base(jiraProvider?.Settings?.UserMappingFile, jiraProvider?.Settings?.DomainMapping)
 {
     _jiraProvider         = jiraProvider;
     _config               = config;
     _fieldMappingsPerType = InitializeFieldMappings();
 }
        private static void BeginSession(string configFile, ConfigJson config, bool force, JiraProvider jiraProvider, int itemsCount)
        {
            var toolVersion = VersionInfo.GetVersionInfo();
            var osVersion   = System.Runtime.InteropServices.RuntimeInformation.OSDescription.Trim();
            var machine     = System.Environment.MachineName;
            var user        = $"{System.Environment.UserDomainName}\\{System.Environment.UserName}";
            var jiraVersion = jiraProvider.GetJiraVersion();

            Logger.Log(LogLevel.Info, $"Export started. Exporting {itemsCount} items.");

            Logger.StartSession("Jira Export",
                                "jira-export-started",
                                new Dictionary <string, string>()
            {
                { "Tool version :", toolVersion },
                { "Start time   :", DateTime.Now.ToString() },
                { "Telemetry    :", Logger.TelemetryStatus },
                { "Session id   :", Logger.SessionId },
                { "Tool user    :"******"Config       :", configFile },
                { "Force        :", force ? "yes" : "no" },
                { "Log level    :", config.LogLevel },
                { "Machine      :", machine },
                { "System       :", osVersion },
                { "Jira url     :", jiraProvider.Settings.Url },
                { "Jira user    :"******"Jira version :", jiraVersion.Version },
                { "Jira type    :", jiraVersion.DeploymentType }
            },
                                new Dictionary <string, string>()
            {
                { "item-count", itemsCount.ToString() },
                { "system-version", jiraVersion.Version },
                { "hosting-type", jiraVersion.DeploymentType }
            });
        }
Esempio n. 21
0
        private static Dictionary <string, object> ExtractFields(string key, JObject remoteIssue, JiraProvider jira)
        {
            var fields = new Dictionary <string, object>();

            var remoteFields   = (JObject)remoteIssue.SelectToken("$.fields");
            var renderedFields = (JObject)remoteIssue.SelectToken("$.renderedFields");

            var extractName = new Func <JToken, object>((t) => t.ExValue <string>("$.name"));
            var extractAccountIdOrUsername = new Func <JToken, object>((t) => t.ExValue <string>("$.name") ?? t.ExValue <string>("$.accountId"));

            if (_fieldExtractionMapping == null)
            {
                _fieldExtractionMapping = new Dictionary <string, Func <JToken, object> >()
                {
                    { "priority", extractName },
                    { "labels", t => t.Values <string>().Any() ? string.Join(" ", t.Values <string>()) : null },
                    { "assignee", extractName },
                    { "creator", extractName },
                    { "reporter", extractAccountIdOrUsername },
                    { jira.Settings.SprintField, t => string.Join(", ", ParseCustomField(jira.Settings.SprintField, t, jira)) },
                    { "status", extractName },
                    { "parent", t => t.ExValue <string>("$.key") }
                };
            }

            foreach (var prop in remoteFields.Properties())
            {
                var    type  = prop.Value.Type;
                var    name  = prop.Name.ToLower();
                object value = null;

                if (_fieldExtractionMapping.TryGetValue(name, out Func <JToken, object> mapping))
                {
                    value = mapping(prop.Value);
                }
                else if (type == JTokenType.String || type == JTokenType.Integer || type == JTokenType.Float)
                {
                    value = prop.Value.Value <string>();
                }
                else if (prop.Value.Type == JTokenType.Date)
                {
                    value = prop.Value.Value <DateTime>();
                }
                else if (type == JTokenType.Array && prop.Value.Any())
                {
                    value = string.Join(";", prop.Value.Select(st => st.ExValue <string>("$.name")).ToList());
                    if ((string)value == ";" || (string)value == "")
                    {
                        value = string.Join(";", prop.Value.Select(st => st.ExValue <string>("$.value")).ToList());
                    }
                }
                else if (type == Newtonsoft.Json.Linq.JTokenType.Object && prop.Value["value"] != null)
                {
                    value = prop.Value["value"].ToString();
                }

                if (value != null)
                {
                    fields[name] = value;

                    if (renderedFields.TryGetValue(name, out JToken rendered))
                    {
                        if (rendered.Type == JTokenType.String)
                        {
                            fields[name + "$Rendered"] = rendered.Value <string>();
                        }
                        else
                        {
                            Logger.Log(LogLevel.Debug, $"Rendered field {name} contains unparsable type {rendered.Type.ToString()}, using text");
                        }
                    }
                }
            }

            fields["key"]      = key;
            fields["issuekey"] = key;

            return(fields);
        }
Esempio n. 22
0
        private static List <JiraRevision> BuildRevisions(JiraItem jiraItem, JiraProvider jiraProvider)
        {
            string issueKey    = jiraItem.Key;
            var    remoteIssue = jiraItem.RemoteIssue;
            Dictionary <string, object> fields      = ExtractFields(issueKey, (JObject)remoteIssue, jiraProvider);
            List <JiraAttachment>       attachments = ExtractAttachments(remoteIssue.SelectTokens("$.fields.attachment[*]").Cast <JObject>()) ?? new List <JiraAttachment>();
            List <JiraLink>             links       = ExtractLinks(issueKey, remoteIssue.SelectTokens("$.fields.issuelinks[*]").Cast <JObject>()) ?? new List <JiraLink>();

            // save these field since these might be removed in the loop
            string reporter  = GetAuthor(fields);
            var    createdOn = fields.TryGetValue("created", out object crdate) ? (DateTime)crdate : default(DateTime);

            if (createdOn == DateTime.MinValue)
            {
                Logger.Log(LogLevel.Debug, "created key was not found, using DateTime default value");
            }


            var changelog = jiraProvider.DownloadChangelog(issueKey).ToList();

            Logger.Log(LogLevel.Debug, $"Downloaded issue: {issueKey} changelog.");
            changelog.Reverse();

            Stack <JiraRevision> revisions = new Stack <JiraRevision>();

            foreach (var change in changelog)
            {
                DateTime created = change.ExValue <DateTime>("$.created");
                string   author  = GetAuthor(change);

                List <RevisionAction <JiraLink> >       linkChanges       = new List <RevisionAction <JiraLink> >();
                List <RevisionAction <JiraAttachment> > attachmentChanges = new List <RevisionAction <JiraAttachment> >();
                Dictionary <string, object>             fieldChanges      = new Dictionary <string, object>(StringComparer.InvariantCultureIgnoreCase);

                var items = change.SelectTokens("$.items[*]")?.Cast <JObject>()?.Select(i => new JiraChangeItem(i));

                foreach (var item in items)
                {
                    if (item.Field == "Link")
                    {
                        var linkChange = TransformLinkChange(item, issueKey, jiraProvider);
                        if (linkChange == null)
                        {
                            continue;
                        }

                        linkChanges.Add(linkChange);

                        UndoLinkChange(linkChange, links);
                    }
                    else if (item.Field == "Attachment")
                    {
                        var attachmentChange = TransformAttachmentChange(item);
                        if (attachmentChange == null)
                        {
                            continue;
                        }

                        attachmentChanges.Add(attachmentChange);

                        UndoAttachmentChange(attachmentChange, attachments);
                    }
                    else
                    {
                        var(fieldref, from, to) = TransformFieldChange(item, jiraProvider);

                        fieldChanges[fieldref] = to;

                        // undo field change
                        if (string.IsNullOrEmpty(from))
                        {
                            fields.Remove(fieldref);
                        }
                        else
                        {
                            fields[fieldref] = from;
                        }
                    }
                }

                var revision = new JiraRevision(jiraItem)
                {
                    Time = created, Author = author, AttachmentActions = attachmentChanges, LinkActions = linkChanges, Fields = fieldChanges
                };
                revisions.Push(revision);
            }

            // what is left after undoing all changes is first revision
            var attActions = attachments.Select(a => new RevisionAction <JiraAttachment>()
            {
                ChangeType = RevisionChangeType.Added, Value = a
            }).ToList();
            var linkActions = links.Select(l => new RevisionAction <JiraLink>()
            {
                ChangeType = RevisionChangeType.Added, Value = l
            }).ToList();
            var fieldActions = fields;

            var firstRevision = new JiraRevision(jiraItem)
            {
                Time = createdOn, Author = reporter, AttachmentActions = attActions, Fields = fieldActions, LinkActions = linkActions
            };

            revisions.Push(firstRevision);
            var listOfRevisions = revisions.ToList();

            List <JiraRevision> commentRevisions = BuildCommentRevisions(jiraItem, jiraProvider);

            listOfRevisions.AddRange(commentRevisions);
            listOfRevisions.Sort();

            foreach (var revAndI in listOfRevisions.Select((r, i) => (r, i)))
            {
                revAndI.Item1.Index = revAndI.Item2;
            }

            return(listOfRevisions);
        }
Esempio n. 23
0
        private void ExecuteMigration(CommandOption user, CommandOption password, CommandOption url, CommandOption configFile, bool forceFresh)
        {
            ConfigJson config = null;

            try
            {
                string           configFileName   = configFile.Value();
                ConfigReaderJson configReaderJson = new ConfigReaderJson(configFileName);
                config = configReaderJson.Deserialize();

                // Migration session level settings
                // where the logs and journal will be saved, logs aid debugging, journal is for recovery of interupted process
                string migrationWorkspace = config.Workspace;

                // level of log messages that will be let through to console
                LogLevel logLevel;
                switch (config.LogLevel)
                {
                case "Info": logLevel = LogLevel.Info; break;

                case "Debug": logLevel = LogLevel.Debug; break;

                case "Warning": logLevel = LogLevel.Warning; break;

                case "Error": logLevel = LogLevel.Error; break;

                case "Critical": logLevel = LogLevel.Critical; break;

                default: logLevel = LogLevel.Debug; break;
                }

                var downloadOptions = JiraProvider.DownloadOptions.IncludeParentEpics | JiraProvider.DownloadOptions.IncludeSubItems | JiraProvider.DownloadOptions.IncludeParents;
                Logger.Init(migrationWorkspace, logLevel);

                var jiraSettings = new JiraSettings(user.Value(), password.Value(), url.Value(), config.SourceProject)
                {
                    BatchSize       = config.BatchSize,
                    UserMappingFile = config.UserMappingFile != null?Path.Combine(migrationWorkspace, config.UserMappingFile) : string.Empty,
                                          AttachmentsDir = Path.Combine(migrationWorkspace, config.AttachmentsFolder),
                                          JQL            = config.Query,
                                          DomainMapping  = config.DomainMapping
                };

                JiraProvider jiraProvider = JiraProvider.Initialize(jiraSettings);

                // Get the custom field names for epic link field and sprint field
                jiraSettings.EpicLinkField = jiraProvider.GetCustomId(config.EpicLinkField);
                jiraSettings.SprintField   = jiraProvider.GetCustomId(config.SprintField);

                var mapper        = new JiraMapper(jiraProvider, config);
                var localProvider = new WiItemProvider(migrationWorkspace);
                var exportedKeys  = new HashSet <string>(Directory.EnumerateFiles(migrationWorkspace, "*.json").Select(f => Path.GetFileNameWithoutExtension(f)));
                var skips         = forceFresh ? new HashSet <string>(Enumerable.Empty <string>()) : exportedKeys;

                foreach (var issue in jiraProvider.EnumerateIssues(jiraSettings.JQL, skips, downloadOptions))
                {
                    WiItem wiItem = mapper.Map(issue);
                    localProvider.Save(wiItem);
                    Logger.Log(LogLevel.Info, $"Exported {wiItem.ToString()}");
                }
            }
            catch (CommandParsingException e)
            {
                Logger.Log(LogLevel.Error, $"Invalid command line option(s): {e}");
            }
            catch (Exception e)
            {
                Logger.Log(LogLevel.Error, $"Unexpected error: {e}");
            }
        }