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); }
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()); }
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); }
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()); }
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 } }); }
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); }
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); }
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}"); } }