private async Task <JsonPatchDocument> CreatePatchDocumentAsync(MigrationContext context, WorkItemUpdate update, CancellationToken cancellationToken) { var doc = new JsonPatchDocument(); if (update.Fields == null) { update.Fields = new Dictionary <string, WorkItemFieldUpdate>(StringComparer.OrdinalIgnoreCase); } var fields = update.Fields; //Ensure that the ChangedBy/Changed Date fields are being set so the history is maintained fields.EnsureFieldSet(WorkItemFields.ChangedBy, update.RevisedBy.Name); fields.EnsureFieldSet(WorkItemFields.ChangedDate, update.RevisedDate); //Copy the fields foreach (var field in fields) { cancellationToken.ThrowIfCancellationRequested(); context.FieldHandlers.TryGetValue(field.Key, out var handlers); if (handlers != null || Settings.IncludeAllFields) { var newField = new WorkItemFieldValue() { Name = field.Key, Value = field.Value.NewValue }; if (handlers != null) { foreach (var handler in handlers) { newField = await handler.HandleAsync(doc, newField, cancellationToken).ConfigureAwait(false); if (newField == null) { break; } } ; } ; if (newField != null) { //The final check before we add the item is to ensure that the field actually exists in the target system var allowedFields = await context.GetTargetFieldsAsync(cancellationToken).ConfigureAwait(false); if (!allowedFields.Contains(newField.Name, StringComparer.OrdinalIgnoreCase)) { Logger.Warning($"{newField.Name} was not found in target, skipping"); } else { var name = (newField.Name != field.Key) ? $"{newField.Name} (renamed from {field.Key})" : newField.Name; var value = newField.Value?.ToString() ?? ""; if (value.Length > 75) { value = value.Left(75) + "..."; } Logger.Debug($"{name} = {value}"); //Add or update it if (field.Value.OldValue != null) { doc.UpdateField(newField.Name, newField.Value); } else { doc.AddField(newField.Name, newField.Value); } }; } else { Logger.Debug($"{field.Key} is marked as ignore"); } } else { Logger.Debug($"{field.Key} has no handlers, skipping"); } } ; return(doc); }