public void AddToListInListInvalidPositionTooLarge() { var doc = new SimpleDTOWithNestedDTO() { ListOfSimpleDTO = new List<SimpleDTO>() { new SimpleDTO() { IntegerList = new List<int>() { 1, 2, 3 } } } }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("ListOfSimpleDTO/20/IntegerList/0", 4); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); var exception = Assert.Throws<JsonPatchException>(() => { deserialized.ApplyTo(doc); }); Assert.Equal( "The property at path '/ListOfSimpleDTO/20/IntegerList/0' could not be added.", exception.Message); }
public void Add_InvalidPath_ThrowsJsonPatchParseException() { //Arrange var patchDocument = new JsonPatchDocument<SimpleEntity>(); //Act patchDocument.Add("FooMissing", "bar"); }
public async Task CreateAServiceOnAkamai_UsingDefaults() { Trace.WriteLine("Looking for a CDN flavor provided by Akamai..."); var flavors = await _cdnService.ListFlavorsAsync(); var flavor = flavors.FirstOrDefault(x => x.Providers.Any(p => string.Equals(p.Name, "Akamai", StringComparison.OrdinalIgnoreCase))); Assert.NotNull(flavor); var akamaiFlavor = flavor.Id; Trace.WriteLine(string.Format("Found the {0} flavor", akamaiFlavor)); Trace.WriteLine("Creating a CDN service using defaults for anything I can omit..."); var domains = new[] {new ServiceDomain("mirror.example.com")}; var origins = new[] {new ServiceOrigin("example.com")}; var serviceDefinition = new ServiceDefinition("ci-test", akamaiFlavor, domains, origins); var serviceId = await _cdnService.CreateServiceAsync(serviceDefinition); Trace.WriteLine(string.Format("Service was created: {0}", serviceId)); try { Trace.WriteLine("Waiting for the service to be deployed..."); var service = await _cdnService.WaitForServiceDeployedAsync(serviceId, progress: new Progress<bool>(x => Trace.WriteLine("..."))); Trace.WriteLine("Verifying service matches the requested definition..."); Assert.Equal("ci-test", service.Name); Assert.Equal(serviceDefinition.FlavorId, service.FlavorId); Assert.Equal(serviceDefinition.Origins.Count, service.Origins.Count()); Assert.Equal(serviceDefinition.Origins.First().Origin, service.Origins.First().Origin); Assert.Equal(serviceDefinition.Domains.Count, service.Domains.Count()); Assert.Equal(serviceDefinition.Domains.First().Domain, service.Domains.First().Domain); Trace.WriteLine("Updating the service..."); var patch = new JsonPatchDocument<ServiceDefinition>(); patch.Replace(x => x.Name, "ci-test2"); var intranetOnly = new ServiceRestriction("intranet", new[] {new ServiceRestrictionRule("intranet", "intranet.example.com")}); patch.Add(x => x.Restrictions, intranetOnly, 0); await _cdnService.UpdateServiceAsync(serviceId, patch); Trace.WriteLine("Waiting for the service changes to be deployed..."); service = await _cdnService.WaitForServiceDeployedAsync(serviceId, progress: new Progress<bool>(x => Trace.WriteLine("..."))); Trace.WriteLine("Verifying service matches updated definition..."); Assert.Equal("ci-test2", service.Name); Assert.Equal(JsonConvert.SerializeObject(intranetOnly), JsonConvert.SerializeObject(service.Restrictions.First())); Trace.WriteLine("Purging all assets on service"); await _cdnService.PurgeCachedAssetsAsync(serviceId); } finally { Trace.WriteLine("Cleaning up any test data..."); Trace.WriteLine("Removing the service..."); _cdnService.DeleteService(serviceId); _cdnService.WaitForServiceDeleted(serviceId); Trace.WriteLine("The service was cleaned up sucessfully."); } }
public void InvalidPathWithDotShouldThrowException() { JsonPatchDocument patchDoc = new JsonPatchDocument(); var exception = Assert.Throws<JsonPatchException>(() => { patchDoc.Add("NewInt.Test", 1); }); Assert.Equal( "The provided string 'NewInt.Test' is an invalid path.", exception.Message); }
public void Add_ValidPath_OperationAdded() { //Arrange var patchDocument = new JsonPatchDocument<SimpleEntity>(); //Act patchDocument.Add("Foo", "bar"); //Assert Assert.AreEqual(1, patchDocument.Operations.Count); Assert.AreEqual(JsonPatchOperationType.add, patchDocument.Operations.Single().Operation); }
public void AddToList() { var doc = new SimpleDTO() { IntegerList = new List<int>() { 1, 2, 3 } }; // create patch JsonPatchDocument<SimpleDTO> patchDoc = new JsonPatchDocument<SimpleDTO>(); patchDoc.Add<int>(o => o.IntegerList, 4, 0); patchDoc.ApplyTo(doc); Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.IntegerList); }
public void AddNewProperty() { dynamic obj = new ExpandoObject(); obj.Test = 1; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("NewInt", 1); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); deserialized.ApplyTo(obj); Assert.Equal(1, obj.NewInt); Assert.Equal(1, obj.Test); }
public void AddResultsShouldReplace() { var doc = new SimpleDTO() { StringProperty = "A" }; // create patch JsonPatchDocument<SimpleDTO> patchDoc = new JsonPatchDocument<SimpleDTO>(); patchDoc.Add<string>(o => o.StringProperty, "B"); patchDoc.ApplyTo(doc); var s = new Operations.Operation(); Assert.Equal("B", doc.StringProperty); }
public void AddResultsShouldReplace() { // Arrange var doc = new SimpleDTO() { StringProperty = "A" }; // create patch var patchDoc = new JsonPatchDocument<SimpleDTO>(); patchDoc.Add<string>(o => o.StringProperty, "B"); // Act patchDoc.ApplyTo(doc); // Assert Assert.Equal("B", doc.StringProperty); }
public void AddResultsShouldReplaceWithSerialization() { var doc = new SimpleDTO() { StringProperty = "A" }; // create patch JsonPatchDocument<SimpleDTO> patchDoc = new JsonPatchDocument<SimpleDTO>(); patchDoc.Add<string>(o => o.StringProperty, "B"); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument<SimpleDTO>>(serialized); deserialized.ApplyTo(doc); Assert.Equal("B", doc.StringProperty); }
public void AddToListNegativePosition() { var doc = new SimpleDTO() { IntegerList = new List<int>() { 1, 2, 3 } }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("IntegerList/-1", 4); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); var exception = Assert.Throws<JsonPatchException>(() => { deserialized.ApplyTo(doc); }); Assert.Equal( "For operation 'add' on array property at path '/IntegerList/-1', the index is negative.", exception.Message); }
public void AddNewPropertyShouldFailIfRootIsNotAnExpandoObject() { dynamic doc = new { Test = 1 }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("NewInt", 1); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); var exception = Assert.Throws<JsonPatchException>(() => { deserialized.ApplyTo(doc); }); Assert.Equal( "The property at path '/NewInt' could not be added.", exception.Message); }
public void AddToListInList() { var doc = new SimpleDTOWithNestedDTO() { ListOfSimpleDTO = new List<SimpleDTO>() { new SimpleDTO() { IntegerList = new List<int>() { 1, 2, 3 } } } }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("ListOfSimpleDTO/0/IntegerList/0", 4); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); deserialized.ApplyTo(doc); Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.ListOfSimpleDTO[0].IntegerList); }
public void AddResultsInReplaceWithSerialization() { // Arrange var doc = new SimpleDTOWithNestedDTO() { SimpleDTO = new SimpleDTO() { StringProperty = "A" } }; // create patch var patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>(); patchDoc.Add<string>(o => o.SimpleDTO.StringProperty, "B"); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument<SimpleDTOWithNestedDTO>>(serialized); // Act deserialized.ApplyTo(doc); // Assert Assert.Equal("B", doc.SimpleDTO.StringProperty); }
public void AddNewPropertyToAnonymousObjectShouldFail() { dynamic doc = new { Test = 1 }; dynamic valueToAdd = new { IntValue = 1, StringValue = "test", GuidValue = Guid.NewGuid() }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("ComplexProperty", valueToAdd); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); var exception = Assert.Throws<JsonPatchException>(() => { deserialized.ApplyTo(doc); }); Assert.Equal( "The property at path '/ComplexProperty' could not be added.", exception.Message); }
public void AddToListInvalidPositionTooSmallWithSerialization() { var doc = new SimpleDTOWithNestedDTO() { SimpleDTO = new SimpleDTO() { IntegerList = new List<int>() { 1, 2, 3 } } }; // create patch JsonPatchDocument<SimpleDTOWithNestedDTO> patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>(); patchDoc.Add<int>(o => o.SimpleDTO.IntegerList, 4, -1); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument<SimpleDTOWithNestedDTO>>(serialized); Assert.Throws<JsonPatchException>(() => { deserialized.ApplyTo(doc); }); }
public JsonPatchDocument GetPatches(V1Deployment deployment) { if (deployment is null) { throw new ArgumentNullException(nameof(deployment)); } logger.LogInformation( "Got deployment named {DeploymentName} in namespace {Namespace}.", deployment.Metadata.Name, deployment.Metadata.NamespaceProperty ?? "default"); logger.LogInformation("Got template: {}", Newtonsoft.Json.JsonConvert.SerializeObject(deployment.Spec.Template)); foreach (var annotation in deployment.Spec.Template.Metadata.Annotations) { logger.LogInformation("Got annotation {AnnotationKey}={AnnotationValue}", annotation.Key, annotation.Value); } var patches = new JsonPatchDocument(); var hasLogOverrideEnabled = false; if (deployment.Spec.Template.Metadata.Annotations.TryGetValue("opulence.dotnet.io/enablelogoverride", out var value) && string.Equals(value, bool.TrueString, StringComparison.OrdinalIgnoreCase)) { hasLogOverrideEnabled = true; } logger.LogInformation("Deployment {DeploymentName} has opulence.dotnet.io/enablelogoverride = {LogOverrideEnabled}.", deployment.Metadata.Name, hasLogOverrideEnabled); if (!hasLogOverrideEnabled) { return(patches); } var foundVolume = false; var volumes = deployment.Spec.Template.Spec?.Volumes ?? Array.Empty <V1Volume>(); for (var i = 0; i < volumes.Count; i++) { var volume = volumes[i]; if (volume.Name == VolumeName) { foundVolume = true; logger.LogInformation("Deployment already has volume {VolumeName}.", VolumeName); break; } } if (!foundVolume) { logger.LogInformation("Adding volume {VolumnName}.", VolumeName); if (volumes.Count == 0) { patches.Add("/spec/template/spec/volumes", Array.Empty <object>()); } patches.Add("/spec/template/spec/volumes/-", new V1Volume() { Name = VolumeName, ConfigMap = new V1ConfigMapVolumeSource() { Name = ConfigMapName, Optional = true, Items = new List <V1KeyToPath>() { new V1KeyToPath() { Key = $"{deployment.Metadata.Name}.json", Path = $"config.json", } }, }, }); } var containers = deployment.Spec.Template.Spec.Containers; for (var i = 0; i < containers.Count; i++) { var container = containers[i]; var foundMount = false; container.VolumeMounts ??= new List <V1VolumeMount>(); for (var j = 0; j < container.VolumeMounts.Count; i++) { var mount = container.VolumeMounts[j]; if (mount.Name == VolumeName) { foundMount = true; logger.LogInformation("Found volume mount {VolumneName} for container {ContainerName}."); break; } } if (foundMount) { continue; } logger.LogInformation("Adding volumne mount {VolumeName} to {ContainerName}.", "opulence-logoverride", container.Name); if (container.VolumeMounts.Count == 0) { patches.Add($"/spec/template/spec/containers/{i}/volumeMounts", Array.Empty <object>()); } patches.Add($"/spec/template/spec/containers/{i}/volumeMounts/-", new V1VolumeMount() { Name = "opulence-logoverride", MountPath = "/var/opulence.dotnet.io/logoverride", ReadOnlyProperty = true, }); } return(patches); }
// Returns workitem URL. public static string CreateSupportTicket(SupportTicket ticket) { VssConnection connection = new VssConnection(new Uri(collectionUri), new VssBasicCredential("", pat)); WorkItemTrackingHttpClient witClient = connection.GetClient <WorkItemTrackingHttpClient>(); 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 = "Support ticket by user: "******"/fields/System.Description", Value = ticket.Description } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/agile2.Area", Value = ticket.AreaOfProblem } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/agile2.Category", Value = ticket.CategoryOfProblem } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/agile2.UserAccount", Value = ticket.AccountUrl } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/agile2.Useremail", Value = ticket.EmailAddress } ); try { WorkItem result = witClient.CreateWorkItemAsync(patchDocument, project, "SupportTicket").Result; return(collectionUri + project + "/_workitems/edit/" + result.Id); } catch (AggregateException ex) { Console.WriteLine("Error creating the support ticket: {0}", ex.InnerException.Message); return(null); } }
public async Task <WorkItemModel> CreateWorkitem(CreateWorkItem workItem) { WorkItemModel workItemModel = new WorkItemModel(); DevOpsConnectionPool poolObj = _builderPool.Get(); try { List <Params> ListParams = new List <Params>(); //VssCredentials creds = new VssBasicCredential(string.Empty, personalaccesstoken); //VssConnection connection = new VssConnection(new Uri(c_collectionUri), creds); //ProjectHttpClient projectClient = connection.GetClient<ProjectHttpClient>(); var workItemTrackingClient = poolObj.VssConnection.GetClient <WorkItemTrackingHttpClient>(); JsonPatchDocument patchDocument = new JsonPatchDocument(); Fields field = null; string title = $"error deploying solution {workItem.crmsolutioname} in {workItem.crmorgurl}"; string bugtest = $"error deploying solution {workItem.crmsolutioname} in {workItem.crmorgurl}. \n see attachment file for full error log."; string description = $"error deploying solution {workItem.crmsolutioname} in {workItem.crmorgurl}. \n see attachment file for full error log."; switch (workItem.type.ToLower()) { case "bug": field = new Fields() { AssignedTo = workItem.assignedTo, BugText = bugtest, //workItem.bugtest, Priority = "2", Title = title, //workItem.title, Severity = "2 - High" }; ListParams.AddRange(GetBugFields(field)); break; default: //Issue,Feature,Task field = new Fields() { AssignedTo = workItem.assignedTo, Description = description, //workItem.description, Priority = "2", Title = title, Severity = "2 - High" }; ListParams.AddRange(GetTaskIssueFeatureFields(field)); break; } foreach (var item in ListParams) { patchDocument.Add( new JsonPatchOperation() { Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Path = item.Path, Value = item.Value, } ); } var workitem = await workItemTrackingClient.CreateWorkItemAsync(patchDocument, workItem.projectid, workItem.type); if (workitem != null) { workItemModel.Id = workitem.Id.Value; workItemModel.url = workitem.Url; } } catch (Exception) { } finally { _builderPool.Return(poolObj); } return(workItemModel); }
public static bool EditProduct(Product product) { var categories = _a.GetResourceAsync <List <Category> >(Api.CategoryApi).Result; bool edited = false; string uriString = $"{Api.ProductApi}/{product.Id}"; Uri patchApiForProduct = new Uri(uriString); Clear(); WriteLine( "Press Esc to when done editing. To add or remove a category add the id.".PadRight(Program.WindowWidth, '#')); WriteLine("".PadRight(Program.WindowWidth, '#')); Coordinates productPropertiesCoordinates = new Coordinates(ContentCursorPosLeft - 3, ContentCursorPosTop); int propertyAndValueSpacing = 15; product.PrintPropertiesWithValues(productPropertiesCoordinates, new PrintSettings { ChosenListProperty = "Id", PrintFlatList = true, PropertiesAndValuesSpacing = propertyAndValueSpacing, PropertiesSpacing = 2, }); OptionsPrinter(" "); PrintCategories(categories); List <string> propertyNames = productPropertiesCoordinates.SavedCoordinates.Keys.ToList(); int moveCursor = 1; bool shouldNotExit = true; ConsoleKeyInfo consoleKeyInfo; Dictionary <string, string> changes = new Dictionary <string, string>(); List <int> oldProductCategoryIds = product.Categories.Select(c => c.Id).ToList(); List <int> newProductCategoryIds = oldProductCategoryIds; bool correctKey; while (shouldNotExit) { SetCursorPosition( productPropertiesCoordinates.SavedCoordinates[propertyNames[moveCursor]].X + propertyAndValueSpacing, productPropertiesCoordinates.SavedCoordinates[propertyNames[moveCursor]].Y); do { consoleKeyInfo = ReadKey(true); correctKey = !(consoleKeyInfo.Key == ConsoleKey.UpArrow || consoleKeyInfo.Key == ConsoleKey.DownArrow || consoleKeyInfo.Key == ConsoleKey.Escape || consoleKeyInfo.Key == ConsoleKey.Enter); } while (correctKey); switch (consoleKeyInfo.Key) { case ConsoleKey.UpArrow: if (moveCursor > 1) { moveCursor--; } break; case ConsoleKey.DownArrow: if (moveCursor < propertyNames.Count - 1) { moveCursor++; } break; case ConsoleKey.Enter: string currentProperty = propertyNames[moveCursor]; if (!currentProperty.ToUpper().Contains("ID")) { if (currentProperty.ToUpper().Contains("CATEGORIES")) { EraseOldText(35); int categoryId = Convert.ToInt32(ReadLine()); if (categories.Any(ca => ca.Id == categoryId)) { if (newProductCategoryIds == oldProductCategoryIds) { newProductCategoryIds = product.Categories.Select(c => c.Id).ToList(); } if (newProductCategoryIds.Contains(categoryId)) { SetCursorPosition( productPropertiesCoordinates.SavedCoordinates["Categories"].X + propertyAndValueSpacing, productPropertiesCoordinates.SavedCoordinates["Categories"].Y); EraseOldText(35); Write("Category removed."); newProductCategoryIds.Remove(categoryId); } else { SetCursorPosition( productPropertiesCoordinates.SavedCoordinates["Categories"].X + propertyAndValueSpacing, productPropertiesCoordinates.SavedCoordinates["Categories"].Y); EraseOldText(35); Write("Category sucessfully added added."); newProductCategoryIds.Add(categoryId); } string productCategoryIdsString = string.Join(", ", newProductCategoryIds); SetCursorPosition( productPropertiesCoordinates.SavedCoordinates["Categories"].X + propertyAndValueSpacing, productPropertiesCoordinates.SavedCoordinates["Categories"].Y); EraseOldText(35); WriteLine(productCategoryIdsString); } else { SetCursorPosition( productPropertiesCoordinates.SavedCoordinates["Categories"].X + propertyAndValueSpacing, productPropertiesCoordinates.SavedCoordinates["Categories"].Y); EraseOldText(35); Write("Invalid category Id."); Thread.Sleep(1000); } } else { EraseOldText(35); string input = ReadLine(); bool added = changes.TryAdd(currentProperty, input); if (!added) { changes[currentProperty] = input; } } } break; case ConsoleKey.Escape: shouldNotExit = false; break; } SetCursorPosition( productPropertiesCoordinates.SavedCoordinates[propertyNames[moveCursor]].X + propertyAndValueSpacing, productPropertiesCoordinates.SavedCoordinates[propertyNames[moveCursor]].Y); } bool isUnchanged = changes.Count <= 0 && newProductCategoryIds == oldProductCategoryIds; if (isUnchanged) { return(edited); } bool b; JsonPatchDocument jsonPatchDocument = new JsonPatchDocument(); OptionsPrinter("Apply edits? (Y)es (N)o"); do { consoleKeyInfo = ReadKey(true); b = !(consoleKeyInfo.Key == ConsoleKey.Y || consoleKeyInfo.Key == ConsoleKey.N); } while (b); switch (consoleKeyInfo.Key) { case ConsoleKey.Y: { foreach (var change in changes) { bool canConvert = decimal.TryParse(change.Value, out var price); if (canConvert) { jsonPatchDocument.Replace(change.Key, price); } else { jsonPatchDocument.Replace(change.Key, change.Value); if (change.Key.ToUpper().Contains("NAME")) { string urlSulg = change.Value.ToLower().Replace(" ", "-"); jsonPatchDocument.Replace("UrlSlug", urlSulg); } } } foreach (var categoryId in oldProductCategoryIds) { if (!newProductCategoryIds.Contains(categoryId)) { jsonPatchDocument.Remove($"Categories/{oldProductCategoryIds.IndexOf(categoryId)}"); } } foreach (var categoryId in newProductCategoryIds) { if (!oldProductCategoryIds.Contains(categoryId)) { ProductCategory productCategory = new ProductCategory(); productCategory.CategoryId = categoryId; jsonPatchDocument.Add($"Categories/-", productCategory); } } var stringContent = new StringContent(JsonConvert.SerializeObject(jsonPatchDocument), Encoding.UTF8, "application/json"); var response = _a.PatchResourceAsync(patchApiForProduct, stringContent).Result; if (response.IsSuccessStatusCode) { Clear(); SetCursorPosition(MenuCursorPosLeft, MenuCursorPosTop); WriteLine("Changes Sucessfully applied"); Thread.Sleep(1500); edited = true; } else { Clear(); SetCursorPosition(MenuCursorPosLeft, MenuCursorPosTop); WriteLine("Something went wrong with applying the changes."); Thread.Sleep(1500); } break; } case ConsoleKey.N: Clear(); SetCursorPosition(MenuCursorPosLeft, MenuCursorPosTop); WriteLine("No changes applied"); Thread.Sleep(1500); break; } return(edited); }
private WorkItem ReplayRevisions(List <RevisionItem> revisionsToMigrate, WorkItem sourceWorkItem, WorkItem targetWorkItem, Project destProject, WorkItemStoreContext sourceStore, int current, WorkItemStoreContext targetStore) { try { var skipToFinalRevisedWorkItemType = _config.SkipToFinalRevisedWorkItemType; var last = sourceStore.GetRevision(sourceWorkItem, revisionsToMigrate.Last().Number); string finalDestType = last.Type.Name; if (skipToFinalRevisedWorkItemType && me.TypeDefinitionMaps.Items.ContainsKey(finalDestType)) { finalDestType = me.TypeDefinitionMaps.Items[finalDestType].Map(); } //If work item hasn't been created yet, create a shell if (targetWorkItem == null) { targetWorkItem = CreateWorkItem_Shell(destProject, sourceWorkItem, skipToFinalRevisedWorkItemType ? finalDestType : sourceStore.GetRevision(sourceWorkItem, revisionsToMigrate.First().Number).Type.Name); } if (_config.CollapseRevisions) { var data = revisionsToMigrate.Select(rev => sourceStore.GetRevision(sourceWorkItem, rev.Number)).Select(rev => new { rev.Id, rev.Rev, rev.RevisedDate, Fields = rev.Fields.AsDictionary() }); var fileData = JsonConvert.SerializeObject(data, new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.None }); var filePath = Path.Combine(Path.GetTempPath(), $"{sourceWorkItem.Id}_PreMigrationHistory.json"); File.WriteAllText(filePath, fileData); targetWorkItem.Attachments.Add(new Attachment(filePath, "History has been consolidated into the attached file.")); revisionsToMigrate = revisionsToMigrate.GetRange(revisionsToMigrate.Count - 1, 1); TraceWriteLine(LogEventLevel.Information, " Attached a consolidated set of {RevisionCount} revisions.", new Dictionary <string, object>() { { "RevisionCount", data.Count() } }); } foreach (var revision in revisionsToMigrate) { var currentRevisionWorkItem = sourceStore.GetRevision(sourceWorkItem, revision.Number); TraceWriteLine(LogEventLevel.Information, " Processing Revision [{RevisionNumber}]", new Dictionary <string, object>() { { "RevisionNumber", revision.Number } }); // Decide on WIT string destType = currentRevisionWorkItem.Type.Name; if (me.TypeDefinitionMaps.Items.ContainsKey(destType)) { destType = me.TypeDefinitionMaps.Items[destType].Map(); } //If the work item already exists and its type has changed, update its type. Done this way because there doesn't appear to be a way to do this through the store. if (!skipToFinalRevisedWorkItemType && targetWorkItem.Type.Name != finalDestType) { Debug.WriteLine($"Work Item type change! '{targetWorkItem.Title}': From {targetWorkItem.Type.Name} to {destType}"); var typePatch = new JsonPatchOperation() { Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Path = "/fields/System.WorkItemType", Value = destType }; var datePatch = new JsonPatchOperation() { Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Path = "/fields/System.ChangedDate", Value = currentRevisionWorkItem.Revisions[revision.Index].Fields["System.ChangedDate"].Value }; var patchDoc = new JsonPatchDocument(); patchDoc.Add(typePatch); patchDoc.Add(datePatch); _witClient.UpdateWorkItemAsync(patchDoc, targetWorkItem.Id, bypassRules: true).Wait(); } PopulateWorkItem(currentRevisionWorkItem, targetWorkItem, destType); me.FieldMaps.ApplyFieldMappings(currentRevisionWorkItem.ToWorkItemData(), targetWorkItem.ToWorkItemData()); targetWorkItem.Fields["System.ChangedBy"].Value = currentRevisionWorkItem.Revisions[revision.Index].Fields["System.ChangedBy"].Value; targetWorkItem.Fields["System.History"].Value = currentRevisionWorkItem.Revisions[revision.Index].Fields["System.History"].Value; //Debug.WriteLine("Discussion:" + currentRevisionWorkItem.Revisions[revision.Index].Fields["System.History"].Value); var fails = targetWorkItem.Validate(); foreach (Field f in fails) { TraceWriteLine(LogEventLevel.Information, "{Current} - Invalid: {CurrentRevisionWorkItemId}-{CurrentRevisionWorkItemTypeName}-{FieldReferenceName}-{SourceWorkItemTitle} Value: {FieldValue}", new Dictionary <string, object>() { { "Current", current }, { "CurrentRevisionWorkItemId", currentRevisionWorkItem.Id }, { "CurrentRevisionWorkItemTypeName", currentRevisionWorkItem.Type.Name }, { "FieldReferenceName", f.ReferenceName }, { "SourceWorkItemTitle", sourceWorkItem.Title }, { "FieldValue", f.Value } }); } //if (fails.Count > 0) // TODO Do we want to stop or continue? //{ // contextLog.Error("Unable to save revission due to the error in validation"); // break; //} targetWorkItem.Save(); TraceWriteLine(LogEventLevel.Information, " Saved TargetWorkItem {TargetWorkItemId}. Replayed revision {RevisionNumber} of {RevisionsToMigrateCount}", new Dictionary <string, object>() { { "TargetWorkItemId", targetWorkItem.Id }, { "RevisionNumber", revision.Number }, { "RevisionsToMigrateCount", revisionsToMigrate.Count } }); } if (targetWorkItem != null) { ProcessWorkItemAttachments(sourceWorkItem, targetWorkItem, false); ProcessWorkItemLinks(sourceStore, targetStore, sourceWorkItem, targetWorkItem); string reflectedUri = sourceStore.CreateReflectedWorkItemId(sourceWorkItem); if (targetWorkItem.Fields.Contains(me.Target.Config.ReflectedWorkItemIDFieldName)) { targetWorkItem.Fields[me.Target.Config.ReflectedWorkItemIDFieldName].Value = reflectedUri; } var history = new StringBuilder(); history.Append( $"This work item was migrated from a different project or organization. You can find the old version at <a href=\"{reflectedUri}\">{reflectedUri}</a>."); targetWorkItem.History = history.ToString(); this.SaveWorkItem(targetWorkItem); attachmentEnricher.CleanUpAfterSave(); TraceWriteLine(LogEventLevel.Information, "...Saved as {TargetWorkItemId}", new Dictionary <string, object> { { "TargetWorkItemId", targetWorkItem.Id } }); } } catch (Exception ex) { TraceWriteLine(LogEventLevel.Information, "...FAILED to Save"); if (targetWorkItem != null) { foreach (Field f in targetWorkItem.Fields) { TraceWriteLine(LogEventLevel.Information, "{FieldReferenceName} ({FieldName}) | {FieldValue}", new Dictionary <string, object>() { { "FieldReferenceName", f.ReferenceName }, { "FieldName", f.Name }, { "FieldValue", f.Value } }); } } Log.Error(ex.ToString(), ex); } return(targetWorkItem); }
private JsonPatchDocument BuildADOIssuePatchDocumentFromV1DefectAssetdto(V1DefectAssetDto v1DefectAssetDto) { JsonPatchDocument patchDocument = null; if (v1DefectAssetDto != null) { patchDocument = new JsonPatchDocument(); if (!string.IsNullOrEmpty(v1DefectAssetDto.ScopeName) || !string.IsNullOrEmpty(v1DefectAssetDto.ItemNumber) || !string.IsNullOrEmpty(v1DefectAssetDto.ItemName)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Title", Value = string.Format("PROJECT: {0}, ITEM NUMBER: {1}, ITEM NAME: {2}", v1DefectAssetDto.ScopeName, v1DefectAssetDto.ItemNumber, v1DefectAssetDto.ItemName) } ); } if (!string.IsNullOrEmpty(v1DefectAssetDto.EstimatedPoints)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Scheduling.StoryPoints", Value = v1DefectAssetDto.EstimatedPoints } ); } //if (!string.IsNullOrEmpty(v1DefectAssetDto.StatusName) // || !string.IsNullOrEmpty(v1DefectAssetDto.AssetState)) //{ // patchDocument.Add( // new JsonPatchOperation() // { // Operation = Operation.Add, // Path = "/fields/System.State", // Value = string.Format("STATUS NAME: {0}, ASSET STATE: {1}", v1DefectAssetDto.StatusName, v1DefectAssetDto.AssetState) // } // ); //} //if (!string.IsNullOrEmpty(v1DefectAssetDto.PriorityName)) //{ // patchDocument.Add( // new JsonPatchOperation() // { // Operation = Operation.Add, // Path = "/fields/Microsoft.VSTS.Common.Priority", // Value = v1DefectAssetDto.PriorityName // } // ); //} if (!string.IsNullOrEmpty(v1DefectAssetDto.TaggedWith)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Tags", Value = v1DefectAssetDto.TaggedWith } ); } if (!string.IsNullOrEmpty(v1DefectAssetDto.Description)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Description", Value = v1DefectAssetDto.Description } ); } if (!string.IsNullOrEmpty(v1DefectAssetDto.SourceName) || !string.IsNullOrEmpty(v1DefectAssetDto.VersionAffected) || !string.IsNullOrEmpty(v1DefectAssetDto.FoundBy) || !string.IsNullOrEmpty(v1DefectAssetDto.ProductArea)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.SystemInfo", Value = string.Format("SOURCE: {0}, VERSION AFFECTED: {1}, FOUND BY: {2}, PRODUCT AREA: {3}", v1DefectAssetDto.SourceName, v1DefectAssetDto.VersionAffected, v1DefectAssetDto.FoundBy, v1DefectAssetDto.ProductArea) } ); } if (!string.IsNullOrEmpty(v1DefectAssetDto.ReportingSite)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Build.FoundIn", Value = v1DefectAssetDto.ReportingSite } ); } //if (!string.IsNullOrEmpty(v1DefectAssetDto.Severity)) //{ // patchDocument.Add( // new JsonPatchOperation() // { // Operation = Operation.Add, // Path = "/fields/Microsoft.VSTS.Common.Severity", // Value = v1DefectAssetDto.Severity // } // ); //} if (!string.IsNullOrEmpty(v1DefectAssetDto.ResolutionDetails)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Common.ResolvedReason", Value = v1DefectAssetDto.ResolutionDetails } ); } //if (!string.IsNullOrEmpty(v1DefectAssetDto.OwnersName)) //{ // patchDocument.Add( // new JsonPatchOperation() // { // Operation = Operation.Add, // Path = "/fields/System.AssignedTo", // Value = v1DefectAssetDto.OwnersName // } // ); //} } return(patchDocument); }
public WorkItem CreateAndLinkToWorkItem() { string title = "My new work item with links"; string description = "This is a new work item that has a link also created on it."; string linkUrl = "https://integrate.visualstudio.com"; JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Title", Value = title } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Scheduling.RemainingWork", Value = "4" } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Description", Value = description } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.History", Value = "Jim has the most context around this." } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "System.LinkTypes.Hierarchy-Reverse", url = linkUrl, attributes = new { comment = "decomposition of work" } } } ); VssConnection connection = Context.Connection; WorkItemTrackingHttpClient workItemTrackingClient = connection.GetClient <WorkItemTrackingHttpClient>(); // Get the project to create the sample work item in TeamProjectReference project = ClientSampleHelpers.FindAnyProject(this.Context); WorkItem result = workItemTrackingClient.CreateWorkItemAsync(patchDocument, project.Name, "Task").Result; return(result); }
public WorkItem CreateWorkItemWithWorkItemLink(string projectName, string linkUrl) { JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Title", Value = "JavaScript implementation for Microsoft Account" } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Scheduling.RemainingWork", Value = "4" } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Description", Value = "Follow the code samples from MSDN" } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.History", Value = "Jim has the most context around this." } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "System.LinkTypes.Hierarchy-Reverse", url = linkUrl, attributes = new { comment = "decomposition of work" } } } ); VssConnection connection = new VssConnection(_uri, _credentials); WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient <WorkItemTrackingHttpClient>(); WorkItem result = workItemTrackingHttpClient.CreateWorkItemAsync(patchDocument, projectName, "Task").Result; return(result); }
/// <summary> /// Create a bug using the .NET client library /// </summary> /// <returns>Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItem</returns> public WorkItem CreateBugUsingClientLib(string subject) { Uri uri = new Uri(_uri); string personalAccessToken = _personalAccessToken; string project = _project; VssBasicCredential credentials = new VssBasicCredential("", _personalAccessToken); 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 = subject } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.ReproSteps", Value = "Our authorization logic needs to allow for users with Microsoft accounts (formerly Live Ids) - http:// msdn.microsoft.com/en-us/library/live/hh826547.aspx" } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Common.Priority", Value = "1" } ); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Common.Severity", Value = "2 - High" } ); VssConnection connection = new VssConnection(uri, credentials); WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient <WorkItemTrackingHttpClient>(); try { WorkItem result = workItemTrackingHttpClient.CreateWorkItemAsync(patchDocument, project, "Task").Result; Console.WriteLine("Bug Successfully Created: Bug #{0}", result.Id); return(result); } catch (AggregateException ex) { Console.WriteLine("Error creating bug: {0}", ex.InnerException.Message); return(null); } }
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 bool Add(string path, object value) { newDoc.Add(path, value); return(true); }
/// <summary> /// Configures the passed JSON Patch so that it yields modified when applied to original /// - Adding fields to the patch present in modified, missing from original /// - Setting fields to the patch present in modified and original with different values /// - Delete fields present in original, missing from modified through /// - IFF map field - set to nil in patch ??? /// - IFF list of maps && merge strategy - use deleteDirective for the elements ??? /// - IFF list of primitives && merge strategy - use parallel deletion list ??? /// - IFF list of maps or primitives with replace strategy (default) - set patch value to the value in modified ??? /// - Build $retainKeys directive for fields with retainKeys patch strategy ??? /// </summary> /// <param name="original">The original object</param> /// <param name="modified">The modified object</param> /// <param name="type">The type that original and modified represent (even if they are not actually that type, but a PSObject)</param> /// <param name="path">The JSON pointer to the currently inspected values</param> /// <param name="mergeStrategy">The strategy to use for patching (replace or merge with mergeKey)</param> public void CreateTwoWayPatch(object original, object modified, Type type, JsonPatchDocument patch, string path = "", MergeStrategyAttribute mergeStrategy = null, bool ignoreDeletions = false, bool ignoreAdditionsAndModifications = false) { if (type == null) { throw new ArgumentNullException(nameof(type)); } if (patch == null) { throw new ArgumentNullException(nameof(patch)); } if (path == null) { throw new ArgumentNullException(nameof(path)); } logger.LogTrace($"Path: {path}"); if (modified == null && original == null) { return; } if (modified == null && original != null) { if (!ignoreDeletions) { patch.Replace(path, modified); } return; } if (original == null && modified != null) { if (!ignoreAdditionsAndModifications) { patch.Replace(path, modified); } return; } // From this point, original and modified are known to be non-null logger.LogTrace($"Type: original {original?.GetType().Name} modified {modified?.GetType().Name} expected {type?.Name}"); // string, int, float, bool, enum, DateTime if (modified is string || type.IsValueType) { logger.LogTrace($"Is value type, comparing {original} <-> {modified}"); // Replace if changed, otherwise do nothing // We NEED to use Equals() here instead of != because the static type is object, meaning the scalar is boxed. // Since operators are resolved at compile time, this would use the == implementation for object, // while Equals() is dynamically dispatched on the real boxed type. if (!original.Equals(modified)) { patch.Replace(path, modified); } return; } // From this point, original and modified are known to be reference types if (System.Object.ReferenceEquals(original, modified)) { // Same object, short cut return; } if (modified is IList) { logger.LogTrace("Is List"); Type valueType = type.GetGenericArguments()[0]; // Handle lists // Really just casting to generic IEnumerable get access to more LINQ. It's all object anyway. IEnumerable <object> originalEnumerable = ((IList)original).Cast <object>(); IEnumerable <object> modifiedEnumerable = ((IList)modified).Cast <object>(); // Check if the list property has a strategic merge strategy attribute if (mergeStrategy != null) { if (mergeStrategy.Key != null) { logger.LogTrace("List is unordered set keyed by merge key"); // The lists are to be treated like dictionaries, keyed by Key logger.LogTrace($"Merge key: {mergeStrategy.Key}"); Func <object, object> keySelector = listElement => { PropertyInfo mergeProperty = ModelHelpers.FindJsonProperty(valueType, mergeStrategy.Key); object value = listElement.GetDynamicPropertyValue(mergeProperty.Name); if (value == null) { throw new Exception($"Merge key {mergeProperty} on type {valueType.FullName} cannot be null"); } logger.LogTrace($"Merge property value: {value}"); return(value); }; // The merge key value is *not* guaranteed to be string, // for example ContainerPortV1 has the merge key ContainerPort which is type int Dictionary <object, object> originalDict = originalEnumerable.ToDictionary(keySelector); Dictionary <object, object> modifiedDict = modifiedEnumerable.ToDictionary(keySelector); var removeOperations = new List <Action>(); int index = 0; foreach (var originalElement in originalEnumerable) { object elementKey = originalElement.GetDynamicPropertyValue(ModelHelpers.FindJsonProperty(valueType, mergeStrategy.Key).Name); string elementPath = path + "/" + index; if (!modifiedDict.ContainsKey(elementKey)) { if (!ignoreDeletions) { // Entry removed in modified // Check that the value at the given index is really the value we want to modify, // to make sure indexes were not modified on the server // Queue these up because they shift array indexes around and for simplicity we want to get the modifications add first // This makes the patch easier to reason about. removeOperations.Add(() => patch.Test(elementPath + "/" + escapeJsonPointer(mergeStrategy.Key), elementKey)); removeOperations.Add(() => patch.Remove(elementPath)); } } else { // Entry present in both, merge recursively patch.Test(elementPath + "/" + escapeJsonPointer(mergeStrategy.Key), elementKey); var countBefore = patch.Operations.Count; CreateTwoWayPatch( original: originalElement, modified: modifiedDict[elementKey], type: valueType, patch: patch, path: elementPath, ignoreDeletions: ignoreDeletions, ignoreAdditionsAndModifications: ignoreAdditionsAndModifications ); if (patch.Operations.Count == countBefore) { // Test was not needed, element was not modified patch.Operations.RemoveAt(patch.Operations.Count - 1); } } index++; } // Modifications are done, add remove operations foreach (var action in removeOperations) { action(); } if (!ignoreAdditionsAndModifications) { // Entries added in modified foreach (var modifiedEntry in modifiedDict) { if (!originalDict.ContainsKey(modifiedEntry.Key)) { // An element that was added in modified patch.Add(path + "/-", modifiedEntry.Value); } } } } else { logger.LogTrace("List is unordered set"); // Lists are to be treated like unordered sets HashSet <object> originalSet = originalEnumerable.ToHashSet(); HashSet <object> modifiedSet = modifiedEnumerable.ToHashSet(); // The index to adress the element on the server after applying every operation in the patch so far. int index = 0; foreach (var originalElement in originalEnumerable) { string elementPath = path + "/" + index; if (!modifiedSet.Contains(originalElement)) { // Deleted from modified if (!ignoreDeletions) { // When patching indexes, make sure elements didn't get moved around on the server // Can directly add them here because unordered sets do not use replace operations, // only remove and adding to the end patch.Test(elementPath, originalElement); patch.Remove(elementPath); } } // Present in both: do nothing index++; } if (!ignoreAdditionsAndModifications) { foreach (var modifiedElement in modifiedSet) { if (!originalSet.Contains(modifiedElement)) { // Added in modified patch.Add(path + "/-", modifiedElement); } } } } } else { logger.LogTrace("List is ordered list"); // List is to be treated as an ordered list, e.g. ContainerV1.Command List <object> originalList = originalEnumerable.ToList(); List <object> modifiedList = modifiedEnumerable.ToList(); var removeOperations = new List <Action>(); int index = 0; foreach (var originalElement in originalList.Take(modifiedList.Count)) { string elementPath = path + "/" + index; if (index >= modifiedList.Count) { // Not present in modified, remove if (!ignoreDeletions) { removeOperations.Add(() => patch.Test(elementPath, originalElement)); removeOperations.Add(() => patch.Remove(elementPath)); } } else { // Present in both, merge recursively // Add a test to check that indexes were not moved on the server patch.Test(elementPath, originalElement); int countBefore = patch.Operations.Count; CreateTwoWayPatch( original: originalElement, modified: modifiedList[index], type: valueType, patch: patch, path: elementPath, ignoreDeletions: ignoreDeletions, ignoreAdditionsAndModifications: ignoreAdditionsAndModifications ); if (patch.Operations.Count == countBefore) { // Test was not needed, element was not modified patch.Operations.RemoveAt(patch.Operations.Count - 1); } } index++; } // Modifications are done, register remove operations foreach (var action in removeOperations) { action(); } // Continue on modifiedList (if it's longer) to add added elements for (; index < modifiedList.Count; index++) { // Added in modifiedList object addedElement = modifiedList[index]; patch.Add(path + "/-", addedElement); } } } else if (modified is IDictionary) { logger.LogTrace("Is Dictionary"); Type valueType = type.GetGenericArguments()[1]; // Handle maps (e.g. KubeResourceV1.Annotations) IDictionary originalDict = (IDictionary)original; IDictionary modifiedDict = (IDictionary)modified; // Always merge maps foreach (DictionaryEntry originalEntry in originalDict) { string entryKey = (string)originalEntry.Key; object entryValue = (object)originalEntry.Value; string entryPath = path + "/" + escapeJsonPointer(entryKey); if (!modifiedDict.Contains(originalEntry.Key)) { if (!ignoreDeletions) { // Entry removed in modified patch.Remove(entryPath); } } else { // Entry present in both, merge recursively CreateTwoWayPatch( original: entryValue, modified: modifiedDict[originalEntry.Key], type: valueType, patch: patch, path: entryPath, ignoreDeletions: ignoreDeletions, ignoreAdditionsAndModifications: ignoreAdditionsAndModifications ); } } if (!ignoreAdditionsAndModifications) { // Entries added in modified foreach (DictionaryEntry modifiedEntry in modifiedDict) { string entryKey = (string)modifiedEntry.Key; object entryValue = (object)modifiedEntry.Value; if (!originalDict.Contains(entryKey)) { // An element that was added in modified patch.Add(path + "/" + escapeJsonPointer(entryKey), entryValue); } } } } else { logger.LogTrace("Is other object"); // resourceVersion: a string that identifies the internal version of this object that can be used by // clients to determine when objects have changed. This value MUST be treated as opaque by clients // and passed unmodified back to the server. // https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#metadata // Add this test before traversing into other properties if (type.IsSubclassOf(typeof(KubeResourceV1))) { var resourceVersion = (string)original.GetDynamicPropertyValue("Metadata")?.GetDynamicPropertyValue("ResourceVersion"); if (!String.IsNullOrEmpty(resourceVersion)) { patch.Test(path + "/metadata/resourceVersion", resourceVersion); } } // Warn if properties were passed that are not recognized to highlight mistakes foreach (var propName in modified.GetDynamicPropertyNames().Where(name => type.GetProperty(name) == null)) { logger.LogWarning($"Unknown property \"{propName}\" on {type.Name} at path \"{path}\""); } // KubeObjects, compare properties recursively foreach (PropertyInfo prop in type.GetProperties()) { logger.LogTrace($"Property {prop.Name}"); // Ignore properties that are not part of modified if (modified is PSObject psObject && psObject.Properties[prop.Name] == null) { continue; } JsonPropertyAttribute jsonAttribute = (JsonPropertyAttribute)prop.GetCustomAttribute(typeof(JsonPropertyAttribute)); string propPath = path + "/" + escapeJsonPointer(jsonAttribute.PropertyName); object originalValue = original.GetDynamicPropertyValue(prop.Name); object modifiedValue = modified.GetDynamicPropertyValue(prop.Name); if (!isPropertyUpdateable(type, prop)) { continue; } // Pass patch strategy attribute to diff function for the property we're looking at MergeStrategyAttribute attribute = (MergeStrategyAttribute)Attribute.GetCustomAttribute(prop, typeof(MergeStrategyAttribute)); CreateTwoWayPatch( original: originalValue, modified: modifiedValue, type: prop.PropertyType, patch: patch, path: propPath, mergeStrategy: attribute, ignoreDeletions: ignoreDeletions, ignoreAdditionsAndModifications: ignoreAdditionsAndModifications ); } } }
private WorkItem CreateWorkItem_ReplayRevisions(WorkItem sourceWorkItem, Project destProject, WorkItemStoreContext sourceStore, int current, WorkItemStoreContext targetStore) { WorkItem newwit = null; try { // just to make sure, we replay the events in the same order as they appeared // maybe, the Revisions collection is not sorted according to the actual Revision number var sortedRevisions = sourceWorkItem.Revisions.Cast <Revision>().Select(x => new { x.Index, Number = Convert.ToInt32(x.Fields["System.Rev"].Value) } ) .OrderBy(x => x.Number) .ToList(); Trace.WriteLine($"...Replaying {sourceWorkItem.Revisions.Count} revisions of work item {sourceWorkItem.Id}", Name); foreach (var revision in sortedRevisions) { var currentRevisionWorkItem = sourceStore.GetRevision(sourceWorkItem, revision.Number); // Decide on WIT if (me.WorkItemTypeDefinitions.ContainsKey(currentRevisionWorkItem.Type.Name)) { var destType = me.WorkItemTypeDefinitions[currentRevisionWorkItem.Type.Name].Map(currentRevisionWorkItem); //If work item hasn't been created yet, create a shell if (newwit == null) { newwit = CreateWorkItem_Shell(destProject, currentRevisionWorkItem, destType); } //If the work item already exists and its type has changed, update its type. Done this way because there doesn't appear to be a way to do this through the store. else if (newwit.Type.Name != destType) { Debug.WriteLine($"Work Item type change! '{newwit.Title}': From {newwit.Type.Name} to {destType}"); var typePatch = new JsonPatchOperation() { Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Path = "/fields/System.WorkItemType", Value = destType }; var datePatch = new JsonPatchOperation() { Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Path = "/fields/System.ChangedDate", Value = currentRevisionWorkItem.Revisions[revision.Index].Fields["System.ChangedDate"].Value }; var patchDoc = new JsonPatchDocument(); patchDoc.Add(typePatch); patchDoc.Add(datePatch); _witClient.UpdateWorkItemAsync(patchDoc, newwit.Id, bypassRules: true).Wait(); } PopulateWorkItem(currentRevisionWorkItem, newwit, destType); me.ApplyFieldMappings(currentRevisionWorkItem, newwit); newwit.Fields["System.ChangedBy"].Value = currentRevisionWorkItem.Revisions[revision.Index].Fields["System.ChangedBy"].Value; newwit.Fields["System.History"].Value = currentRevisionWorkItem.Revisions[revision.Index].Fields["System.History"].Value; Debug.WriteLine("Discussion:" + currentRevisionWorkItem.Revisions[revision.Index].Fields["System.History"].Value); var fails = newwit.Validate(); foreach (Field f in fails) { Trace.WriteLine( $"{current} - Invalid: {currentRevisionWorkItem.Id}-{currentRevisionWorkItem.Type.Name}-{f.ReferenceName}-{sourceWorkItem.Title} Value: {f.Value}", Name); } newwit.Save(); Trace.WriteLine( $" ...Saved as {newwit.Id}. Replayed revision {revision.Number} of {sourceWorkItem.Revisions.Count}", Name); } else { Trace.WriteLine(string.Format("...the WITD named {0} is not in the list provided in the configuration.json under WorkItemTypeDefinitions. Add it to the list to enable migration of this work item type.", currentRevisionWorkItem.Type.Name), Name); break; } } if (newwit != null) { string reflectedUri = sourceStore.CreateReflectedWorkItemId(sourceWorkItem); if (newwit.Fields.Contains(me.Target.Config.ReflectedWorkItemIDFieldName)) { newwit.Fields["System.ChangedBy"].Value = "Migration"; newwit.Fields[me.Target.Config.ReflectedWorkItemIDFieldName].Value = reflectedUri; } var history = new StringBuilder(); history.Append( $"This work item was migrated from a different project or organization. You can find the old version at <a href=\"{reflectedUri}\">{reflectedUri}</a>."); newwit.History = history.ToString(); newwit.Save(); Trace.WriteLine($"...Saved as {newwit.Id}", Name); if (_config.UpdateSourceReflectedId && sourceWorkItem.Fields.Contains(me.Source.Config.ReflectedWorkItemIDFieldName)) { sourceWorkItem.Fields[me.Source.Config.ReflectedWorkItemIDFieldName].Value = targetStore.CreateReflectedWorkItemId(newwit); sourceWorkItem.Save(); Trace.WriteLine($"...and Source Updated {sourceWorkItem.Id}", Name); } } } catch (Exception ex) { Trace.WriteLine("...FAILED to Save", Name); if (newwit != null) { foreach (Field f in newwit.Fields) { Trace.WriteLine($"{f.ReferenceName} ({f.Name}) | {f.Value}", Name); } } Trace.WriteLine(ex.ToString(), Name); } return(newwit); }
private static async Task CreateBugAsync(string collectionUri, string teamProjectName, string team, string url, string severity, string title, string description, string solution, string areaPath, string iterationPath, string personalAccessToken) { var connection = GetConnection(collectionUri, personalAccessToken); var witClient = connection.GetClient <WorkItemTrackingHttpClient>(); var workClient = connection.GetClient <WorkHttpClient>(); string fullTitle = severity + " - " + title + " in " + url; // Check to see if bug exists for team and is current open var wiql = new Wiql(); wiql.Query = "SELECT [System.Title] FROM WorkItems WHERE [System.TeamProject] = '" + teamProjectName + "' AND [System.WorkItemType] = 'Bug' AND [System.Title] CONTAINS '" + fullTitle + "' AND [System.State] <> 'Closed' AND [System.State] <> 'Removed' AND [System.AreaPath] UNDER '" + areaPath + "' AND [System.IterationPath] UNDER '" + iterationPath + "'"; var results = await witClient.QueryByWiqlAsync(wiql); string severityValue = severity == "High" ? "2 - High" : severity == "Low" ? "4 - Low" : "3 - Medium"; if (results.WorkItems.Count() == 0) { // create new bug var doc = new JsonPatchDocument(); doc.Add( new JsonPatchOperation() { Path = "/fields/System.Title", Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Value = fullTitle }); doc.Add( new JsonPatchOperation() { Path = "/fields/Microsoft.VSTS.TCM.ReproSteps", Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Value = description + " " + solution }); doc.Add( new JsonPatchOperation() { Path = "/fields/Microsoft.VSTS.Common.Severity", Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Value = severityValue }); doc.Add( new JsonPatchOperation() { Path = "/fields/System.AreaPath", Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Value = areaPath }); doc.Add( new JsonPatchOperation() { Path = "/fields/System.IterationPath", Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Value = iterationPath }); doc.Add( new JsonPatchOperation() { Path = "/fields/System.Tags", Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Value = "OWASP" }); var witCreated = await witClient.CreateWorkItemAsync(doc, teamProjectName, "Bug"); System.Console.WriteLine("Bug created"); } else { // if bug was created and resolved but bug was still found, move back to active var bug = results.WorkItems.First(); var bugDetail = await witClient.GetWorkItemAsync(bug.Id); string state = bugDetail.Fields.First(w => w.Key == "System.State").Value.ToString(); if (state.ToString() == "resolved") { var doc = new JsonPatchDocument(); doc.Add( new JsonPatchOperation() { Path = "/fields/System.State", Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Replace, Value = "Active" }); await witClient.UpdateWorkItemAsync(doc, bug.Id); System.Console.WriteLine("Bug is not fixed. Moving to active."); } else { System.Console.WriteLine("Bug is already active"); } } }
public AzureTestCase CreatTestCase(AzureTestCase testCase) { var uri = new Uri(_uri); var credentials = new VssBasicCredential(string.Empty, _personalAccessToken); var patchDocument = new JsonPatchDocument(); patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Title", Value = testCase.Title, }); patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Common.Priority", Value = testCase.Priority, }); if (!string.IsNullOrEmpty(testCase.AreaPath)) { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.AreaPath", Value = testCase.AreaPath, }); } if (!string.IsNullOrEmpty(testCase.IterationPath)) { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.IterationPath", Value = testCase.IterationPath, }); } if (!string.IsNullOrEmpty(testCase.Description)) { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Description", Value = testCase.Description, }); } if (!string.IsNullOrEmpty(TestStepsService.GenerateTestStepsHtml(testCase.TestSteps)) || !string.IsNullOrEmpty(testCase.TestStepsHtml)) { if (testCase.TestSteps.Any()) { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.Steps", Value = TestStepsService.GenerateTestStepsHtml(testCase.TestSteps), }); } else { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.Steps", Value = testCase.TestStepsHtml, }); } } patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.AutomatedTestName", Value = testCase.AutomatedTestName, }); patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.AutomatedTestStorage", Value = testCase.AutomatedTestStorage, }); if (!string.IsNullOrEmpty(testCase.RequirementUrl)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "System.LinkTypes.Hierarchy-Reverse", url = testCase.RequirementUrl, attributes = new Dictionary <string, object>() { { "isLocked", false }, { "name", "Parent" }, { "comment", "added automatically" }, }, }, }); } VssConnection connection = new VssConnection(uri, credentials); WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient <WorkItemTrackingHttpClient>(); try { WorkItem result = workItemTrackingHttpClient.CreateWorkItemAsync(patchDocument, _project, "Test Case").Result; return(ConvertWorkItemToAzureTestCase(result)); } catch (AggregateException ex) { Debug.WriteLine("Error creating test case: {0}", ex.InnerException.Message); return(null); } }
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); }
private WorkItem ReplayRevisions(List <RevisionItem> revisionsToMigrate, WorkItem sourceWorkItem, WorkItem targetWorkItem, Project destProject, WorkItemStoreContext sourceStore, int current, WorkItemStoreContext targetStore) { try { foreach (var revision in revisionsToMigrate) { var currentRevisionWorkItem = sourceStore.GetRevision(sourceWorkItem, revision.Number); TraceWriteLine(currentRevisionWorkItem, $" Processing Revision[{revision.Number}"); // Decide on WIT string destType = currentRevisionWorkItem.Type.Name; if (me.WorkItemTypeDefinitions.ContainsKey(destType)) { destType = me.WorkItemTypeDefinitions[destType].Map(currentRevisionWorkItem); } //If work item hasn't been created yet, create a shell if (targetWorkItem == null) { targetWorkItem = CreateWorkItem_Shell(destProject, currentRevisionWorkItem, destType); } //If the work item already exists and its type has changed, update its type. Done this way because there doesn't appear to be a way to do this through the store. else if (targetWorkItem.Type.Name != destType) { Debug.WriteLine($"Work Item type change! '{targetWorkItem.Title}': From {targetWorkItem.Type.Name} to {destType}"); var typePatch = new JsonPatchOperation() { Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Path = "/fields/System.WorkItemType", Value = destType }; var datePatch = new JsonPatchOperation() { Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Path = "/fields/System.ChangedDate", Value = currentRevisionWorkItem.Revisions[revision.Index].Fields["System.ChangedDate"].Value }; var patchDoc = new JsonPatchDocument(); patchDoc.Add(typePatch); patchDoc.Add(datePatch); _witClient.UpdateWorkItemAsync(patchDoc, targetWorkItem.Id, bypassRules: true).Wait(); } PopulateWorkItem(currentRevisionWorkItem, targetWorkItem, destType); me.ApplyFieldMappings(currentRevisionWorkItem, targetWorkItem); targetWorkItem.Fields["System.ChangedBy"].Value = currentRevisionWorkItem.Revisions[revision.Index].Fields["System.ChangedBy"].Value; targetWorkItem.Fields["System.History"].Value = currentRevisionWorkItem.Revisions[revision.Index].Fields["System.History"].Value; //Debug.WriteLine("Discussion:" + currentRevisionWorkItem.Revisions[revision.Index].Fields["System.History"].Value); var fails = targetWorkItem.Validate(); foreach (Field f in fails) { TraceWriteLine(currentRevisionWorkItem, $"{current} - Invalid: {currentRevisionWorkItem.Id}-{currentRevisionWorkItem.Type.Name}-{f.ReferenceName}-{sourceWorkItem.Title} Value: {f.Value}"); } targetWorkItem.Save(); TraceWriteLine(currentRevisionWorkItem, $" Saved TargetWorkItem {targetWorkItem.Id}. Replayed revision {revision.Number} of {currentRevisionWorkItem.Revisions.Count}"); } if (targetWorkItem != null) { ProcessWorkItemAttachments(sourceWorkItem, targetWorkItem, false); ProcessWorkItemLinks(sourceStore, targetStore, sourceWorkItem, targetWorkItem, false); string reflectedUri = sourceStore.CreateReflectedWorkItemId(sourceWorkItem); if (targetWorkItem.Fields.Contains(me.Target.Config.ReflectedWorkItemIDFieldName)) { targetWorkItem.Fields[me.Target.Config.ReflectedWorkItemIDFieldName].Value = reflectedUri; } var history = new StringBuilder(); history.Append( $"This work item was migrated from a different project or organization. You can find the old version at <a href=\"{reflectedUri}\">{reflectedUri}</a>."); targetWorkItem.History = history.ToString(); SaveWorkItem(targetWorkItem); attachmentOMatic.CleanUpAfterSave(targetWorkItem); TraceWriteLine(sourceWorkItem, $"...Saved as {targetWorkItem.Id}"); if (_config.UpdateSourceReflectedId && sourceWorkItem.Fields.Contains(me.Source.Config.ReflectedWorkItemIDFieldName)) { sourceWorkItem.Fields[me.Source.Config.ReflectedWorkItemIDFieldName].Value = targetStore.CreateReflectedWorkItemId(targetWorkItem); SaveWorkItem(sourceWorkItem); TraceWriteLine(sourceWorkItem, $"...and Source Updated {sourceWorkItem.Id}"); } } } catch (Exception ex) { TraceWriteLine(sourceWorkItem, "...FAILED to Save"); if (targetWorkItem != null) { foreach (Field f in targetWorkItem.Fields) { TraceWriteLine(sourceWorkItem, $"{f.ReferenceName} ({f.Name}) | {f.Value}"); } } TraceWriteLine(sourceWorkItem, ex.ToString()); } return(targetWorkItem); }
public void AddResultsShouldReplaceInNestedInDynamic() { dynamic doc = new ExpandoObject(); doc.Nested = new NestedDTO(); doc.Nested.DynamicProperty = new ExpandoObject(); doc.Nested.DynamicProperty.InBetweenFirst = new ExpandoObject(); doc.Nested.DynamicProperty.InBetweenFirst.InBetweenSecond = new ExpandoObject(); doc.Nested.DynamicProperty.InBetweenFirst.InBetweenSecond.StringProperty = "A"; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("/Nested/DynamicProperty/InBetweenFirst/InBetweenSecond/StringProperty", "B"); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); deserialized.ApplyTo(doc); Assert.Equal("B", doc.Nested.DynamicProperty.InBetweenFirst.InBetweenSecond.StringProperty); }
protected override WebApiWorkItem DoSetItem() { var wi = GetItem <WebApiWorkItem>(); var tpc = GetCollection(); var fields = GetParameter <Hashtable>(nameof(SetWorkItem.Fields), new Hashtable()); var bypassRules = GetParameter <bool>(nameof(SetWorkItem.BypassRules)); var boardColumn = GetParameter <string>(nameof(SetWorkItem.BoardColumn)); var boardColumnDone = GetParameter <bool>(nameof(SetWorkItem.BoardColumnDone)); var boardLane = GetParameter <string>(nameof(SetWorkItem.BoardLane)); foreach (var argName in _parameterMap.Keys.Where(f => HasParameter(f) && !fields.ContainsKey(f))) { fields.Add(_parameterMap[argName], GetParameter <object>(argName)); } if (fields.Count > 0) { var patch = new JsonPatchDocument() { new JsonPatchOperation() { Operation = Operation.Test, Path = "/rev", Value = wi.Rev } }; foreach (DictionaryEntry field in fields) { patch.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = $"/fields/{field.Key}", Value = field.Value is IEnumerable <string>? string.Join(";", (IEnumerable <string>)field.Value) : field.Value }); } var client = GetClient <WorkItemTrackingHttpClient>(); wi = client.UpdateWorkItemAsync(patch, (int)wi.Id, false, bypassRules) .GetResult("Error updating work item"); } // Change board status if (HasParameter(nameof(SetWorkItem.BoardColumn)) || HasParameter(nameof(SetWorkItem.BoardColumnDone)) || HasParameter(nameof(SetWorkItem.BoardLane))) { var patch = new JsonPatchDocument() { new JsonPatchOperation() { Operation = Operation.Test, Path = "/rev", Value = wi.Rev } }; var(_, tp, t) = GetCollectionProjectAndTeam(); var board = FindBoard((string)wi.Fields["System.WorkItemType"], tpc, tp, t); if (HasParameter(nameof(SetWorkItem.BoardColumn))) { patch.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = $"/fields/{board.Fields.ColumnField.ReferenceName}", Value = GetParameter <string>(nameof(SetWorkItem.BoardColumn)) }); } if (HasParameter(nameof(SetWorkItem.BoardLane))) { patch.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = $"/fields/{board.Fields.RowField.ReferenceName}", Value = GetParameter <string>(nameof(SetWorkItem.BoardLane)) }); } if (HasParameter(nameof(SetWorkItem.BoardColumnDone))) { patch.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = $"/fields/{board.Fields.DoneField.ReferenceName}", Value = GetParameter <bool>(nameof(SetWorkItem.BoardColumnDone)) }); } var client = GetClient <WorkItemTrackingHttpClient>(); wi = client.UpdateWorkItemAsync(patch, (int)wi.Id, false, bypassRules) .GetResult("Error updating work item"); } return(wi); }
public void AddToListAtEndWithSerialization() { var doc = new { IntegerList = new List<int>() { 1, 2, 3 } }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("IntegerList/3", 4); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); deserialized.ApplyTo(doc); Assert.Equal(new List<int>() { 1, 2, 3, 4 }, doc.IntegerList); }
public static async Task CreateTask(VssConnection connection) { WorkItemTrackingHttpClient witClient = connection.GetClient <WorkItemTrackingHttpClient>(); try { JsonPatchDocument patchDocument = new JsonPatchDocument(); // título da solicitação patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Title", Value = "titulo" } ); // descrição da solicitação patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Custom.c474834f-4b08-42ac-aaa3-1bcf80e3ae66", Value = "descricao" } ); // razão social do cliente patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Custom.ClienteLista", Value = "XCLIENTE DE TESTE - NÃO APAGAR" } ); // responsável pelo cadastro patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Custom.Contato", Value = "PAULO" } ); // protocolo patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Custom.ProtocoloManager", Value = "999999" } ); // sistema/produto //https://dev.azure.com/gruposym/_apis/projects?api-version=5.1 patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Custom.Produto", Value = "NEFRODATA ACD" } ); //prioridade patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Custom.Prioridade", Value = "2 - Médio" } ); WorkItem workitem = await witClient.CreateWorkItemAsync(patchDocument, "Nefrodata", "Erro"); } catch (AggregateException aex) { VssServiceException vssex = aex.InnerException as VssServiceException; if (vssex != null) { Console.WriteLine(vssex.Message); } } }
public void AddToListAppendWithSerialization() { var doc = new SimpleDTOWithNestedDTO() { SimpleDTO = new SimpleDTO() { IntegerList = new List<int>() { 1, 2, 3 } } }; // create patch JsonPatchDocument<SimpleDTOWithNestedDTO> patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>(); patchDoc.Add<int>(o => o.SimpleDTO.IntegerList, 4); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument<SimpleDTOWithNestedDTO>>(serialized); deserialized.ApplyTo(doc); Assert.Equal(new List<int>() { 1, 2, 3, 4 }, doc.SimpleDTO.IntegerList); }
public async Task <WorkItemModel> CreateLinkedWorkitem(CreateWorkItem workItem) { WorkItemModel workItemModel = new WorkItemModel(); DevOpsConnectionPool poolObj = _builderPool.Get(); try { List <Params> ListParams = new List <Params>(); //VssCredentials creds = new VssBasicCredential(string.Empty, personalaccesstoken); //VssConnection connection = new VssConnection(new Uri(c_collectionUri), creds); //ProjectHttpClient projectClient = connection.GetClient<ProjectHttpClient>(); var workItemTrackingClient = poolObj.VssConnection.GetClient <WorkItemTrackingHttpClient>(); JsonPatchDocument patchDocument = new JsonPatchDocument(); Fields field = null; string title = $"Error deploying solution {workItem.crmsolutioname} in {workItem.crmorgurl}."; string bugtest = $"Error deploying solution {workItem.crmsolutioname} in {workItem.crmorgurl}. \n See attachment file for full error log."; string description = $"Error deploying solution {workItem.crmsolutioname} in {workItem.crmorgurl}. \n See attachment file for full error log."; switch (workItem.type.ToLower()) { case "bug": field = new Fields() { AssignedTo = workItem.assignedTo, BugText = bugtest, Priority = "2", Title = title, Severity = "2 - High" }; ListParams.AddRange(GetBugFields(field)); break; default: //Issue,Feature,Task field = new Fields() { AssignedTo = workItem.assignedTo, Description = description, Priority = "2", Title = title, Severity = "2 - High" }; ListParams.AddRange(GetTaskIssueFeatureFields(field)); break; } foreach (var item in ListParams)//there will be one type of witalways { patchDocument.Add( new JsonPatchOperation() { Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Path = item.Path, Value = item.Value, } ); } if (workItem.parentWit > 0) { /* patchDocument.Add(new Microsoft.VisualStudio.Services.WebApi.Patch.Json.JsonPatchOperation() * { * Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, * Path = "/fields/System.Title", * Value = "childWIT" * });*/ patchDocument.Add(new Microsoft.VisualStudio.Services.WebApi.Patch.Json.JsonPatchOperation() { Operation = Microsoft.VisualStudio.Services.WebApi.Patch.Operation.Add, Path = "/relations/-", Value = new { rel = "System.LinkTypes.Hierarchy-Reverse", url = poolObj.VssConnection.Uri.AbsoluteUri + "/" + workItem.projectname + "/_apis/wit/workItems/" + workItem.parentWit } }); } var workitem = await workItemTrackingClient.CreateWorkItemAsync(patchDocument, workItem.projectid, workItem.type); if (workitem != null) { workItemModel.Id = workitem.Id.Value; workItemModel.url = workitem.Url; } } catch (Exception ex) { } finally { _builderPool.Return(poolObj); } return(workItemModel); }
public void AddNewPropertyToExpandoOjectInTypedObject() { var doc = new NestedDTO() { DynamicProperty = new ExpandoObject() }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("DynamicProperty/NewInt", 1); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); deserialized.ApplyTo(doc); Assert.Equal(1, doc.DynamicProperty.NewInt); }
public void AddToListInvalidPositionTooSmall() { var doc = new SimpleDTOWithNestedDTO() { SimpleDTO = new SimpleDTO() { IntegerList = new List<int>() { 1, 2, 3 } } }; // create patch JsonPatchDocument<SimpleDTOWithNestedDTO> patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>(); patchDoc.Add<int>(o => o.SimpleDTO.IntegerList, 4, -1); Assert.Throws<JsonPatchException>(() => { patchDoc.ApplyTo(doc); }); }
public void AddResultsReplaceShouldFailOnAnonymousDueToNoSetter() { var doc = new { StringProperty = "A" }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("StringProperty", "B"); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); var exception = Assert.Throws<JsonPatchException>(() => { deserialized.ApplyTo(doc); }); Assert.Equal( "The property at path '/StringProperty' could not be updated.", exception.Message); }
public void AddToListAppend() { var doc = new SimpleDTOWithNestedDTO() { SimpleDTO = new SimpleDTO() { IntegerList = new List<int>() { 1, 2, 3 } } }; // create patch JsonPatchDocument<SimpleDTOWithNestedDTO> patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>(); patchDoc.Add<int>(o => o.SimpleDTO.IntegerList, 4); patchDoc.ApplyTo(doc); Assert.Equal(new List<int>() { 1, 2, 3, 4 }, doc.SimpleDTO.IntegerList); }
public void btnStart_Click(object sender, EventArgs e) { try { VssConnection connection = new VssConnection(new Uri("http://vmlvktfs1.dev.haav.com/DefaultCollection"), new VssCredentials()); WorkItemTrackingHttpClient witClient = connection.GetClient <WorkItemTrackingHttpClient>(); List <int> testcasesid = new List <int>(); string[] fileData = File.ReadAllLines(txtPath.Text); foreach (string line in fileData) { testcasesid.Add(Convert.ToInt32(line)); } lblStatus.Text = "Loading all work items"; List <WorkItem> workitems = witClient.GetWorkItemsAsync(testcasesid).Result; lblStatus.Text = "All work items loaded successfully"; if (chkIterationPath.Checked) { if (txtiteration.Text != "") { foreach (var workitem in workitems) { //if (workitem.Fields["System.State"].ToString() == "Not Implemented") //{ int itemID = Convert.ToInt32(workitem.Id); lblStatus.Text = "Started Operation on Test Case " + itemID; JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.IterationPath", Value = txtiteration.Text } ); WorkItem result = witClient.UpdateWorkItemAsync(patchDocument, itemID).Result; lblStatus.Text = "Test Case with ID " + itemID + " Ieration path has been Updated"; ///System.Threading.Thread.Sleep(60000); //Console.WriteLine(" {0}: {1}", workitem.Id, workitem.Fields["System.Title"]); //} } } } else if (chkAssignedTo.Checked) { if (txtassignedTo.Text != "") { foreach (var workitem in workitems) { //if (workitem.Fields["System.State"].ToString() == "Not Implemented") //{ int itemID = Convert.ToInt32(workitem.Id); lblStatus.Text = "Started Operation on Test Case " + itemID; JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.AssignedTo", Value = txtassignedTo.Text } ); WorkItem result = witClient.UpdateWorkItemAsync(patchDocument, itemID).Result; lblStatus.Text = "Test Case with ID " + itemID + " Assigned To has been Updated"; ///System.Threading.Thread.Sleep(60000); //Console.WriteLine(" {0}: {1}", workitem.Id, workitem.Fields["System.Title"]); //} } } } else if (chkrhtxtbox.Checked) { //string[] RichTextBoxLines = rchtxtbox.Lines; if (rchtxtbox.Text != "") { foreach (var workitem in workitems) { //if (workitem.Fields["System.State"].ToString() == "Not Implemented") //{ string description = ""; if (workitem.Fields.ContainsKey("System.Description")) { description = workitem.Fields["System.Description"].ToString(); } description = rchtxtbox.Text + description; int itemID = Convert.ToInt32(workitem.Id); lblStatus.Text = "Started Operation on Test Case " + itemID; JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Description", Value = description } ); WorkItem result = witClient.UpdateWorkItemAsync(patchDocument, itemID).Result; lblStatus.Text = "Test Case with ID " + itemID + " Description path has been Updated"; ///System.Threading.Thread.Sleep(60000); //Console.WriteLine(" {0}: {1}", workitem.Id, workitem.Fields["System.Title"]); //} } } } else if (chkareapath.Checked) { //string[] RichTextBoxLines = rchtxtbox.Lines; if (txtarepath.Text != "") { foreach (var workitem in workitems) { int itemID = Convert.ToInt32(workitem.Id); lblStatus.Text = "Started Operation on Test Case " + itemID; JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.AreaPath", Value = txtarepath.Text } ); WorkItem result = witClient.UpdateWorkItemAsync(patchDocument, itemID).Result; lblStatus.Text = "Test Case with ID " + itemID + " Area path has been Updated"; ///System.Threading.Thread.Sleep(60000); //Console.WriteLine(" {0}: {1}", workitem.Id, workitem.Fields["System.Title"]); //} } } } else { foreach (var workitem in workitems) { if (workitem.Fields["System.State"].ToString() == "Not Implemented") { int itemID = Convert.ToInt32(workitem.Id); lblStatus.Text = "Started Operation on Test Case " + itemID; JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.State", Value = "Ready for Test" } ); WorkItem result = witClient.UpdateWorkItemAsync(patchDocument, itemID).Result; lblStatus.Text = "Test Case with ID " + itemID + " Status has been Updated"; ///System.Threading.Thread.Sleep(60000); //Console.WriteLine(" {0}: {1}", workitem.Id, workitem.Fields["System.Title"]); } } } lblStatus.Text = "All Test Cases Updated Successfully"; } catch (Exception ex) { MessageBox.Show(ex.ToString()); } }
public async Task <TestRunResponse> RecordTestRunEndAsync(TestCaseInfo testCase, TestRunOutcome outcome, string errorMessage, IEnumerable <string> impactFiles, IEnumerable <string> impactMethods) { var patch = new JsonPatchDocument <TestRunRequest>(); patch.Add(r => r.State, TestRunState.Finished); patch.Add(r => r.Outcome, outcome); patch.Add(r => r.FinishTime, DateTime.Now); patch.Add(r => r.ErrorMessage, errorMessage); var runResponse = await _client.PatchTestRunAsync(_session.Id, testCase.TestRunId, patch); _logger.Info("Test run finished: " + ObjToString(runResponse)); List <CodeSignature> codeSignatures = new List <CodeSignature>(); if (impactFiles != null) { codeSignatures.AddRange( impactFiles.Select(f => new CodeSignature(f, CodeSignatureUtils.CalculateSignature(f), CodeSignatureType.File))); } if (impactMethods != null) { codeSignatures.AddRange( impactMethods.Select(f => new CodeSignature(f, CodeSignatureUtils.CalculateSignature(f), CodeSignatureType.Method))); } var impactRequest = new TestCaseImpactUpdateRequest() { ProductLine = _productLine, CodeSignatures = codeSignatures }; var impactDataCount = codeSignatures.Count; LogDebug("Updating test impact information. Number of files/methods (code signatures): " + impactDataCount); if (impactDataCount == 0) { LogDebug("(No impact data is available)"); } Exception impactUpdateException = null; try { await _client.UpdateTestImpactAsync(testCase.Id, impactRequest); } catch (Exception ex) { impactUpdateException = ex; _logger.Error("Updating test case impact data failed:\r\n" + ex.ToString()); } var lastStateRequest = new TestLastStateUpdateRequest() { ProductLine = _productLine, TestRunSessionId = _session.Id, Outcome = outcome }; if (impactUpdateException != null) { lastStateRequest.DictatedRunReason = RunReason.ImpactUpdateFailed; _logger.Warn("Test case will be run again next time."); } LogDebug($"Updating test last state. Azure Test Case Id: {testCase.AzureTestCaseId}, Outcome: {outcome}"); await _client.UpdateTestLastStateAsync(testCase.Id, lastStateRequest); return(runResponse); }
public void ShouldNotBeAbleToAddToNonExistingPropertyThatIsNotTheRoot() { //Adding to a Nonexistent Target // // An example target JSON document: // { "foo": "bar" } // A JSON Patch document: // [ // { "op": "add", "path": "/baz/bat", "value": "qux" } // ] // This JSON Patch document, applied to the target JSON document above, // would result in an error (therefore, it would not be applied), // because the "add" operation's target location that references neither // the root of the document, nor a member of an existing object, nor a // member of an existing array. var doc = new NestedDTO() { DynamicProperty = new ExpandoObject() }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("DynamicProperty/OtherProperty/IntProperty", 1); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); var exception = Assert.Throws<JsonPatchException>(() => { deserialized.ApplyTo(doc); }); Assert.Equal( "The property at path '/DynamicProperty/OtherProperty/IntProperty' could not be added.", exception.Message); }
public void AddNewPropertyToTypedObjectInExpandoObject() { dynamic dynamicProperty = new ExpandoObject(); dynamicProperty.StringProperty = "A"; var doc = new NestedDTO() { DynamicProperty = dynamicProperty }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("DynamicProperty/StringProperty", "B"); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); deserialized.ApplyTo(doc); Assert.Equal("B", doc.DynamicProperty.StringProperty); }
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}"); }
/// <summary> /// Creates a JSON PatchDocument to update the state of the server-side WorkItem. /// </summary> /// <returns>JSON document</returns> internal JsonPatchDocument CreatePatchDocument() { var document = new JsonPatchDocument(); ProcessFieldProperties((fieldInfo, initialValue, currentValue, changed) => { if (changed) { JsonPatchOperation operation; if (currentValue == null) { operation = new JsonPatchOperation { Operation = Operation.Remove, Path = $"/fields/{fieldInfo.FieldName}" }; } else if (initialValue == null) { operation = new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{fieldInfo.FieldName}", Value = currentValue.ToString() }; } else { operation = new JsonPatchOperation { Operation = Operation.Replace, Path = $"/fields/{fieldInfo.FieldName}", Value = currentValue.ToString() }; } document.Add(operation); } return(true); }); if (addComments != null) { document.Add(new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/System.History", Value = addComments.ToString() }); } if (document.Count > 0) { if (witModel != null) { document.Insert(0, new JsonPatchOperation { Operation = Operation.Test, Path = "/rev", Value = Revision }); } return(document); } return(null); }
public static WorkItem CreateBug(string title, string stepsToReproduce, string description, List <string> filePathsToBeAttached = null) { var credentials = new VssBasicCredential(string.Empty, _personalAccessToken); JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Title", Value = title, }); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.SystemInfo", Value = description, }); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.ReproSteps", Value = stepsToReproduce, }); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Common.Priority", Value = ConfigurationService.GetSection <AzureDevOpsBugReportingSettings>().DefaultPriority, }); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.Common.Severity", Value = ConfigurationService.GetSection <AzureDevOpsBugReportingSettings>().DefaultSeverity, }); try { using var httpClient = new WorkItemTrackingHttpClient(new Uri(_uri), credentials); var attachments = CreateAttachments(httpClient, filePathsToBeAttached); AddAttachmentRelationships(patchDocument, attachments); WorkItem result = httpClient.CreateWorkItemAsync(patchDocument, _project, "Bug").Result; return(result); } catch { return(null); } return(null); }
public void AddToExistingPropertyOnNestedObject() { dynamic doc = new { Test = 1, nested = new NestedDTO() }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("Nested/StringProperty", "A"); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); deserialized.ApplyTo(doc); Assert.Equal("A", doc.nested.StringProperty); Assert.Equal(1, doc.Test); }
public async Task <bool> CreateIssue(Event <LogEventData> evt) { if (evt == null) //Not sure when or if this could happen? { return(false); } //If the event level is defined and it is not in the list do not log it if ((LogEventLevelList?.Count ?? 0) > 0 && !LogEventLevelList.Contains(evt.Data.Level)) { return(false); } _step = "Beginning process"; LogIfDebug(_step); var description = evt.Data.Exception ?? evt.Data.RenderedMessage; //var messageId = ComputeId(description); var connection = new VssConnection(new Uri(AzureDevOpsUrl), new VssBasicCredential(string.Empty, PersonalAccessToken)); var workitemClient = await connection.GetClientAsync <WorkItemTrackingHttpClient>(); WorkItemQueryResult workItemQueryResult = null; // Try to match an existing work item if (!string.IsNullOrEmpty(SeqEventField)) { _step = "Querying existing work item"; LogIfDebug(_step); Wiql wiql = new Wiql() { Query = "Select [State], [Title] " + "From WorkItems " + "Where [" + SeqEventField + "] = '" + evt.Id + "' " + "And [System.TeamProject] = '" + Project + "' " + //"And [System.State] <> 'Closed' " + "Order By [State] Asc, [Changed Date] Desc" }; //execute the query to get the list of work items in teh results workItemQueryResult = await workitemClient.QueryByWiqlAsync(wiql); if (workItemQueryResult.WorkItems.Count() != 0) { Log.Information("Duplicate DevOps item creation prevented for event id {id}", evt.Id); return(false); } } _step = "Adding fields"; LogIfDebug(_step); var document = new JsonPatchDocument(); _step = "Adding title"; string title = $"SEQ Event - {evt.Data.RenderedMessage}".TruncateWithEllipsis(255); //DevOps has max 255 character length for title if (!string.IsNullOrEmpty(Title)) //User has defined their own title parsing { title = GetSeqMappedPropertyString(Title, evt).TruncateWithEllipsis(255); } document.Add( new JsonPatchOperation() { Path = "/fields/System.Title", Operation = Operation.Add, Value = title }); document.Add( new JsonPatchOperation() { Path = "/fields/System.AssignedTo", Operation = Operation.Add, Value = AssignedTo ?? string.Empty }); if (!ParentWorkItemLinkUrl.IsNullOrEmpty()) { document.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "System.LinkTypes.Hierarchy-Reverse", url = ParentWorkItemLinkUrl, attributes = new { comment = "Seq Event Auto-Parent Link" } } } ); } if (!string.IsNullOrEmpty(SeqEventField)) { _step = "Adding Seq ID mapping"; LogIfDebug(_step); document.Add(new JsonPatchOperation() { Path = "/fields/" + SeqEventField, Operation = Operation.Add, Value = evt.Id }); } if (!string.IsNullOrEmpty(Tags)) { _step = "Adding tags"; LogIfDebug(_step); document.Add(new JsonPatchOperation() { Path = "/fields/Tags", Operation = Operation.Add, Value = Tags //DevOps takes a comma seperated list without any alterations }); } _step = "Setting description"; LogIfDebug(_step); document.Add( new JsonPatchOperation() { Path = $"/fields/{DescriptionDevOpsMappingField}", Operation = Operation.Add, Value = RenderDescription(evt) }); if (!string.IsNullOrEmpty(SeqToDevOpsMapping)) { _step = "Setting Seq to DevOps property mappings"; LogIfDebug(_step); var keyValuePairs = SeqToDevOpsMapping.ParseKeyValueArray(); foreach (var value in keyValuePairs) { if (evt.Data.Properties.ContainsKey(value.Key)) { LogIfDebug("Setting Seq to DevOps Property: " + value.Value + " Value: " + value.Key); document.Add( new JsonPatchOperation() { Path = $"/fields/{value.Value}", Operation = Operation.Add, Value = evt.Data.Properties[value.Key] }); } } } if (!string.IsNullOrEmpty(DevOpsMappings)) { _step = "Setting DevOps static property mappings"; LogIfDebug(_step); var keyValuePairs = DevOpsMappings.ParseKeyValueArray(); foreach (var value in keyValuePairs) { LogIfDebug("Setting DevOps Static Property: " + value.Key + " Value: " + value.Value); document.Add( new JsonPatchOperation() { Path = $"/fields/{value.Key}", Operation = Operation.Add, Value = value.Value }); } } if (!string.IsNullOrEmpty(AreaPath)) { _step = "Setting Area Path"; LogIfDebug(_step); document.Add( new JsonPatchOperation() { Path = $"/fields/System.AreaPath", Operation = Operation.Add, Value = AreaPath }); } if (!string.IsNullOrEmpty(Iteration)) { _step = "Setting Iteration"; LogIfDebug(_step); document.Add( new JsonPatchOperation() { Path = $"/fields/System.IterationPath", Operation = Operation.Add, Value = Iteration }); } _step = "Adding work item"; LogIfDebug(_step); var workitem = await workitemClient.CreateWorkItemAsync(document, Project, DevOpsIssueType, false, true); _step = "Finished process"; LogIfDebug(_step); return(true); }
public void AddToListInvalidPositionTooLarge() { var doc = new { IntegerList = new List<int>() { 1, 2, 3 } }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("IntegerList/4", 4); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); var exception = Assert.Throws<JsonPatchException>(() => { deserialized.ApplyTo(doc); }); Assert.Equal( "For operation 'add' on array property at path '/IntegerList/4', the index is larger than the array size.", exception.Message); }
public IActionResult Post([FromBody] JObject payload) { string tags = ""; string authHeader = ""; string pat = ""; #if DEBUG isDebug = true; #endif if (!isDebug) { tags = Request.Headers.ContainsKey("Work-Item-Tags") ? Request.Headers["Work-Item-Tags"] : new StringValues(""); authHeader = Request.Headers.ContainsKey("Authorization") ? Request.Headers["Authorization"] : new StringValues(""); if (!authHeader.StartsWith("Basic")) { return(new StandardResponseObjectResult("missing basic authorization header", StatusCodes.Status401Unauthorized)); } //get pat from basic authorization header. This was set in the web hook pat = this.GetPersonalAccessToken(authHeader); } PayloadViewModel vm = this.BuildPayloadViewModel(payload); //make sure pat is not empty, if it is, pull from appsettings vm.pat = (!string.IsNullOrEmpty(pat)) ? pat : _appSettings.Value.AzureDevOpsToken; if (string.IsNullOrEmpty(vm.pat)) { return(new StandardResponseObjectResult("missing pat from authorization header and appsettings", StatusCodes.Status404NotFound)); } if (vm.eventType != "workitem.created") { return(new OkResult()); } if (vm.id == -1) { return(new OkResult()); } JsonPatchDocument patchDocument = new JsonPatchDocument(); patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Test, Path = "/rev", Value = (vm.rev + 1).ToString() } ); if (string.IsNullOrEmpty(vm.assignedTo)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.AssignedTo", Value = vm.createdBy } ); } if (!string.IsNullOrEmpty(tags)) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.Tags", Value = tags } ); } patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/System.IterationPath", Value = vm.teamProject } ); var result = _workItemRepo.UpdateWorkItem(patchDocument, vm); return((result != null) ? new OkResult() : new StatusCodeResult(500)); }
public void ShouldAddToListWithDifferentCase() { var doc = new { IntegerList = new List<int>() { 1, 2, 3 } }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("integerlist/0", 4); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); deserialized.ApplyTo(doc); Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.IntegerList); }
public async Task <WorkItemModel> UpdateWorkItem(WorkItemModel workItemModel) { using (Logger.BeginScopeContext(nameof(UpdateWorkItem))) { 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 { // Implement simple, sensible logging mechanism // // https://github.com/microsoft/sarif-sdk/issues/1771 throw; } } } var patchDocument = new JsonPatchDocument(); if (attachmentReference != null) { patchDocument.Add( new JsonPatchOperation { Operation = Operation.Remove, Path = $"/relations/0" }); patchDocument.Add( new JsonPatchOperation { Operation = Operation.Add, Path = $"/relations/-", Value = new { rel = "AttachedFile", attachmentReference.Url } }); patchDocument.Add( new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{AzureDevOpsFieldNames.History}", Value = workItemModel.CommentOrDiscussion }); patchDocument.Add( new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{AzureDevOpsFieldNames.ReproSteps}", Value = workItemModel.BodyOrDescription }); } try { if (int.TryParse(workItemModel.Uri.OriginalString.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault(), out int workItemId)) { Logger.LogInformation($"Updating work item id: {workItemId}"); _ = await _witClient.UpdateWorkItemAsync(patchDocument, id : workItemId); Logger.LogInformation($"UPDATED: {workItemModel.Uri}"); } } catch (Exception e) { Console.Error.WriteLine(e); this.Logger.LogError(e, "Updating work item: {workItemModel.Title}", workItemModel.Title); if (patchDocument != null) { string patchJson = JsonConvert.SerializeObject(patchDocument, Formatting.Indented); Console.Error.WriteLine(patchJson); } } return(workItemModel); } }
public void ShouldReplacePropertyWithDifferentCase() { dynamic doc = new ExpandoObject(); doc.StringProperty = "A"; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("stringproperty", "B"); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); deserialized.ApplyTo(doc); Assert.Equal("B", doc.StringProperty); }
public async Task <WorkItemModel> CreateWorkItem(WorkItemModel workItemModel) { using (Logger.BeginScopeContext(nameof(CreateWorkItem))) { //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 { // Implement simple, sensible logging mechanism // // https://github.com/microsoft/sarif-sdk/issues/1771 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) }, new JsonPatchOperation { Operation = Operation.Add, Path = $"/fields/{AzureDevOpsFieldNames.ReproSteps}", Value = workItemModel.BodyOrDescription } }; 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"; Logger.LogInformation($"Creating work item: {workItemModel.Title}"); workItem = await _witClient.CreateWorkItemAsync(patchDocument, project : workItemModel.RepositoryOrProject, workItemKind); workItemModel.Uri = new Uri(workItem.Url, UriKind.Absolute); workItemModel.HtmlUri = new Uri(((ReferenceLink)workItem.Links.Links["html"]).Href, UriKind.Absolute); Logger.LogInformation($"CREATED: {workItemModel.HtmlUri}"); } catch (Exception e) { Console.Error.WriteLine(e); this.Logger.LogError(e, "Creating work item: {workItemModel.Title}", workItemModel.Title); if (patchDocument != null) { string patchJson = JsonConvert.SerializeObject(patchDocument, Formatting.Indented); Console.Error.WriteLine(patchJson); } } return(workItemModel); } }
public void AddNewPropertyToExpandoOject() { dynamic doc = new { Test = 1, nested = new ExpandoObject() }; // create patch JsonPatchDocument patchDoc = new JsonPatchDocument(); patchDoc.Add("Nested/NewInt", 1); var serialized = JsonConvert.SerializeObject(patchDoc); var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument>(serialized); deserialized.ApplyTo(doc); Assert.Equal(1, doc.nested.NewInt); Assert.Equal(1, doc.Test); }
public AzureTestCase AssociateAutomationToExistingTestCase(string workItemId, string testName, string testProjectName, string requirementUrl) { var uri = new Uri(_uri); var credentials = new VssBasicCredential(string.Empty, _personalAccessToken); VssConnection connection = new VssConnection(uri, credentials); WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient <WorkItemTrackingHttpClient>(); var existingItem = workItemTrackingHttpClient.GetWorkItemAsync(workItemId.ToInt(), expand: WorkItemExpand.Relations).Result; var patchDocument = new JsonPatchDocument(); if (existingItem.Fields.ContainsKey("Microsoft.VSTS.TCM.AutomatedTestName")) { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Replace, Path = "/fields/Microsoft.VSTS.TCM.AutomatedTestName", Value = testName, }); } else { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.AutomatedTestName", Value = testName, }); } if (existingItem.Fields.ContainsKey("Microsoft.VSTS.TCM.AutomatedTestStorage")) { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Replace, Path = "/fields/Microsoft.VSTS.TCM.AutomatedTestStorage", Value = testProjectName, }); } else { patchDocument.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/fields/Microsoft.VSTS.TCM.AutomatedTestStorage", Value = testProjectName, }); } if (!string.IsNullOrEmpty(requirementUrl)) { if (existingItem.Relations == null) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "System.LinkTypes.Hierarchy-Reverse", url = requirementUrl, attributes = new Dictionary <string, object>() { { "isLocked", false }, { "name", "Parent" }, { "comment", "added automatically" }, }, }, }); } else if (!existingItem.Relations.Any(x => x.Url.Equals(requirementUrl))) { patchDocument.Add( new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "System.LinkTypes.Hierarchy-Reverse", url = requirementUrl, attributes = new Dictionary <string, object>() { { "isLocked", false }, { "name", "Parent" }, { "comment", "added automatically" }, }, }, }); } } try { WorkItem result = workItemTrackingHttpClient.UpdateWorkItemAsync(patchDocument, workItemId.ToInt()).Result; return(ConvertWorkItemToAzureTestCase(result)); } catch (AggregateException ex) { Debug.WriteLine("Error updating test case: {0}", ex.InnerException.Message); return(null); } }