public string AddAttachmentToBug() { var _id = _configuration.WorkItemId; var _filePath = _configuration.FilePath; using (WorkItemTrackingHttpClient workItemTrackingHttpClient = new WorkItemTrackingHttpClient(_uri, _credentials)) { // upload attachment to attachment store and // get a reference to that file AttachmentReference attachmentReference = workItemTrackingHttpClient.CreateAttachmentAsync(_filePath).Result; JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = attachmentReference.Url, attributes = new { comment = "adding link to bug" } } }); WorkItem result = workItemTrackingHttpClient.UpdateWorkItemAsync(patchDocument, _id).Result; } return("success"); }
/// <summary> /// Uploads the screenshot at the given path to the work item with the given issue id /// </summary> /// <param name="path">path of screenshot to upload</param> /// <returns>Task with URL of the screenshot attachment reference, null if not connected</returns> internal async Task <string> AttachScreenshotToIssue(string path, int issueId) { if (!ConnectedToAzureDevOps) { return(null); } WorkItemTrackingHttpClient wit = _baseServerConnection.GetClient <WorkItemTrackingHttpClient>(); AttachmentReference attachment = await wit.CreateAttachmentAsync(new FileStream(path, FileMode.Open), Invariant($"{issueId}-pic.png")).ConfigureAwait(false); JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = attachment.Url, attributes = new { comment = "Screenshot of element" } } } ); // Return attachment URL once this work item is updated #pragma warning disable CA2007 // Do not directly await a Task #pragma warning disable CA2008 // Do not create tasks without passing a TaskScheduler return(await wit.UpdateWorkItemAsync(patchDoc, issueId).ContinueWith(t => attachment.Url)); #pragma warning restore CA2008 // Do not create tasks without passing a TaskScheduler #pragma warning restore CA2007 // Do not directly await a Task }
private AttachmentReference CreateAttachmentReference(Attachment attachment) { var fileName = Path.Combine(Path.GetTempPath(), attachment.Name); try { using (var client = new WebClient()) { client.UseDefaultCredentials = true; client.DownloadFile(attachment.Uri, fileName); } var attachmentRef = workClient.CreateAttachmentAsync(fileName).Result; File.Delete(fileName); return(attachmentRef); } catch (Exception e) { Logger.Error($"{GetType()} Failed downloading attachment from {attachment.Uri} to {fileName}{Environment.NewLine}\t{e.Message}"); } return(null); }
private static List <AttachmentReference> CreateAttachments(WorkItemTrackingHttpClient httpClient, List <string> filePathsToBeAttached) { var attachments = new List <AttachmentReference>(); if (filePathsToBeAttached == null || !filePathsToBeAttached.Any()) { return(attachments); } foreach (var filePath in filePathsToBeAttached) { try { using FileStream fileStream = File.OpenRead(filePath); var attachment = httpClient.CreateAttachmentAsync(uploadStream: fileStream, project: _project, fileName: Path.GetFileName(filePath)).Result; attachments.Add(attachment); } catch (Exception ex) { Debug.WriteLine(ex.Message); } } return(attachments); }
public AttachmentReference UploadAttachmentBinaryFile(string filePath) { VssConnection connection = new VssConnection(_uri, _credentials); WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient <WorkItemTrackingHttpClient>(); AttachmentReference attachmentReference = workItemTrackingHttpClient.CreateAttachmentAsync(@filePath).Result; return(attachmentReference); }
private AttachmentReference UploadAttachment(AzureDevOpsWorkItemAttachment attachment) { AttachmentReference uploadedFile; var name = Path.GetFileName(attachment.AttachmentPath); using (var attStream = new FileStream(attachment.AttachmentPath, FileMode.Open, FileAccess.Read)) { uploadedFile = WorkItemTrackingHttpClient.CreateAttachmentAsync(attStream, ProjectName, fileName: name) .Result; } return(uploadedFile); }
public WorkItem AddAttachment() { int id = Convert.ToInt32(Context.GetValue <WorkItem>("$newWorkItem3").Id); string filePath = ClientSampleHelpers.GetSampleTextFile(); VssConnection connection = Context.Connection; WorkItemTrackingHttpClient workItemTrackingClient = connection.GetClient <WorkItemTrackingHttpClient>(); // upload attachment to store and get a reference to that file AttachmentReference attachmentReference = workItemTrackingClient.CreateAttachmentAsync(filePath).Result; JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Test, Path = "/rev", Value = "1" } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.History", Value = "Adding the necessary spec" } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = attachmentReference.Url, attributes = new { comment = "VanDelay Industries - Spec" } } } ); WorkItem result = workItemTrackingClient.UpdateWorkItemAsync(patchDocument, id).Result; return(result); }
/// <summary> /// Upload attachment of file at the given path to the issue with given issue id /// from "AzureDevOps-dotnet-samples" repo /// Also adds comment about snapshot to work item /// </summary> /// <param name="path">path to file that should be attached</param> /// <param name="issueId">issue id to attach file to</param> /// <returns>Task with completed issue ID or null if user is not connected to AzureDevOps</returns> internal async Task <int?> AttachTestResultToIssue(string path, int issueId) { if (!ConnectedToAzureDevOps) { return(null); } WorkItemTrackingHttpClient wit = _baseServerConnection.GetClient <WorkItemTrackingHttpClient>(); AttachmentReference attachment; using (FileStream outputStream = new FileStream(path, FileMode.Open)) { attachment = await wit.CreateAttachmentAsync(outputStream, Invariant($"{issueId}.a11ytest")).ConfigureAwait(false); } JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add(new JsonPatchOperation() { Operation = Operation.Test, Path = "/rev", Value = "1" } ); patchDoc.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.History", Value = "Attached an Accessibility Insights for Windows test file and screenshot." } ); patchDoc.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = attachment.Url, attributes = new { comment = "Accessibility Insights for Windows test file" } } } ); #pragma warning disable CA2007 // Do not directly await a Task #pragma warning disable CA2008 // Do not create tasks without passing a TaskScheduler return(await wit.UpdateWorkItemAsync(patchDoc, issueId).ContinueWith(t => t.Result.Id)); #pragma warning restore CA2008 // Do not create tasks without passing a TaskScheduler #pragma warning restore CA2007 // Do not directly await a Task }
// This method will upload attachment and return url for file and Id private static AttachmentReference UploadAttachment(Uri uri, VssBasicCredential credentials) { try { VssConnection _tpc = new VssConnection(uri, credentials); WorkItemTrackingHttpClient workItemTrackingHttpClient = _tpc.GetClient <WorkItemTrackingHttpClient>(); AttachmentReference attachment = workItemTrackingHttpClient.CreateAttachmentAsync(PathOfAttachment).Result; // Save the attachment ID for the "download" sample call later return(attachment); } catch (Exception ex) { Log.Logger.Error("Error occurred while Attaching Attachment in bug" + ex); return(null); } }
public WorkItem UpdateWorkItemAddAttachment(int id, string filePath) { VssConnection connection = new VssConnection(_uri, _credentials); WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient <WorkItemTrackingHttpClient>(); // upload attachment to attachment store and // get a reference to that file AttachmentReference attachmentReference = workItemTrackingHttpClient.CreateAttachmentAsync(filePath).Result; JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Test, Path = "/rev", Value = "1" } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.History", Value = "Adding the necessary spec" } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = attachmentReference.Url, attributes = new { comment = "VanDelay Industries - Spec" } } } ); WorkItem result = workItemTrackingHttpClient.UpdateWorkItemAsync(patchDocument, id).Result; return(result); }
public async static Task <AttachmentReference> CreateAttachmentAsync(WorkItemTrackingHttpClient client, MemoryStream uploadStream) { return(await RetryHelper.RetryAsync(async() => { // clone the stream since if upload fails it disploses the underlying stream using (var clonedStream = new MemoryStream()) { await uploadStream.CopyToAsync(clonedStream); // reset position for both streams uploadStream.Position = 0; clonedStream.Position = 0; return await client.CreateAttachmentAsync(clonedStream); } }, 5)); }
public AttachmentReference UploadBinaryFile() { // Full path to the binary file to upload as an attachment string filePath = ClientSampleHelpers.GetSampleBinaryFile(); VssConnection connection = Context.Connection; WorkItemTrackingHttpClient workItemTrackingClient = connection.GetClient <WorkItemTrackingHttpClient>(); Console.WriteLine("Attempting upload of: {0}", filePath); AttachmentReference attachment = workItemTrackingClient.CreateAttachmentAsync(@filePath).Result; Console.WriteLine("Attachment created"); Console.WriteLine(" ID : {0}", attachment.Id); Console.WriteLine(" URL : {0}", attachment.Url); return(attachment); }
static void AddAttachment(int WiID, System.IO.Compression.ZipArchiveEntry FilePath) { AttachmentReference att; using (var attStream = FilePath.Open()) { att = WitClient.CreateAttachmentAsync(attStream, FilePath.Name).Result; // upload the file } List <object> references = new List <object> { new { rel = RelConstants.AttachmentRefStr, url = att.Url, attributes = new { comment = "" } } }; AddWorkItemRelations(WiID, references); }
public AttachmentReference UploadTextFile() { // Full path to the text file to upload as an attachment string filePath = ClientSampleHelpers.GetSampleTextFile(); // Get a client VssConnection connection = Context.Connection; WorkItemTrackingHttpClient workItemTrackingClient = connection.GetClient <WorkItemTrackingHttpClient>(); Console.WriteLine("Attempting upload of: {0}", filePath); // Upload the attachment AttachmentReference attachment = workItemTrackingClient.CreateAttachmentAsync(@filePath).Result; Console.WriteLine("Attachment created"); Console.WriteLine(" ID : {0}", attachment.Id); Console.WriteLine(" URL : {0}", attachment.Url); // Save the attachment ID for the "download" sample call later Context.SetValue <Guid>("$attachmentId", attachment.Id); Context.SetValue <string>("$attachmentFileName", Path.GetFileName(filePath)); return(attachment); }
public async Task <IActionResult> CreateWorkItem([FromBody] WorkTask wt) { CurrentUser cUser = new CurrentUser(HttpContext, _configuration); var uriLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_Uri"); Uri accountUri = new Uri(uriLookup.Lvalue); var tokenLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_AccessToken"); String personalAccessToken = tokenLookup.Lvalue; var projectLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_Project"); wt.Project = projectLookup.Lvalue; // Create a connection to the account VssConnection connection = new VssConnection(accountUri, new VssBasicCredential(string.Empty, personalAccessToken)); // Get an instance of the work item tracking client WorkItemTrackingHttpClient witClient = connection.GetClient <WorkItemTrackingHttpClient>(); JsonPatchDocument patchDocument = new JsonPatchDocument { //add fields and their values to your patch document new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Title", Value = wt.Title }, new JsonPatchOperation() { Operation = Operation.Add, Path = (wt.Type == "Bug" ? "/fields/Microsoft.VSTS.TCM.ReproSteps" : "/fields/System.Description"), Value = wt.Description } }; var iterationLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_IterationPath"); if (iterationLookup != null) { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.IterationPath", Value = iterationLookup.Lvalue }); } var parentRelationLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_ParentRelation"); if (parentRelationLookup != null) { int id = Int32.Parse(parentRelationLookup.Lvalue); WorkItem workitem = witClient.GetWorkItemAsync(id).Result; patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "System.LinkTypes.Hierarchy-Reverse", url = workitem.Url //url = "https://dev.azure.com/inser13/SKF-Emaintenance/_apis/wit/workItems/2308" } }); } var areaPathLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_AreaPath"); if (areaPathLookup != null) { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.AreaPath", Value = areaPathLookup.Lvalue }); } if (wt.Priority != string.Empty && wt.Priority != null) { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Common.Priority", Value = wt.Priority }); } patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Tags", Value = wt.Tags + "," + cUser.Email }); //if (wt.Comment != string.Empty && wt.Comment != null) //{ // patchDocument.Add(new JsonPatchOperation() // { // Operation = Operation.Add, // Path = "/fields/System.History", // Value = wt.Comment + "<p>By :" + cUser.Email + "</p>" // }); //} if (wt.DeviceInfo != string.Empty && wt.DeviceInfo != null) { string Comment = null; if (wt.Comment != string.Empty && wt.Comment != null) { Comment = wt.Comment + "<p>By :" + cUser.Email + "</p>"; } Comment = Comment + "</p>" + wt.DeviceInfo; patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.History", Value = Comment }); } try { if (wt.ScreenShot != string.Empty && wt.ScreenShot != null) { string convert = wt.ScreenShot.Replace("data:image/png;base64,", String.Empty); var stream = new MemoryStream(Convert.FromBase64String(convert)); AttachmentReference attachment = witClient.CreateAttachmentAsync(stream, wt.Project, "ScreenShot.jpg", null, null, null).Result; patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = attachment.Url, attributes = new { comment = "Emaintenance Attachment by " + cUser.Email } } }); } WorkItem result = witClient.CreateWorkItemAsync(patchDocument, wt.Project, wt.Type).Result; return(Ok(result)); } catch (AggregateException ex) { throw ex; } }
public override async Task <IEnumerable <WorkItemFilingMetadata> > FileWorkItems(IEnumerable <WorkItemFilingMetadata> workItemFilingMetadata) { foreach (WorkItemFilingMetadata metadata in workItemFilingMetadata) { AttachmentReference attachmentReference = null; string attachmentText = metadata.Attachment?.Text; if (!string.IsNullOrEmpty(attachmentText)) { using (var stream = new MemoryStream()) using (var writer = new StreamWriter(stream)) { writer.Write(attachmentText); writer.Flush(); stream.Position = 0; try { attachmentReference = await _witClient.CreateAttachmentAsync(stream, fileName : metadata.Attachment.Name); } catch { // TBD error handling throw; } } } var patchDocument = new JsonPatchDocument { new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{WorkItemFields.Title}", Value = metadata.Title }, new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{WorkItemFields.ReproSteps}", Value = metadata.Description }, new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{WorkItemFields.AreaPath}", Value = metadata.AreaPath }, new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{WorkItemFields.Tags}", Value = string.Join(",", metadata.GetAllTags()) } }; foreach (var customField in metadata.CustomFields) { patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{customField.Key}", Value = customField.Value }); } if (attachmentReference != null) { patchDocument.Add( new JsonPatchOperation { Operation = Operation.Add, Path = $"/relations/-", Value = new { rel = "AttachedFile", attachmentReference.Url } }); } WorkItem workItem = null; try { Console.Write($"Creating work item: {metadata.Title}"); workItem = await _witClient.CreateWorkItemAsync(patchDocument, project : _projectName, "Bug"); Console.WriteLine($": {workItem.Id}: DONE"); } catch (Exception e) { Console.Error.WriteLine(e); if (patchDocument != null) { string patchJson = JsonConvert.SerializeObject(patchDocument, Formatting.Indented); Console.Error.WriteLine(patchJson); } continue; } const string HTML = "html"; SarifLog sarifLog = (SarifLog)metadata.Object; foreach (Result result in sarifLog.Runs[0].Results) { if (workItem.Links?.Links?.ContainsKey(HTML) == true) { result.WorkItemUris = new List <Uri> { new Uri(((ReferenceLink)workItem.Links.Links[HTML]).Href, UriKind.Absolute) }; } else { result.WorkItemUris = new List <Uri> { new Uri(workItem.Url, UriKind.Absolute) }; } } } return(workItemFilingMetadata); }
private void Save(bool skipSaveSuccessDialog = false) { var isNewMode = (tabControl1.SelectedTab == tabNewItem); var isUpdateMode = (tabControl1.SelectedTab == tabUpdateItem); btnSaveNClose.Enabled = false; btnSave.Enabled = false; List <string> saveFilePaths = new List <string>(); string tempFolder = GetTempFolder(); try { Directory.CreateDirectory(tempFolder); var tfsConnection = Globals.ThisAddIn.Settings.TfsConfigurations[cbProject.SelectedIndex]; WorkItemTrackingHttpClient witClient = GetVssClient(tfsConnection); WorkItem result = null; var bodyText = new StringBuilder(txtBody.Text); if (isNewMode) { JsonPatchDocument patchDocument = new JsonPatchDocument(); //add fields and their values to your patch document patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Title", Value = txtTitle.Text } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.ReproSteps", Value = bodyText.ToString() } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Description", Value = bodyText.ToString() } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.SystemInfo", Value = txtSystemInformation.Text } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Common.Priority", Value = cbPriority.Text } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Common.Severity", Value = cbSeverity.Text } ); //TODO: //patchDocument.Add( // new JsonPatchOperation() // { // Operation = Operation.Add, // Path = "/fields/System.AssignedTo", // Value = _exchangeUser.Name + " <" + _exchangeUser.PrimarySmtpAddress + ">" // } //); // add parent if (parentItem != null) { patchDocument.Add(new Microsoft.VisualStudio.Services.WebApi.Patch.Json.JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "System.LinkTypes.Hierarchy-Reverse", url = tfsConnection.TfsUrl + tfsConnection.TfsProject + "/_apis/wit/workItems/" + parentItem.Id, attributes = new { comment = "link parent WIT" } } }); var fieldsToInherit = new string[] { "System.AreaPath", "System.IterationPath" }; foreach (var fieldToInherit in fieldsToInherit) { if (parentItem.Fields.ContainsKey(fieldToInherit)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/" + fieldToInherit, Value = parentItem.Fields[fieldToInherit] } );; } } } /* * patchDocument.Add( * new JsonPatchOperation() * { * Operation = Operation.Add, * Path = "/fields/Microsoft.VSTS.Common.ReviewedBy", * Value = _outlookCurrentMailItem.SenderName + " <" + _outlookCurrentMailItem.Sender.GetExchangeUser().PrimarySmtpAddress + ">" * } * ); */ result = witClient.CreateWorkItemAsync(patchDocument, tfsConnection.TfsProject, cbWorkItemType.Text).Result; } else { result = existingItem; } // test //https://github.com/microsoft/azure-devops-dotnet-samples/blob/master/ClientLibrary/Samples/WorkItemTracking/WorkItemsSample.cs //var commentsDocument = new JsonPatchDocument(); //commentsDocument.Add( // new JsonPatchOperation() // { // Operation = Operation.Add, // Path = "/fields/System.History", // Value = "test comment from client lib sample code", // } //); //WorkItem commentsREsult = witClient.UpdateWorkItemAsync(commentsDocument, result.Id.Value).Result; //var q = 1; //// add files if (chkLstBoxAttachements.GetItemChecked(0)) { var subject = _outlookCurrentMailItem.Subject.GetSafeFileSystemName() + ".msg"; string allMessage = tempFolder + subject; saveFilePaths.Add(allMessage); _outlookCurrentMailItem.SaveAs(allMessage, OlSaveAsType.olMSG); } // this is not a mistake, the first checkbox is reserved for the whole message, // and the outlook attachments weirdly is numbered from 1 for (int i = 1; i < chkLstBoxAttachements.Items.Count; i++) { if (chkLstBoxAttachements.GetItemChecked(i)) { var filename = _outlookCurrentMailItem.Attachments[i].GetFileName(); filename = filename.GetSafeFileSystemName(); string fPath = tempFolder + filename; saveFilePaths.Add(fPath); _outlookCurrentMailItem.Attachments[i].SaveAsFile(fPath); } } if ((saveFilePaths.Count > 0) || (isUpdateMode)) { List <string> uploadedAttachementUrl = new List <string>(); if (saveFilePaths.Count > 0) { JsonPatchDocument attachDocument = new JsonPatchDocument(); foreach (string fp in saveFilePaths) { AttachmentReference attachment = null; Task taskAttach = new Task(() => { attachment = witClient.CreateAttachmentAsync(fp).Result; }); taskAttach.Start(); Task.WaitAll(new[] { taskAttach }); attachDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = attachment.Url, attributes = new { name = Path.GetFileName(fp), comment = txtAttachementComment.Text } } } );; uploadedAttachementUrl.Add(attachment.Url); } WorkItem attachmentResult = witClient.UpdateWorkItemAsync(attachDocument, result.Id.Value).Result; } //Prepare new body text bodyText.Append("<br/>"); foreach (string ustr in uploadedAttachementUrl) { var extension = System.IO.Path.GetExtension(ustr).ToLower(); if (imageExtensions.Contains(extension)) { bodyText.Append("<br/>"); bodyText.Append(String.Format("<img src =\"{0}\"/> ", ustr)); } } if (isNewMode) { JsonPatchDocument updatedItemBody = new JsonPatchDocument(); updatedItemBody.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.ReproSteps", Value = bodyText.ToString() } ); updatedItemBody.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Description", Value = bodyText.ToString() } ); WorkItem finalResult = witClient.UpdateWorkItemAsync(updatedItemBody, result.Id.Value).Result; var url = GetUrlFromWorkItem(finalResult); System.Windows.Forms.Clipboard.SetText(url, TextDataFormat.Text); MessageBox.Show("Item Created Successfully, with ID: " + finalResult.Id + "\r\n\r\n" + url, "Item Created With Attachment, URL copied to clipboard"); } else if (isUpdateMode) { JsonPatchDocument updatedItemBody = new JsonPatchDocument(); updatedItemBody.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.History", Value = bodyText.ToString() } ); WorkItem finalResult = witClient.UpdateWorkItemAsync(updatedItemBody, result.Id.Value).Result; var url = GetUrlFromWorkItem(finalResult); System.Windows.Forms.Clipboard.SetText(url, TextDataFormat.Text); if (!skipSaveSuccessDialog) { MessageBox.Show("Item updated Successfully, with ID: " + finalResult.Id + "\r\n\r\n" + url, "Item Updated, URL copied to clipboard"); } } } else { var url = GetUrlFromWorkItem(result); System.Windows.Forms.Clipboard.SetText(url, TextDataFormat.Text); if (!skipSaveSuccessDialog) { MessageBox.Show("Item Created Successfully, with ID: " + result.Id + "\r\n\r\n" + url, "Item Created, URL copied to clipboard"); } } } catch (System.Exception ex) { MessageBox.Show(ex.Message); } finally { try { foreach (string fp in saveFilePaths) { if (File.Exists(fp)) { File.Delete(fp); } } } catch (System.Exception ex) { //TODO: silently log? } CheckAndEnableControls(); } }
private static async Task CreateWorkItem( WorkItemTrackingHttpClient client, string type, Issue jira, string parentKey, string title, string description, string state, params JsonPatchOperation[] fields) { // Short-circuit if we've already processed this item. if (Migrated.ContainsKey(jira.Key.Value)) { return; } var vsts = new JsonPatchDocument { new JsonPatchOperation { Path = "/fields/System.State", Value = state }, new JsonPatchOperation { Path = "/fields/System.CreatedBy", Value = ResolveUser(jira.Reporter) }, new JsonPatchOperation { Path = "/fields/System.CreatedDate", Value = jira.Created.Value.ToUniversalTime() }, new JsonPatchOperation { Path = "/fields/System.ChangedBy", Value = ResolveUser(jira.Reporter) }, new JsonPatchOperation { Path = "/fields/System.ChangedDate", Value = jira.Created.Value.ToUniversalTime() }, new JsonPatchOperation { Path = "/fields/System.Title", Value = title }, new JsonPatchOperation { Path = "/fields/System.Description", Value = description }, new JsonPatchOperation { Path = "/fields/Microsoft.VSTS.Common.Priority", Value = ResolvePriority(jira.Priority) }, new JsonPatchOperation { Path = "/fields/Microsoft.VSTS.Common.ClosedDate", Value = jira.ResolutionDate }, new JsonPatchOperation { Path = "/fields/Microsoft.VSTS.Scheduling.FinishDate", Value = jira.ResolutionDate }, new JsonPatchOperation { Path = "/fields/Microsoft.VSTS.Common.ResolvedDate", Value = jira.ResolutionDate }, new JsonPatchOperation { Path = "/fields/Microsoft.VSTS.Common.ResolvedReason", Value = jira.Resolution?.Name }, new JsonPatchOperation { Path = "/fields/System.IterationPath", Value = ResolveIteration(jira.CustomFields["Sprint"]?.Values, config["AzureDevOps:Project"]) }, new JsonPatchOperation { Path = "/fields/Microsoft.VSTS.Scheduling.StoryPoints", Value = jira.CustomFields["Story Points"]?.Values[0] }, new JsonPatchOperation { Path = "/fields/Microsoft.VSTS.Scheduling.Effort", Value = jira.CustomFields["Story Points"]?.Values[0] }, new JsonPatchOperation { Path = "/fields/System.AreaPath", Value = ResolveAreaPath(jira.CustomFields["DC Team"]?.Values[0], config["AzureDevOps:Project"]) }, new JsonPatchOperation { Path = "/fields/System.AssignedTo", Value = ResolveUser(jira.Assignee) } } ; if (parentKey != null) { vsts.Add(new JsonPatchOperation { Path = "/relations/-", Value = new WorkItemRelation { Rel = "System.LinkTypes.Hierarchy-Reverse", Url = $"https://{config["AzureDevOps:Url"]}/_apis/wit/workItems/{Migrated[parentKey]}" } }); } if (jira.Labels.Any()) { vsts.Add(new JsonPatchOperation { Path = "/fields/System.Tags", Value = jira.Labels.Aggregate("", (l, r) => $"{l};{r}").Trim(';', ' ') }); } foreach (var attachment in await jira.GetAttachmentsAsync()) { var path = Path.GetTempFileName(); attachment.Download(path); await using var stream = new MemoryStream(await File.ReadAllBytesAsync(path)); var uploaded = await client.CreateAttachmentAsync(stream, config["AzureDevOps:Project"], fileName : attachment.FileName); vsts.Add(new JsonPatchOperation { Path = "/relations/-", Value = new WorkItemRelation { Rel = "AttachedFile", Url = uploaded.Url } }); File.Delete(path); } var all = vsts.Concat(fields) .Where(p => p.Value != null) .ToList(); vsts = new JsonPatchDocument(); vsts.AddRange(all); var workItem = await client.CreateWorkItemAsync(vsts, config["AzureDevOps:Project"], type, bypassRules : true); AddMigrated(jira.Key.Value, workItem.Id.Value); await CreateComments(client, workItem.Id.Value, jira); Console.WriteLine($"Added {type}: {jira.Key}{title}"); }
static void Main(string[] args) { // provide collection url of tfs var collectionUri = "http://*****:*****@"C: \Users\pankagar\Downloads\Canvas.png", FileMode.Open, FileAccess.Read); var attachmentObject = _witClient.CreateAttachmentAsync(uploadStream, "Canvas.png", "Simple").Result; // create a patchdocument object JsonPatchDocument json = new JsonPatchDocument(); // create a new patch operation for title field JsonPatchOperation patchDocument1 = new JsonPatchOperation(); patchDocument1.Operation = Operation.Add; patchDocument1.Path = "/fields/System.Title"; patchDocument1.Value = "Testing Rest Api"; json.Add(patchDocument1); // create a new patch operation for priority field JsonPatchOperation patchDocument2 = new JsonPatchOperation(); patchDocument2.Operation = Operation.Add; patchDocument2.Path = "/fields/Microsoft.VSTS.Common.Priority"; patchDocument2.Value = "2"; json.Add(patchDocument2); // create testbasehelper object TestBaseHelper helper = new TestBaseHelper(); // create testbase object to utilize teststep helpers ITestBase tb = helper.Create(); // create 2 test steps ts1 and ts2 ITestStep ts1 = tb.CreateTestStep(); ITestStep ts2 = tb.CreateTestStep(); ts1.Title = "title -> title1"; ts2.Title = "title -> title2"; ts1.ExpectedResult = "expected1"; ts2.ExpectedResult = "expected2"; ts1.Description = "description1"; ts2.Description = "description2"; // adding attachment to step1 ts1.Attachments.Add(ts1.CreateAttachment(attachmentObject.Url, "CanvasImage")); // add your steps actions to testbase object tb.Actions.Add(ts1); tb.Actions.Add(ts2); // update json based on all actions (including teststeps and teststep attachemnts) json = tb.SaveActions(json); var xml = ""; /* getting xml for teststeps * xml = tb.GenerateXmlFromActions(); */ // create Test Case work item using all test steps: ts1 and ts2 var testCaseObject = _witClient.CreateWorkItemAsync(json, projectName, "Test Case").Result; int testCaseId = Convert.ToInt32(testCaseObject.Id); // get Test Case using all relations testCaseObject = _witClient.GetWorkItemAsync(testCaseId, null, null, WorkItemExpand.Relations).Result; // update Test Case if (testCaseObject.Fields.ContainsKey("Microsoft.VSTS.TCM.Steps")) { xml = testCaseObject.Fields["Microsoft.VSTS.TCM.Steps"].ToString(); tb = helper.Create(); // create tcmattachemntlink object from workitem relation, teststep helper will use this IList <TestAttachmentLink> tcmlinks = new List <TestAttachmentLink>(); foreach (WorkItemRelation rel in testCaseObject.Relations) { TestAttachmentLink tcmlink = new TestAttachmentLink(); tcmlink.Url = rel.Url; tcmlink.Attributes = rel.Attributes; tcmlink.Rel = rel.Rel; tcmlinks.Add(tcmlink); } // load teststep xml and attachemnt links tb.LoadActions(xml, tcmlinks); ITestStep ts; //updating 1st test step ts = (ITestStep)tb.Actions[0]; ts.Title = "title -> title11"; ts.ExpectedResult = "expected11"; //removing 2ns test step tb.Actions.RemoveAt(1); //adding new test step ITestStep ts3 = tb.CreateTestStep(); ts3.Title = "title -> title3"; ts3.ExpectedResult = "expected3"; tb.Actions.Add(ts3); JsonPatchDocument json2 = new JsonPatchDocument(); // update json based on all new changes ( updated step xml and attachments) json2 = tb.SaveActions(json2); // update testcase wit using new json testCaseObject = _witClient.UpdateWorkItemAsync(json2, testCaseId).Result; /* Note : If you want to remove attachment then create new patchOperation, details are available here : * https://www.visualstudio.com/en-us/docs/integrate/api/wit/work-items#remove-an-attachment */ } }
public override async Task <IEnumerable <WorkItemModel> > FileWorkItems(IEnumerable <WorkItemModel> workItemModels) { foreach (WorkItemModel workItemModel in workItemModels) { //TODO: Provide helper that generates useful attachment name from filed bug. // This helper should be common to both ADO and GH filers. The implementation // should work like this: the filer should prefix the proposed file name with // the account and project and number of the filed work item. So an attachment // name of Scan.sarif would be converted tO // // MyAcct_MyProject_WorkItem1000_Scan.sarif // // The GH filer may prefer to use 'issue' instead: // // myowner_my-repo_Issue1000_Scan.sarif // // The common helper should preserve casing choices in the account/owner and // project/repo information that's provided. // // Obviously, this proposal requires a change below to first file the bug, // then compute the file name and add the attachment. // // https://github.com/microsoft/sarif-sdk/issues/1753 AttachmentReference attachmentReference = null; string attachmentText = workItemModel.Attachment?.Text; if (!string.IsNullOrEmpty(attachmentText)) { using (var stream = new MemoryStream()) using (var writer = new StreamWriter(stream)) { writer.Write(attachmentText); writer.Flush(); stream.Position = 0; try { attachmentReference = await _witClient.CreateAttachmentAsync(stream, fileName : workItemModel.Attachment.Name); } catch { // TBD error handling throw; } } } var patchDocument = new JsonPatchDocument { new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{AzureDevOpsFieldNames.Title}", Value = workItemModel.Title }, new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{AzureDevOpsFieldNames.Area}", Value = workItemModel.Area ?? workItemModel.RepositoryOrProject }, new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{AzureDevOpsFieldNames.Tags}", Value = string.Join(",", workItemModel.LabelsOrTags) } }; if (workItemModel.CustomFields != null) { foreach (KeyValuePair <string, string> customField in workItemModel.CustomFields) { patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{customField.Key}", Value = customField.Value }); } } if (attachmentReference != null) { patchDocument.Add( new JsonPatchOperation { Operation = Operation.Add, Path = $"/relations/-", Value = new { rel = "AttachedFile", attachmentReference.Url } }); } WorkItem workItem = null; try { // TODO: Make work item kind configurable for Azure DevOps filer // // https://github.com/microsoft/sarif-sdk/issues/1770 string workItemKind = "Bug"; Console.Write($"Creating work item: {workItemModel.Title}"); workItem = await _witClient.CreateWorkItemAsync(patchDocument, project : this.ProjectOrRepository, workItemKind); Console.WriteLine($": {workItem.Id}: DONE"); } catch (Exception e) { Console.Error.WriteLine(e); if (patchDocument != null) { string patchJson = JsonConvert.SerializeObject(patchDocument, Formatting.Indented); Console.Error.WriteLine(patchJson); } continue; } const string HTML = "html"; workItemModel.HtmlUri = new Uri(((ReferenceLink)workItem.Links.Links[HTML]).Href, UriKind.Absolute); // TODO: ADO work item filer should populate the raw URI (in addition to HtmlUri) // // https://github.com/microsoft/sarif-sdk/issues/1773 } return(workItemModels); }
public async Task <IActionResult> UploadFilesAjax() { try { CurrentUser cUser = new CurrentUser(HttpContext, _configuration); var uriLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_Uri"); Uri accountUri = new Uri(uriLookup.Lvalue); var tokenLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_AccessToken"); String personalAccessToken = tokenLookup.Lvalue; var projectLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_Project"); string Project = projectLookup.Lvalue; // Create a connection to the account VssConnection connection = new VssConnection(accountUri, new VssBasicCredential(string.Empty, personalAccessToken)); // Get an instance of the work item tracking client WorkItemTrackingHttpClient witClient = connection.GetClient <WorkItemTrackingHttpClient>(); JsonPatchDocument patchDocument = new JsonPatchDocument(); var files = Request.Form.Files; var customHeaders = Request.Headers; StringValues taskId = ""; if (customHeaders.ContainsKey("taskId")) { customHeaders.TryGetValue("taskId", out taskId); } foreach (var file in files) { ContentDispositionHeaderValue.TryParse(file.ContentDisposition, out ContentDispositionHeaderValue parsedContentDisposition); var fileName = parsedContentDisposition.Name.TrimStart('\"').TrimEnd('\"'); Stream ms = file.OpenReadStream(); AttachmentReference attachment = witClient.CreateAttachmentAsync(ms, Project, fileName, null, null, null).Result; patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = attachment.Url, attributes = new { comment = "Emaintenance Attachment by " + cUser.Email } } }); } WorkItem result = witClient.UpdateWorkItemAsync(patchDocument, Int32.Parse(taskId)).Result; return(Ok(result)); } catch (Exception ex) { throw ex; } }
private async Task CreateWorkItem(string type, Issue jiraIssue, string parentKey, string title, string description, string state, params JsonPatchOperation[] fields) { var patchDocument = new JsonPatchDocument { new JsonPatchOperation { Path = "/fields/System.State", Value = state }, new JsonPatchOperation { Path = "/fields/System.CreatedBy", Value = MapUser(jiraIssue.ReporterUser.DisplayName) }, new JsonPatchOperation { Path = "/fields/System.CreatedDate", Value = jiraIssue.Created.Value.ToUniversalTime() }, new JsonPatchOperation { Path = "/fields/System.ChangedBy", Value = MapUser(jiraIssue.ReporterUser.DisplayName) }, new JsonPatchOperation { Path = "/fields/System.ChangedDate", Value = jiraIssue.Created.Value.ToUniversalTime() }, new JsonPatchOperation { Path = "/fields/System.Title", Value = title }, new JsonPatchOperation { Path = "/fields/System.Description", Value = description }, new JsonPatchOperation { Path = "/fields/Custom.JiraID", Value = jiraIssue.Key.Value }, new JsonPatchOperation { Path = "/fields/Microsoft.VSTS.Common.Priority", Value = MapPriority(jiraIssue.Priority) } }; if (jiraIssue.AdditionalFields["customfield_10301"] != null) { patchDocument.Add(new JsonPatchOperation { Path = "/fields/Custom.TestURL", Value = jiraIssue.AdditionalFields["customfield_10301"]?.ToString() }); } if (parentKey != null) { patchDocument.Add(new JsonPatchOperation { Path = "/relations/-", Value = new WorkItemRelation { Rel = "System.LinkTypes.Hierarchy-Reverse", Url = $"https://ciappdev.visualstudio.com/_apis/wit/workItems/{parentKey}" } }); } if (jiraIssue.AssigneeUser != null) { patchDocument.Add(new JsonPatchOperation { Path = "/fields/System.AssignedTo", Value = MapUser(jiraIssue.AssigneeUser.DisplayName) }); } patchDocument.Add(new JsonPatchOperation { Path = "/fields/System.Tags", Value = _jiraProjectAbbrev }); var attachments = await _jiraClient.Issues.GetAttachmentsAsync(jiraIssue.JiraIdentifier).ConfigureAwait(false); if (attachments != null) { foreach (var attachment in attachments) { var bytes = attachment.DownloadData(); using var stream = new MemoryStream(bytes); var uploaded = await _witClient.CreateAttachmentAsync(stream, _devOpsProjectName, fileName : attachment.FileName).ConfigureAwait(false); patchDocument.Add(new JsonPatchOperation { Path = "/relations/-", Value = new WorkItemRelation { Rel = "AttachedFile", Url = uploaded.Url } }); } } var all = patchDocument.Concat(fields).Where(p => p.Value != null).ToList(); patchDocument = new JsonPatchDocument(); patchDocument.AddRange(all); try { var workItem = await _witClient.CreateWorkItemAsync(patchDocument, _devOpsProjectName, type, bypassRules : true).ConfigureAwait(false); await CreateComments(workItem.Id.Value, jiraIssue).ConfigureAwait(false); Console.WriteLine($"Added {type}: {jiraIssue.Key} {title}"); } catch (Exception ex) { Console.WriteLine(ex.Message); } }
public static async Task <JsonPatchDocument> GetChangesAsync(this Item item, QueryDb queryDb, WorkItemTrackingHttpClient itemClient, PodioAPI.Podio podio, PodioApp app, bool ignoreRequirements, string currentIterationName) { var patchDocument = new JsonPatchDocument(); var podioType = item.GetPodioType(app.PodioTypeExternalId); var appId = item.App.AppId; var mappings = queryDb.TypeMappings .Include(x => x.FieldMappings) .ThenInclude(x => x.CategoryMappings) .Where(x => x.PodioType == podioType) .SelectMany(x => x.FieldMappings) .ToList(); if (!ignoreRequirements) { foreach (var categoryField in mappings.Where(x => x.FieldType == FieldType.Category)) { var requiredValue = categoryField.CategoryMappings.SingleOrDefault(x => x.Required); if (requiredValue != null) { var field = item.Fields .SingleOrDefault(x => x.ExternalId == categoryField.PodioFieldName); if (field == null) { throw new ArgumentException("Rules dictate this item should not be synced"); } var podioValueField = field.Values.ToObject <PodioValue[]>(); var fieldValue = podioValueField.FirstOrDefault()?.Value?.Text; if (!requiredValue.PodioValue.Equals(fieldValue, StringComparison.InvariantCultureIgnoreCase)) { throw new ArgumentException("Rules dictate this item should not be synced"); } } } } var revision = queryDb.Links.SingleOrDefault(x => x.PodioId == item.ItemId)?.PodioRevision; var fieldIds = revision.HasValue ? (await podio.ItemService.GetItemRevisionDifference(item.ItemId, revision.Value, item.CurrentRevision.Revision)).Select(x => x.ExternalId) : item.Fields.Select(x => x.ExternalId); foreach (var field in item.Fields.Where(x => fieldIds.Contains(x.ExternalId))) { var mapping = mappings.SingleOrDefault(x => x.PodioFieldName == field.ExternalId); if (mapping == null || (string.IsNullOrWhiteSpace(mapping.AzureFieldName) && !(mapping.FieldType == FieldType.Image || mapping.FieldType == FieldType.User))) { continue; } switch (mapping.FieldType) { case FieldType.Image: foreach (var file in item.Field <ImageItemField>(field.ExternalId)?.Images.ToList()) { FileResponse fileResponse = await podio.FileService.DownloadFile(file); using (var stream = new MemoryStream(fileResponse.FileContents)) { var imgReference = await itemClient.CreateAttachmentAsync(stream); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = imgReference.Url, attributes = new { name = file.Name } } } ); } } break; case FieldType.User: var userField = item.Field <ContactItemField>(field.ExternalId); var contact = userField.Contacts.FirstOrDefault(); string commentText = ""; if (!queryDb.Links.Any(x => x.PodioId == item.ItemId)) { commentText = $"Created by {contact.Name}</br>Podio url: <a href=\"{item.Link}\" target=\"_blank\">{item.Link}</a>"; patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = $"/fields/System.History", Value = commentText } ); } break; case FieldType.Category: var categoryValue = field.Values.ToObject <PodioValue[]>(); var mappedValue = mapping.CategoryMappings.FirstOrDefault(x => x.PodioValue == categoryValue[0].Value.Text)?.AzureValue; patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = $"/fields/{mapping.AzureFieldName}", Value = mappedValue } ); break; case FieldType.Boolean: break; case FieldType.Int: var numberValue = field.Values.First().Value <string>("value"); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = $"/fields/{mapping.AzureFieldName}", Value = numberValue } ); break; case FieldType.String: var stringValue = field.Values.First().Value <string>("value"); var existing = patchDocument.SingleOrDefault(x => x.Path == $"/fields/{mapping.AzureFieldName}"); if (existing != null) { existing.Value += mapping.PrefixValue + stringValue; } else { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = $"/fields/{mapping.AzureFieldName}", Value = mapping.PrefixValue + stringValue } ); } break; case FieldType.Date: var dateField = item.Field <DateItemField>(field.ExternalId); var dateValue = dateField.Start.HasValue ? dateField.Start.Value : dateField.End.HasValue ? dateField.End.Value : DateTime.UtcNow; patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = $"/fields/{mapping.AzureFieldName}", Value = dateValue } ); break; } } // TODO should only attach files if they are not allready attached to Azure item // Handle files foreach (var file in item.Files) { var podioFile = await podio.FileService.DownloadFile(file); using (var stream = new MemoryStream(podioFile.FileContents)) { var fileReference = await itemClient.CreateAttachmentAsync(stream); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = fileReference.Url, attributes = new { name = file.Name } } } ); } } // Set to current iteration if it's a bug and it has priority 1 if (patchDocument.Any(x => x.Path == "/fields/Microsoft.VSTS.Common.Priority" && x.Value.ToString() == "1") && string.Equals(item.GetAzureType(app), "bug", StringComparison.CurrentCultureIgnoreCase)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = $"/fields/System.IterationPath", Value = currentIterationName }); } return(patchDocument); }
public async Task <IActionResult> UpdateWorkItem([FromBody] WorkTask wt) { CurrentUser cUser = new CurrentUser(HttpContext, _configuration); var uriLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_Uri"); Uri accountUri = new Uri(uriLookup.Lvalue); var tokenLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_AccessToken"); String personalAccessToken = tokenLookup.Lvalue; var projectLookup = await lookupRepo.GetLookupConfigByName(0, "Y", "DevOps_Project"); wt.Project = projectLookup.Lvalue; // Create a connection to the account VssConnection connection = new VssConnection(accountUri, new VssBasicCredential(string.Empty, personalAccessToken)); // Get an instance of the work item tracking client WorkItemTrackingHttpClient witClient = connection.GetClient <WorkItemTrackingHttpClient>(); JsonPatchDocument patchDocument = new JsonPatchDocument { new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.History", Value = wt.Comment + "<p>By : " + cUser.Email + "</p>" } }; if (wt.Priority != string.Empty && wt.Priority != null) { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Replace, Path = "/fields/Microsoft.VSTS.Common.Priority", Value = wt.Priority }); } try { if (wt.ScreenShot != string.Empty && wt.ScreenShot != null) { string convert = wt.ScreenShot.Replace("data:image/png;base64,", String.Empty); var stream = new MemoryStream(Convert.FromBase64String(convert)); AttachmentReference attachment = witClient.CreateAttachmentAsync(stream, wt.Project, "ScreenShot.jpg", null, null, null).Result; patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = attachment.Url, attributes = new { comment = "Emaintenance Attachment by " + cUser.Email } } }); } WorkItem result = witClient.UpdateWorkItemAsync(patchDocument, Int32.Parse(wt.Id)).Result; return(Ok(result)); } catch (AggregateException ex) { throw ex; } }
/// <summary> /// Creates a new bugitem with the given <paramref name="details"/> and the <paramref name="pictureOfBug"/> in the repro steps /// </summary> public async Task CreateBug(BugDetails details, ImageData pictureOfBug) { const string urlEncodedFilename = "bug-shooter-screenshot.jpeg"; var bitmap = (Bitmap)pictureOfBug.MergedImage; AttachmentReference attachment; using (var stream = ConvertBitmapToMaxQualityJpeg(bitmap)) { attachment = await _workItemClient.CreateAttachmentAsync(stream, urlEncodedFilename, uploadType : "Simple", areaPath : null, userState : null, cancellationToken : default); } string reproAsHtml = details.ReproSteps.Replace("\n", "<br />"); JsonPatchDocument doc = new JsonPatchDocument { new JsonPatchOperation { Path = TitleFieldPath, Operation = Operation.Add, Value = details.Title }, new JsonPatchOperation { Path = ReproStepsFieldPath, Operation = Operation.Add, Value = $"{reproAsHtml}<br /><img src='{attachment.Url}' />" }, new JsonPatchOperation { Path = RelationFieldPath, Operation = Operation.Add, Value = new { rel = "AttachedFile", url = attachment.Url } } }; if (!string.IsNullOrWhiteSpace(_output.IterationName)) { doc.Add(new JsonPatchOperation { Path = IterationFieldPath, Operation = Operation.Add, Value = _output.IterationName }); } if (!string.IsNullOrEmpty(_output.BuildDefinitionName) && !string.IsNullOrEmpty(details.Build)) { doc.Add(new JsonPatchOperation { Path = FoundInBuildFieldPath, Operation = Operation.Add, Value = $"{details.Build}" }); } await _workItemClient.CreateWorkItemAsync(doc, _output.ProjectName, WorkItemType); }