public async Task SaveLinks_Converts_VanityUrl_To_LowerCase(string vanityUrl) { // Arrange ILogger fakeLogger = A.Fake <ILogger>(); LinkBundle bundle = Fixture.Create <LinkBundle>(); bundle.VanityUrl = vanityUrl; HttpRequest req = this.AuthenticatedRequest; req.Body = this.GetHttpRequestBodyStream(JsonConvert.SerializeObject(bundle)); IAsyncCollector <LinkBundle> collector = A.Fake <IAsyncCollector <LinkBundle> >(); // Act IActionResult result = await LinkOperations.SaveLinks(req, collector, fakeLogger); // Assert Assert.IsType <CreatedResult>(result); CreatedResult createdResult = result as CreatedResult; LinkBundle createdBundle = createdResult.Value as LinkBundle; Assert.Equal(vanityUrl.ToLower(), createdBundle.VanityUrl); A.CallTo(() => collector.AddAsync(A <LinkBundle> .That.Matches(b => b.VanityUrl == vanityUrl.ToLower()), default)).MustHaveHappened(); }
public async Task PostLinkBundleCreatesLinkBundleWhenValidPayload() { // Arrange LinkBundle expectedLinkBundle = null; LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = "samplelink", Description = "sampledescription", Links = new List <Link> { new Link { Id = "sample" } } }; _mockService.Setup(r => r.CreateLinkBundleAsync(It.IsAny <LinkBundle>())) .Callback <LinkBundle>(x => expectedLinkBundle = x); // Act ActionResult <LinkBundle> result = await _linksController.PostLinkBundleAsync(linkBundle); // Assert _mockService.Verify(x => x.CreateLinkBundleAsync(It.IsAny <LinkBundle>()), Times.Once); Assert.IsType <CreatedAtActionResult>(result.Result); Assert.Equal(linkBundle.Description, expectedLinkBundle.Description); Assert.Equal(linkBundle.UserId, expectedLinkBundle.UserId); Assert.Equal(linkBundle.VanityUrl, expectedLinkBundle.VanityUrl); Assert.Equal(linkBundle.Links.Count, expectedLinkBundle.Links.Count()); }
public async Task SaveLinks_Populates_VanityUrl_If_Not_Provided() { // Arrange ILogger fakeLogger = A.Fake <ILogger>(); Binder fakeBinder = A.Fake <Binder>(); LinkBundle bundle = Fixture.Create <LinkBundle>(); bundle.VanityUrl = string.Empty; HttpRequest req = this.AuthenticatedRequest; req.Body = this.GetHttpRequestBodyStream(JsonConvert.SerializeObject(bundle)); IAsyncCollector <LinkBundle> collector = A.Fake <IAsyncCollector <LinkBundle> >(); // Act IActionResult result = await _linkOperations.SaveLinks(req, collector, fakeBinder, fakeLogger); // Assert Assert.IsType <CreatedResult>(result); CreatedResult createdResult = result as CreatedResult; LinkBundle createdBundle = createdResult.Value as LinkBundle; Assert.False(string.IsNullOrEmpty(createdBundle.VanityUrl)); Assert.Equal(createdBundle.VanityUrl.ToLower(), createdBundle.VanityUrl); A.CallTo(() => collector.AddAsync(A <LinkBundle> .That.Matches(b => !string.IsNullOrEmpty(b.VanityUrl)), default)).MustHaveHappened(); }
public async Task UpdateList_Applies_JsonPatch_To_Bundle() { // Arrange JsonPatchDocument <LinkBundle> patchReqDocument = new JsonPatchDocument <LinkBundle>(); patchReqDocument.Replace(d => d.Description, "Description"); patchReqDocument.Replace(d => d.Links, this.Fixture.CreateMany <IDictionary <string, string> >()); HttpRequest req = this.AuthenticatedRequest; req.Body = this.GetHttpRequestBodyStream(JsonConvert.SerializeObject(patchReqDocument)); IEnumerable <LinkBundle> docs = this.Fixture.CreateMany <LinkBundle>(1); IDocumentClient docClient = this.Fixture.Create <IDocumentClient>(); LinkBundle captured = null; A.CallTo(() => docClient.UpsertDocumentAsync(A <Uri> .Ignored, A <LinkBundle> .Ignored, A <RequestOptions> .Ignored, false, default(CancellationToken))) .Invokes((IFakeObjectCall callOb) => { captured = callOb.Arguments[1] as LinkBundle; }); string vanityUrl = "vanity"; // Act IActionResult result = await LinkOperations.UpdateList(req, docs, docClient, vanityUrl, A.Dummy <ILogger>()); // Assert Assert.Equal("Description", captured.Description); Assert.IsType <NoContentResult>(result); }
public async Task PatchLinkBundleAppliesJsonPathToLinkBundle() { // Arrange LinkBundle expectedLinkBundle = null; LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = "samplelink" }; _mockService.Setup(service => service.GetUserAccountEmail()) .Returns(linkBundle.UserId); _mockService.Setup(service => service.FindLinkBundleAsync(linkBundle.VanityUrl)) .ReturnsAsync(linkBundle); _mockService.Setup(r => r.UpdateLinkBundleAsync(It.IsAny <LinkBundle>())) .Callback <LinkBundle>(x => expectedLinkBundle = x); string description = "sampledescription"; JsonPatchDocument <LinkBundle> patchReqDocument = new JsonPatchDocument <LinkBundle>(); patchReqDocument.Add(d => d.Description, description); // Act ActionResult <LinkBundle> result = await _linksController.PatchLinkBundleAsync(linkBundle.VanityUrl, patchReqDocument); // Assert Assert.IsType <NoContentResult>(result.Result); Assert.Equal(linkBundle.VanityUrl, expectedLinkBundle.VanityUrl); Assert.Equal(linkBundle.UserId, expectedLinkBundle.UserId); Assert.Equal(description, expectedLinkBundle.Description); }
private FormEditBundle(LinkBundle linkBundle) { InitializeComponent(); _linkBundle = linkBundle; Text = _linkBundle.Name; repositoryItemTextEditBundleItems.EnableSelectAll(); repositoryItemMemoEditBundleItems.EnableSelectAll(); barLargeButtonItemLinksAddInfo.Caption = InfoItem.ItemName; barLargeButtonItemLinksAddRevenue.Caption = RevenueItem.ItemName; barLargeButtonItemLinksAddStrategy.Caption = StrategyItem.ItemName; if (CreateGraphics().DpiX > 96) { var font = new Font(styleController.Appearance.Font.FontFamily, styleController.Appearance.Font.Size - 2, styleController.Appearance.Font.Style); styleController.Appearance.Font = font; styleController.AppearanceDisabled.Font = font; styleController.AppearanceDropDown.Font = font; styleController.AppearanceDropDownHeader.Font = font; styleController.AppearanceFocused.Font = font; styleController.AppearanceReadOnly.Font = font; xtraTabControl.AppearancePage.HeaderActive.Font = new Font(xtraTabControl.AppearancePage.HeaderActive.Font.FontFamily, xtraTabControl.AppearancePage.HeaderActive.Font.Size - 2, xtraTabControl.AppearancePage.HeaderActive.Font.Style); xtraTabControl.AppearancePage.Header.Font = new Font(xtraTabControl.AppearancePage.Header.Font.FontFamily, xtraTabControl.AppearancePage.Header.Font.Size - 2, xtraTabControl.AppearancePage.Header.Font.Style); xtraTabControl.AppearancePage.HeaderDisabled.Font = new Font(xtraTabControl.AppearancePage.HeaderDisabled.Font.FontFamily, xtraTabControl.AppearancePage.HeaderDisabled.Font.Size - 2, xtraTabControl.AppearancePage.HeaderDisabled.Font.Style); xtraTabControl.AppearancePage.HeaderHotTracked.Font = new Font(xtraTabControl.AppearancePage.HeaderHotTracked.Font.FontFamily, xtraTabControl.AppearancePage.HeaderHotTracked.Font.Size - 2, xtraTabControl.AppearancePage.HeaderHotTracked.Font.Style); buttonXCancel.Font = new Font(buttonXCancel.Font.FontFamily, buttonXCancel.Font.Size - 2, buttonXCancel.Font.Style); buttonXOK.Font = new Font(buttonXOK.Font.FontFamily, buttonXOK.Font.Size - 2, buttonXOK.Font.Style); } }
public async Task SaveLinks_Valid_Payload_Returns_CreatRequest() { // Arrange ILogger fakeLogger = A.Fake <ILogger>(); LinkBundle bundle = Fixture.Create <LinkBundle>(); HttpRequest req = this.AuthenticatedRequest; req.Body = this.GetHttpRequestBodyStream(JsonConvert.SerializeObject(bundle)); IAsyncCollector <LinkBundle> collector = A.Fake <IAsyncCollector <LinkBundle> >(); // Act IActionResult result = await LinkOperations.SaveLinks(req, collector, fakeLogger); // Assert Assert.IsType <CreatedResult>(result); CreatedResult createdResult = result as CreatedResult; LinkBundle createdBundle = createdResult.Value as LinkBundle; Assert.Equal("userid", createdBundle.UserId); A.CallTo(() => collector.AddAsync(A <LinkBundle> .That.Matches(b => b.UserId == "userid"), default)).MustHaveHappened(); }
public static LinkBundleLink Create(LibraryFolder parentFolder, LinkBundle bundle) { return CreateEntity<LinkBundleLink>(linkBundleLink => { linkBundleLink.Name = bundle.Name; linkBundleLink.Folder = parentFolder; ((LinkBundleLinkSettings)linkBundleLink.Settings).BundleId = bundle.ExtId; }); }
/// <summary> /// Gets a link bundle by the specified vanity url /// </summary> /// <param name="vanityUrl"></param> /// <returns></returns> private async Task <(HttpResponseMessage, LinkBundle)> GetLinkBundleByVanityUrl(string vanityUrl) { LinkBundle userLinkBundle = null; var response = await this.client.GetAsync($"/api/links/{vanityUrl}"); if (response.StatusCode == HttpStatusCode.OK) { userLinkBundle = JsonConvert.DeserializeObject <LinkBundle>(await response.Content.ReadAsStringAsync()); } return(response, userLinkBundle); }
public async Task <ActionResult <LinkBundle> > PostLinkBundleAsync(LinkBundle linkBundle) { if (linkBundle.Links.Count() == 0) { var problemDetails = new ProblemDetails() { Title = "Payload is invalid", Detail = "No links are provided", Status = StatusCodes.Status400BadRequest, Type = "/linkylink/clientissue", Instance = Request.Path }; return(new BadRequestObjectResult(problemDetails)); } string userHandle = _linksService.GetUserAccountEmail(); linkBundle.UserId = userHandle; ValidateVanityUrl(linkBundle); string vanity_regex = @"^([\w\d-])+(/([\w\d-])+)*$"; Match match = Regex.Match(linkBundle.VanityUrl, vanity_regex, RegexOptions.IgnoreCase); if (!match.Success) { return(new BadRequestResult()); } try { await _linksService.CreateLinkBundleAsync(linkBundle); } catch (DbUpdateException) { if (await _linksService.LinkBundleExistsAsync(linkBundle.Id)) { return(Conflict()); } else { throw; } } catch (Exception) { return(new StatusCodeResult(StatusCodes.Status500InternalServerError)); } return(CreatedAtAction("GetLinkBundle", new { vanityUrl = linkBundle.VanityUrl }, linkBundle)); }
public async Task GetLinkBundleForUserReturnsUnAuthorizedIfMissingAuth() { // Arrange LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = "samplelink" }; // Act ActionResult <LinkBundle> result = await _linksController.GetLinkBundlesForUserAsync(linkBundle.UserId, QueryOptions.Default); // Assert Assert.IsType <UnauthorizedResult>(result.Result); }
public async Task DeleteLinkBundleReturnsUnAuthorizedIfMissingAuth() { // Arrange LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = "samplelink" }; // Act ActionResult <LinkBundle> result = await _linksController.DeleteLinkBundleAsync(linkBundle.VanityUrl); // Assert Assert.IsType <UnauthorizedResult>(result.Result); }
public async Task CreateLinkBundleAsyncCreatesLinkBundleInDB() { // Arrange var newBundle = new LinkBundle { VanityUrl = "samplelink" }; Assert.Equal(0, _sourceList.Count()); // Act await _linksService.CreateLinkBundleAsync(newBundle); // Assert Assert.Equal(1, _sourceList.Count()); }
public async Task PostLinkBundleReturnsBadRequestIfLinksAreNotProvided() { // Arrange LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = string.Empty }; // Act ActionResult <LinkBundle> result = await _linksController.PostLinkBundleAsync(linkBundle); // Assert Assert.IsType <BadRequestObjectResult>(result.Result); }
public async Task RemoveLinkBundleAsyncRemovesLinkBundleFromDB() { // Arrange var bundleToDelete = new LinkBundle { Id = "samplelink" }; _sourceList.Add(bundleToDelete); Assert.Equal(1, _sourceList.Count); // Act await _linksService.RemoveLinkBundleAsync(bundleToDelete); //Asert Assert.Equal(0, _sourceList.Count); }
public async Task PostLinkBundleReturnsBadRequestIfVanityUrlNameIsInvalid() { // Arrange LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = "name@", Links = new List <Link> { new Link() } }; // Act ActionResult <LinkBundle> result = await _linksController.PostLinkBundleAsync(linkBundle); // Assert Assert.IsType <BadRequestObjectResult>(result.Result); }
public async Task PostLinkBundleThrowsDBUpdateException() { // Arrange LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = string.Empty, Links = new List <Link> { new Link() } }; _mockService.Setup(r => r.CreateLinkBundleAsync(linkBundle)) .Throws(new DbUpdateException()); // Act, Assert Assert.ThrowsAsync <DbUpdateException>(() => _linksController.PostLinkBundleAsync(linkBundle)); }
public async Task GetLinkBundleReturnsDocumentIfLinkBundleExists() { // Arrange LinkBundle linkBundle = new LinkBundle { VanityUrl = "samplelink" }; _mockService.Setup(service => service.FindLinkBundleAsync(linkBundle.VanityUrl)) .ReturnsAsync(linkBundle); // Act ActionResult <LinkBundle> result = await _linksController.GetLinkBundleAsync(linkBundle.VanityUrl); // Assert Assert.IsType <LinkBundle>(result.Value); Assert.Equal(result.Value.VanityUrl, linkBundle.VanityUrl); }
public async Task <ActionResult <LinkBundle> > PostLinkBundleAsync(LinkBundle linkBundle) { if (linkBundle.Links.Count() == 0) { return(BadRequest("Invalid Payload. No Links are provided with LinkBundle.")); } string userHandle = _linksService.GetUserAccountEmail(); linkBundle.UserId = userHandle; ValidateVanityUrl(linkBundle); string vanity_regex = @"^([\w\d-])+(/([\w\d-])+)*$"; Match match = Regex.Match(linkBundle.VanityUrl, vanity_regex, RegexOptions.IgnoreCase); if (!match.Success) { return(BadRequest("Invalid Payload. Vanity Url name is invalid.")); } try { await _linksService.CreateLinkBundleAsync(linkBundle); } catch (DbUpdateException) { if (await _linksService.LinkBundleExistsAsync(linkBundle.Id)) { return(Conflict()); } else { throw; } } catch (Exception) { return(new StatusCodeResult(StatusCodes.Status500InternalServerError)); } return(CreatedAtAction("GetLinkBundle", new { vanityUrl = linkBundle.VanityUrl }, linkBundle)); }
public async Task PatchLinkBundleReturnsNotFoundIfLinkBundleDoesntExist() { // Arrange LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = "samplelink" }; _mockService.Setup(service => service.GetUserAccountEmail()) .Returns("*****@*****.**"); JsonPatchDocument <LinkBundle> patchReqDocument = new JsonPatchDocument <LinkBundle>(); // Act ActionResult <LinkBundle> result = await _linksController.PatchLinkBundleAsync(linkBundle.VanityUrl, patchReqDocument); // Assert Assert.IsType <NotFoundResult>(result.Result); }
public async Task SaveLinks_Returns_BadRequest_If_Vanity_Url_Fails_Regex(string vanityUrl) { // Arrange ILogger fakeLogger = A.Fake <ILogger>(); HttpRequest req = this.DefaultRequest; LinkBundle payload = this.Fixture.Create <LinkBundle>(); payload.VanityUrl = vanityUrl; req.Body = this.GetHttpRequestBodyStream(JsonConvert.SerializeObject(payload)); IAsyncCollector <LinkBundle> collector = A.Fake <IAsyncCollector <LinkBundle> >(); // Act IActionResult result = await LinkOperations.SaveLinks(req, collector, fakeLogger); // Assert Assert.IsType <BadRequestResult>(result); A.CallTo(() => collector.AddAsync(A <LinkBundle> .Ignored, CancellationToken.None)).MustNotHaveHappened(); }
public async Task DeleteLinkBundleAllowsOtherOwnerToDeleteBundles() { // Arrange LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = "samplelink" }; _mockService.Setup(service => service.GetUserAccountEmail()) .Returns("*****@*****.**"); _mockService.Setup(service => service.FindLinkBundleAsync(linkBundle.VanityUrl)) .ReturnsAsync(linkBundle); // Act ActionResult <LinkBundle> result = await _linksController.DeleteLinkBundleAsync(linkBundle.VanityUrl); // Assert Assert.IsType <NoContentResult>(result.Result); }
public static DialogResult Run(LinkBundle linkBundle) { var dilogResult = DialogResult.Cancel; linkBundle.PerformTransaction(linkBundle.Library.Context, linkBundleCopy => { using (var form = new FormEditBundle(linkBundleCopy)) { dilogResult = form.ShowDialog(MainController.Instance.MainForm); if (dilogResult == DialogResult.OK) linkBundleCopy.MarkAsModified(); return dilogResult == DialogResult.OK; } }, copyMethod => MainController.Instance.ProcessManager.Run("Preparing Data...", cancelationToken => copyMethod()), (context, original, current) => MainController.Instance.ProcessManager.Run("Saving Changes...", cancelationToken => { original.Save(context, current, false); })); return dilogResult; }
public async Task PatchLinkBundleReturnsForbiddenIfLinkBundleOwnedByOtherUser() { // Arrange LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = "samplelink" }; _mockService.Setup(service => service.GetUserAccountEmail()) .Returns("*****@*****.**"); _mockService.Setup(service => service.FindLinkBundleAsync(linkBundle.VanityUrl)) .ReturnsAsync(linkBundle); JsonPatchDocument <LinkBundle> patchReqDocument = new JsonPatchDocument <LinkBundle>(); // Act ActionResult <LinkBundle> result = await _linksController.PatchLinkBundleAsync(linkBundle.VanityUrl, patchReqDocument); // Assert Assert.IsType <ForbidResult>(result.Result); }
/// <summary> /// Validates that the Vanity Url name is not empty. If its empty, the system generates the new name. /// </summary> /// <param name="linkDocument"></param> private void ValidateVanityUrl(LinkBundle linkDocument) { string characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; if (string.IsNullOrWhiteSpace(linkDocument.VanityUrl)) { var code = new char[7]; var rng = new RNGCryptoServiceProvider(); var bytes = new byte[sizeof(uint)]; for (int i = 0; i < code.Length; i++) { rng.GetBytes(bytes); uint num = BitConverter.ToUInt32(bytes, 0) % (uint)characters.Length; code[i] = characters[(int)num]; } linkDocument.VanityUrl = new String(code); } // force lowercase linkDocument.VanityUrl = linkDocument.VanityUrl.ToLower(); }
public async Task PostLinkBundleReturnsConflictIfRecordExists() { // Arrange LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = string.Empty, Links = new List <Link> { new Link() } }; _mockService.Setup(service => service.LinkBundleExistsAsync(linkBundle.Id)) .ReturnsAsync(true); _mockService.Setup(r => r.CreateLinkBundleAsync(linkBundle)) .Throws(new DbUpdateException()); // Act ActionResult <LinkBundle> result = await _linksController.PostLinkBundleAsync(linkBundle); // Assert Assert.IsType <ConflictResult>(result.Result); }
/// <summary> /// Creates a new LinkBundle for the authenticated user /// </summary> /// <returns></returns> private async Task <(HttpResponseMessage, LinkBundle)> CreateLinkBundle() { var now = DateTime.UtcNow; var linkBundle = new LinkBundle { VanityUrl = $"int-test-{now.ToFileTimeUtc()}", UserId = this.b2cConfig.Username, Description = $"Link bundle created from unit test @ {now.ToLongDateString()}", Links = new List <Link> { new Link { Id = "https://www.microsoft.com", Url = "https://www.microsoft.com", Title = "Microsoft", Description = "Description for Microsoft" } } }; var jsonContent = new StringContent(JsonConvert.SerializeObject(linkBundle), System.Text.Encoding.UTF8, "application/json"); var response = await this.client.PostAsync($"/api/links", jsonContent); return(response, linkBundle); }
public async Task PostLinkBundleConvertsVanityUrlToLowerCase(string vanityUrl) { // Arrange LinkBundle expectedLinkBundle = null; LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = vanityUrl, Links = new List <Link> { new Link() } }; _mockService.Setup(r => r.CreateLinkBundleAsync(It.IsAny <LinkBundle>())) .Callback <LinkBundle>(x => expectedLinkBundle = x); // Act ActionResult <LinkBundle> result = await _linksController.PostLinkBundleAsync(linkBundle); // Assert Assert.IsType <CreatedAtActionResult>(result.Result); Assert.Equal(vanityUrl.ToLower(), expectedLinkBundle.VanityUrl); }
public async Task PostLinkBundlePopulatesVanityUrlIfNotProvided() { // Arrange LinkBundle expectedLinkBundle = null; LinkBundle linkBundle = new LinkBundle { UserId = "*****@*****.**", VanityUrl = string.Empty, Links = new List <Link> { new Link() } }; _mockService.Setup(r => r.CreateLinkBundleAsync(It.IsAny <LinkBundle>())) .Callback <LinkBundle>(x => expectedLinkBundle = x); // Act ActionResult <LinkBundle> result = await _linksController.PostLinkBundleAsync(linkBundle); // Assert Assert.IsType <CreatedAtActionResult>(result.Result); Assert.False(string.IsNullOrEmpty(expectedLinkBundle.VanityUrl)); }
public async Task RemoveLinkBundleAsync(LinkBundle linkBundle) { _context.LinkBundle.Remove(linkBundle); await _context.SaveChangesAsync(); }
public async Task CreateLinkBundleAsync(LinkBundle linkBundle) { _context.LinkBundle.Add(linkBundle); await _context.SaveChangesAsync(); }
public async Task UpdateLinkBundleAsync(LinkBundle linkBundle) { _context.Entry(linkBundle).State = EntityState.Modified; await _context.SaveChangesAsync(); }
public void AddLinkBundle(LinkBundle bundle, int position = -1) { var selectedLink = SelectedLinkRow; if (selectedLink != null) position = selectedLink.Index; _outsideChangesInProgress = true; var newLink = LinkBundleLink.Create(DataSource, bundle); if (position >= 0) ((List<BaseLibraryLink>)DataSource.Links).InsertItem(newLink, position); else DataSource.Links.AddItem(newLink); var newRow = InsertLinkRow(newLink, position); _outsideChangesInProgress = false; UpdateGridSize(); newRow.Selected = true; DataChanged?.Invoke(this, EventArgs.Empty); }
private void UpdateLinkBundleAdd(IDiagramModel model, Link link) { if (model == null) return; if (link == null) return; //?? some way not to participate in link bundling? //?? bundling all links between nodes, not specifically between pairs of ports Node fromnode = link.FromNode; if (fromnode == null) return; Node tonode = link.ToNode; if (tonode == null) return; Object todata = link.ToData; Object fromdata = link.FromData; // see if there's an existing LinkBundle between the nodes/ports Object fromparam = link.FromPortId; Object toparam = link.ToPortId; LinkBundle bundle = fromnode.FindBundle(fromparam, tonode, toparam); List<Link> bundledlinks = null; // if set to a list, we'll need a LinkBundle if one doesn't already exist ILinksModel lmodel = model as ILinksModel; if (lmodel != null) { // could be any number of links in either direction var linkdatas = lmodel.GetLinksBetweenNodes(fromdata, fromparam, todata, toparam) .Concat(lmodel.GetLinksBetweenNodes(todata, toparam, fromdata, fromparam)) .Distinct().ToList(); List<Link> links = linkdatas.Select(d => FindLinkForData(d, lmodel)).Where(l => l != null).ToList(); if (links.Count > 1) { bundledlinks = links; } } else { // in other models, even without multiple links from one node to another, there can be one in each direction Link fromto = FindLinkForData(fromdata, todata, model); Link tofrom = FindLinkForData(todata, fromdata, model); if (fromto != null && tofrom != null) { bundledlinks = new List<Link>(); bundledlinks.Add(fromto); bundledlinks.Add(tofrom); } } if (bundledlinks != null) { // need a LinkBundle if (bundle == null) { bundle = new LinkBundle() { Node1 = fromnode, Param1 = fromparam, Node2 = tonode, Param2 = toparam }; fromnode.AddBundle(bundle); tonode.AddBundle(bundle); } bundle.Links = bundledlinks; for (int i = 0; i < bundledlinks.Count; i++) { Link l = bundledlinks[i]; if (l.BundleIndex == 0) { int idx = NextIndex(bundledlinks); // always returns positive index l.Bundle = bundle; // update the back pointers l.BundleIndex = ((l.FromNode == bundle.Node1) ? idx : -idx); // negative if links go the other way l.InvalidateRelationships("curviness"); } } } }