diff --git a/Src/HoneyBear.HalClient.Unit.Tests/HalClientAsyncUnitTests.cs b/Src/HoneyBear.HalClient.Unit.Tests/HalClientAsyncUnitTests.cs index 634dc0d..6a607f9 100644 --- a/Src/HoneyBear.HalClient.Unit.Tests/HalClientAsyncUnitTests.cs +++ b/Src/HoneyBear.HalClient.Unit.Tests/HalClientAsyncUnitTests.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Threading.Tasks; using HoneyBear.HalClient.Unit.Tests.ProxyResources; using NUnit.Framework; @@ -52,7 +51,7 @@ public void Navigate_to_single_embedded_resource() Task Act(IHalClient sut) => sut .RootAsync(RootUri) .GetAsync("order", new {orderRef = _context.OrderRef}, Curie) - .GetAsync("orderitem", Curie); + .GetAsync("orderitem-query", Curie); _context.ActAsync(Act); @@ -85,13 +84,60 @@ public void Navigate_to_paged_embedded_resource() Task Act(IHalClient sut) => sut .RootAsync(RootUri) .GetAsync("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, Curie) - .GetAsync("order", Curie); + .GetAsync("order", new {orderRef = _context.OrderRef}, Curie); _context.ActAsync(Act); _context.AssertThatEmbeddedPagedResourceIsPresent(); } + [Test] + public void Navigate_to_paged_embedded_resource_and_navigate_to_embedded_resource_via_parent() + { + _context + .ArrangeHomeResource() + .ArrangePagedResource(); + + Task Act(IHalClient sut) + { + var resource = + sut + .RootAsync(RootUri) + .GetAsync("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, Curie) + .Item(); + + return sut.GetAsync(resource, "order", new {orderRef = _context.OrderRef}, Curie); + } + + _context.ActAsync(Act); + + _context.AssertThatSingleResourceIsPresent(); + } + + [Test] + public void Navigate_to_paged_linked_resource_and_navigate_to_linked_resource_via_parent() + { + _context + .ArrangeHomeResource() + .ArrangePagedResourceWithLinkedResources() + .ArrangeSingleResource(); + + Task Act(IHalClient sut) + { + var resource = + sut + .RootAsync(RootUri) + .GetAsync("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, Curie) + .Item(); + + return sut.GetAsync(resource, "order", new {orderRef = _context.OrderRef}, Curie); + } + + _context.ActAsync(Act); + + _context.AssertThatSingleResourceIsPresent(); + } + [Test] public void Navigate_to_paged_embedded_resource_and_navigate_to_embedded_resource_array() { @@ -107,15 +153,12 @@ Task Act(IHalClient sut) .GetAsync("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, Curie) .GetAsync("order", Curie); - var order = - nav - .Items() - .First(); + var order = nav.Item(); return nav .GetAsync(order, "orderitem-query", Curie) - .GetAsync("orderitem", Curie); + .GetAsync("orderitem", new {orderItemRef = _context.OrderItemRef}, Curie); } _context.ActAsync(Act); @@ -136,17 +179,14 @@ Task Act(IHalClient sut) sut .RootAsync(RootUri) .GetAsync("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, Curie) - .GetAsync("order", Curie); + .GetAsync("order", new {orderRef = _context.OrderRef}, Curie); - var order = - nav - .Items() - .First(); + var order = nav.Item(); return nav .GetAsync(order, "orderitem-query", Curie) - .GetAsync("orderitem", Curie); + .GetAsync("orderitem", new {orderItemRef = _context.OrderItemRef}, Curie); } _context.ActAsync(Act); @@ -560,7 +600,7 @@ Task Act(IHalClient sut) => sut } [Test] - public void HalClientAsync_can_be_created_with_specifed_HttpClient() => + public void HalClientAsync_can_be_created_with_specified_HttpClient() => _context.AssertThatHttpClientCanBeProvided(); [Test] @@ -624,5 +664,21 @@ public void Throws_exception_when_HTTP_request_is_unsuccessful() Assert.Throws(() => _context.ActAsync(Act)); } + + [Test] + public void Throws_exception_when_resource_does_not_exist() + { + _context + .ArrangeHomeResource() + .ArrangePagedResource() + .ArrangeFailedOrderRequest(); + + Task Act(IHalClient sut) => sut + .RootAsync(RootUri) + .GetAsync("order-queryby-user", new { userRef = HalClientTestContext.UserRef }, Curie) + .GetAsync("order", new { orderRef = HalClientTestContext.NonExistentOrderRef }, Curie); + + Assert.Throws(() => _context.ActAsync(Act)); + } } } \ No newline at end of file diff --git a/Src/HoneyBear.HalClient.Unit.Tests/HalClientTestContext.cs b/Src/HoneyBear.HalClient.Unit.Tests/HalClientTestContext.cs index daa0001..594c55d 100644 --- a/Src/HoneyBear.HalClient.Unit.Tests/HalClientTestContext.cs +++ b/Src/HoneyBear.HalClient.Unit.Tests/HalClientTestContext.cs @@ -25,9 +25,11 @@ internal sealed class HalClientTestContext public const string RootUri = "/v1/version/1"; public const string Curie = "retail"; public Guid OrderRef => _order.OrderRef; + public Guid OrderItemRef => _orderItem.OrderItemRef; public OrderAdd OrderAdd { get; } public OrderEdit OrderEdit { get; } public static readonly Guid UserRef = Guid.NewGuid(); + public static readonly Guid NonExistentOrderRef = Guid.NewGuid(); private readonly IHalClient _sut; private readonly IJsonHttpClient _http; @@ -35,6 +37,7 @@ internal sealed class HalClientTestContext private readonly dynamic _curies; private readonly Version _version; private readonly Order _order; + private readonly Order _otherOrder; private readonly OrderItem _orderItem; private readonly PagedList _paged; private bool _hasCurie; @@ -48,7 +51,8 @@ public HalClientTestContext() _http = _fixture.Freeze(); _version = _fixture.Create(); - _order = _fixture.Build().With(x => x.DeliveryDate, null).Create(); + _order = _fixture.Build().With(x => x.OrderNumber, "2").With(x => x.DeliveryDate, null).Create(); + _otherOrder = _fixture.Build().With(x => x.OrderNumber, "1").Create(); _orderItem = _fixture.Create(); _paged = _fixture.Create(); OrderAdd = _fixture.Create(); @@ -101,16 +105,27 @@ public HalClientTestContext ArrangeSingleResource() return this; } - public void ArrangePagedResource() => + public HalClientTestContext ArrangePagedResource() + { ArrangeGet($"/v1/order?userRef={UserRef}", CreatePagedResourceJson()); + return this; + } + + public HalClientTestContext ArrangePagedResourceWithLinkedResources() + { + ArrangeGet($"/v1/order?userRef={UserRef}", CreatePagedResourceWithLinkedResourcesJson()); + + return this; + } + public void ArrangePagedResourceWithEmbeddedArrayOfResources() => ArrangeGet($"/v1/order?userRef={UserRef}", CreatePagedResourceWithEmbeddedArrayOfResourcesJson()); public void ArrangePagedResourceWithLinkedArrayOfResources() { ArrangeGet($"/v1/order?userRef={UserRef}", CreatePagedResourceWithLinkedArrayOfResourcesJson()); - ArrangeGet($"/v1/orderitem?orderRef={OrderRef}", CreatePagedResourceWithArrayOfResourcesJson()); + ArrangeGet($"/v1/orderitem?orderRef={OrderRef}", CreatePagedResourceWithArrayOfResourcesJson(OrderRef)); } public void ArrangeDefaultPagedResource() => @@ -171,6 +186,11 @@ public void ArrangeFailedHomeRequest() => .Expect(h => h.GetAsync(RootUri)) .Return(NotFound()); + public void ArrangeFailedOrderRequest() => + _http + .Expect(h => h.GetAsync($"/v1/order/{NonExistentOrderRef}")) + .Return(NotFound()); + public void Act(Func act) => _result = act(_sut); @@ -363,12 +383,13 @@ private object CreateSingleResourceJson() => curies = _curies, self = new {href = $"/v1/order/{OrderRef}"}, retail_order_edit = new {href = $"/v1/order/{OrderRef}"}, - retail_order_delete = new {href = $"/v1/order/{OrderRef}"} + retail_order_delete = new {href = $"/v1/order/{OrderRef}"}, + retail_orderitem_query = new {href = $"/v1/orderitem?orderRef={OrderRef}"} }, _embedded = new { - retail_orderitem = + retail_orderitem_query = new[] { new @@ -402,12 +423,13 @@ private object CreateSingleResourceJsonWithoutCurie() => { self = new {href = $"/v1/order/{OrderRef}"}, order_edit = new {href = $"/v1/order/{OrderRef}"}, - order_delete = new {href = $"/v1/order/{OrderRef}"} + order_delete = new {href = $"/v1/order/{OrderRef}"}, + orderitem_query = new {href = $"/v1/orderitem?orderRef={OrderRef}"} }, _embedded = new { - orderitem = + orderitem_query = new[] { new @@ -439,7 +461,8 @@ private object CreatePagedResourceJson() => new { curies = _curies, - self = new {href = $"/v1/order?userRef={UserRef}"} + self = new {href = $"/v1/order?userRef={UserRef}"}, + retail_order = new {href = "/v1/order/{orderRef}", templated = true} }, _embedded = new @@ -447,6 +470,19 @@ private object CreatePagedResourceJson() => retail_order = new[] { + new + { + _otherOrder.OrderRef, + _otherOrder.OrderNumber, + _otherOrder.Status, + _otherOrder.Total, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/order/{_otherOrder.OrderRef}"} + } + }, new { _order.OrderRef, @@ -457,13 +493,29 @@ private object CreatePagedResourceJson() => new { curies = _curies, - self = new {href = $"/v1/order/{OrderRef}"} + self = new {href = $"/v1/order/{_order.OrderRef}"} } } } } }; + private object CreatePagedResourceWithLinkedResourcesJson() => + new + { + _paged.PageNumber, + _paged.PageSize, + _paged.KnownPagesAvailable, + _paged.TotalItemsCount, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/order?userRef={UserRef}"}, + retail_order = new {href = "/v1/order/{orderRef}", templated = true} + } + }; + private object CreatePagedResourceWithEmbeddedArrayOfResourcesJson() => new { @@ -475,7 +527,8 @@ private object CreatePagedResourceWithEmbeddedArrayOfResourcesJson() => new { curies = _curies, - self = new {href = $"/v1/order?userRef={UserRef}"} + self = new {href = $"/v1/order?userRef={UserRef}"}, + retail_order = new {href = "/v1/order/{orderRef}", templated = true} }, _embedded = new @@ -483,6 +536,26 @@ private object CreatePagedResourceWithEmbeddedArrayOfResourcesJson() => retail_order = new[] { + new + { + _otherOrder.OrderRef, + _otherOrder.OrderNumber, + _otherOrder.Status, + _otherOrder.DeliveryDate, + _otherOrder.Total, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/order/{_otherOrder.OrderRef}"}, + retail_orderitem_query = new {href = $"/v1/orderitem?orderRef={_otherOrder.OrderRef}"} + }, + _embedded = + new + { + retail_orderitem_query = CreatePagedResourceWithArrayOfResourcesJson(_otherOrder.OrderRef) + } + }, new { _order.OrderRef, @@ -494,12 +567,13 @@ private object CreatePagedResourceWithEmbeddedArrayOfResourcesJson() => new { curies = _curies, - self = new {href = $"/v1/order/{OrderRef}"} + self = new {href = $"/v1/order/{_order.OrderRef}"}, + retail_orderitem_query = new {href = $"/v1/orderitem?orderRef={_order.OrderRef}"} }, _embedded = new { - retail_orderitem_query = CreatePagedResourceWithArrayOfResourcesJson() + retail_orderitem_query = CreatePagedResourceWithArrayOfResourcesJson(_order.OrderRef) } } } @@ -517,7 +591,8 @@ private object CreatePagedResourceWithLinkedArrayOfResourcesJson() => new { curies = _curies, - self = new {href = $"/v1/order?userRef={UserRef}"} + self = new {href = $"/v1/order?userRef={UserRef}"}, + retail_order = new {href = "/v1/order/{orderRef}", templated = true} }, _embedded = new @@ -525,6 +600,21 @@ private object CreatePagedResourceWithLinkedArrayOfResourcesJson() => retail_order = new[] { + new + { + _otherOrder.OrderRef, + _otherOrder.OrderNumber, + _otherOrder.Status, + _otherOrder.DeliveryDate, + _otherOrder.Total, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/order/{_otherOrder.OrderRef}"}, + retail_orderitem_query = new {href = $"/v1/orderitem?orderRef={_otherOrder.OrderRef}"} + } + }, new { _order.OrderRef, @@ -536,16 +626,18 @@ private object CreatePagedResourceWithLinkedArrayOfResourcesJson() => new { curies = _curies, - self = new {href = $"/v1/order/{OrderRef}"}, - retail_orderitem_query = new {href = $"/v1/orderitem?orderRef={OrderRef}"} + self = new {href = $"/v1/order/{_order.OrderRef}"}, + retail_orderitem_query = new {href = $"/v1/orderitem?orderRef={_order.OrderRef}"} } } } } }; - private object CreatePagedResourceWithArrayOfResourcesJson() => - new + private object CreatePagedResourceWithArrayOfResourcesJson(Guid orderRef) + { + var other = _fixture.Create(); + return new { _paged.PageNumber, _paged.PageSize, @@ -555,7 +647,8 @@ private object CreatePagedResourceWithArrayOfResourcesJson() => new { curies = _curies, - self = new {href = $"/v1/orderitem?orderRef={OrderRef}"} + self = new {href = $"/v1/orderitem?orderRef={orderRef}"}, + retail_orderitem = new {href = "/v1/orderitem/{orderItemRef}", templated = true} }, _embedded = new @@ -580,6 +673,7 @@ private object CreatePagedResourceWithArrayOfResourcesJson() => } } }; + } private object CreateDefaultPagedResourceJson() => new @@ -592,7 +686,8 @@ private object CreateDefaultPagedResourceJson() => new { curies = _curies, - self = new {href = "/v1/order"} + self = new {href = "/v1/order"}, + retail_order = new {href = "/v1/order/{orderRef}", templated = true} }, _embedded = new @@ -600,6 +695,35 @@ private object CreateDefaultPagedResourceJson() => retail_order = new[] { + new + { + _otherOrder.OrderRef, + _otherOrder.OrderNumber, + _otherOrder.Status, + _otherOrder.DeliveryDate, + _otherOrder.Total, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/order/{_otherOrder.OrderRef}"} + }, + _embedded = + new + { + retail_user = + new + { + UserRef, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/user/{UserRef}"} + } + } + } + }, new { _order.OrderRef, @@ -611,7 +735,7 @@ private object CreateDefaultPagedResourceJson() => new { curies = _curies, - self = new {href = $"/v1/order/{OrderRef}"} + self = new {href = $"/v1/order/{_order.OrderRef}"} }, _embedded = new diff --git a/Src/HoneyBear.HalClient.Unit.Tests/HalClientUnitTests.cs b/Src/HoneyBear.HalClient.Unit.Tests/HalClientUnitTests.cs index 9637698..2afdb2f 100644 --- a/Src/HoneyBear.HalClient.Unit.Tests/HalClientUnitTests.cs +++ b/Src/HoneyBear.HalClient.Unit.Tests/HalClientUnitTests.cs @@ -1,5 +1,4 @@ -using System.Linq; -using HoneyBear.HalClient.Models; +using HoneyBear.HalClient.Models; using HoneyBear.HalClient.Unit.Tests.ProxyResources; using NUnit.Framework; @@ -51,7 +50,7 @@ public void Navigate_to_single_embedded_resource() IHalClient Act(IHalClient sut) => sut .Root(RootUri) .Get("order", new {orderRef = _context.OrderRef}, Curie) - .Get("orderitem", Curie); + .Get("orderitem-query", Curie); _context.Act(Act); @@ -84,13 +83,60 @@ public void Navigate_to_paged_embedded_resource() IHalClient Act(IHalClient sut) => sut .Root(RootUri) .Get("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, Curie) - .Get("order", Curie); + .Get("order", new {orderRef = _context.OrderRef}, Curie); _context.Act(Act); _context.AssertThatEmbeddedPagedResourceIsPresent(); } + [Test] + public void Navigate_to_paged_embedded_resource_and_navigate_to_embedded_resource_via_parent() + { + _context + .ArrangeHomeResource() + .ArrangePagedResource(); + + IHalClient Act(IHalClient sut) + { + var resource = + sut + .Root(RootUri) + .Get("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, Curie) + .Item(); + + return sut.Get(resource, "order", new {orderRef = _context.OrderRef}, Curie); + } + + _context.Act(Act); + + _context.AssertThatSingleResourceIsPresent(); + } + + [Test] + public void Navigate_to_paged_linked_resource_and_navigate_to_linked_resource_via_parent() + { + _context + .ArrangeHomeResource() + .ArrangePagedResourceWithLinkedResources() + .ArrangeSingleResource(); + + IHalClient Act(IHalClient sut) + { + var resource = + sut + .Root(RootUri) + .Get("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, Curie) + .Item(); + + return sut.Get(resource, "order", new {orderRef = _context.OrderRef}, Curie); + } + + _context.Act(Act); + + _context.AssertThatSingleResourceIsPresent(); + } + [Test] public void Navigate_to_paged_embedded_resource_and_navigate_to_embedded_resource_array() { @@ -104,14 +150,13 @@ IHalClient Act(IHalClient sut) sut .Root(RootUri) .Get("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, Curie) - .Get("order", Curie) - .Items() - .First(); + .Get("order", new {orderRef = _context.OrderRef}, Curie) + .Item(); return sut .Get(order, "orderitem-query", Curie) - .Get("orderitem", Curie); + .Get("orderitem", new {orderItemRef = _context.OrderItemRef}, Curie); } _context.Act(Act); @@ -132,14 +177,13 @@ IHalClient Act(IHalClient sut) sut .Root(RootUri) .Get("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, Curie) - .Get("order", Curie) - .Items() - .First(); + .Get("order", new {orderRef = _context.OrderRef}, Curie) + .Item(); return sut .Get(order, "orderitem-query", Curie) - .Get("orderitem", Curie); + .Get("orderitem", new {orderItemRef = _context.OrderItemRef}, Curie); } _context.Act(Act); @@ -553,7 +597,7 @@ IHalClient Act(IHalClient sut) => sut } [Test] - public void HalClient_can_be_created_with_specifed_HttpClient() => + public void HalClient_can_be_created_with_specified_HttpClient() => _context.AssertThatHttpClientCanBeProvided(); [Test] @@ -617,5 +661,21 @@ public void Throws_exception_when_HTTP_request_is_unsuccessful() Assert.Throws(() => _context.Act(Act)); } + + [Test] + public void Throws_exception_when_resource_does_not_exist() + { + _context + .ArrangeHomeResource() + .ArrangePagedResource() + .ArrangeFailedOrderRequest(); + + IHalClient Act(IHalClient sut) => sut + .Root(RootUri) + .Get("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, Curie) + .Get("order", new {orderRef = HalClientTestContext.NonExistentOrderRef}, Curie); + + Assert.Throws(() => _context.Act(Act)); + } } } \ No newline at end of file diff --git a/Src/HoneyBear.HalClient/HalClient.cs b/Src/HoneyBear.HalClient/HalClient.cs index c0c4fa2..86844b5 100644 --- a/Src/HoneyBear.HalClient/HalClient.cs +++ b/Src/HoneyBear.HalClient/HalClient.cs @@ -177,12 +177,8 @@ public IHalClient Get(string rel, object parameters, string curie) { var relationship = Relationship(rel, curie); - var embedded = _current.FirstOrDefault(r => r.Embedded.Any(e => e.Rel == relationship)); - if (embedded != null) - { - var current = embedded.Embedded.Where(e => e.Rel == relationship); - return new HalClient(this, current); - } + if (TryGetEmbedded(relationship, parameters, out var embedded)) + return new HalClient(this, embedded); return BuildAndExecute(relationship, parameters, uri => _client.GetAsync(uri)); } @@ -199,12 +195,8 @@ public async Task GetAsync(string rel, object parameters, string cur { var relationship = Relationship(rel, curie); - var embedded = _current.FirstOrDefault(r => r.Embedded.Any(e => e.Rel == relationship)); - if (embedded != null) - { - var current = embedded.Embedded.Where(e => e.Rel == relationship); - return new HalClient(this, current); - } + if (TryGetEmbedded(relationship, parameters, out var embedded)) + return new HalClient(this, embedded); return await BuildAndExecuteAsync(relationship, parameters, uri => _client.GetAsync(uri)); } @@ -253,16 +245,18 @@ public IHalClient Get(IResource resource, string rel, object parameters, string { var relationship = Relationship(rel, curie); - if (resource.Embedded.Any(e => e.Rel == relationship)) - { - var current = resource.Embedded.Where(e => e.Rel == relationship); - return new HalClient(this, current); - } + // If there are no parameters, check if there are any embedded resources with the given link relation + if (parameters == null && TryGetEmbedded(resource, relationship, out var embedded)) + return new HalClient(this, embedded); var link = resource.Links.FirstOrDefault(l => l.Rel == relationship); if (link == null) throw new FailedToResolveRelationship(relationship); + // If there are parameters, check if any of the embedded resources match on the self link URI + if (parameters != null && TryGetEmbedded(resource, relationship, parameters, link, out embedded)) + return new HalClient(this, embedded); + return Execute(Construct(link, parameters), uri => _client.GetAsync(uri)); } @@ -279,16 +273,18 @@ public async Task GetAsync(IResource resource, string rel, object pa { var relationship = Relationship(rel, curie); - if (resource.Embedded.Any(e => e.Rel == relationship)) - { - var current = resource.Embedded.Where(e => e.Rel == relationship); - return new HalClient(this, current); - } + // If there are no parameters, check if there are any embedded resources with the given link relation + if (parameters == null && TryGetEmbedded(resource, relationship, out var embedded)) + return new HalClient(this, embedded); var link = resource.Links.FirstOrDefault(l => l.Rel == relationship); if (link == null) throw new FailedToResolveRelationship(relationship); + // If there are parameters, check if any of the embedded resources match on the self link URI + if (parameters != null && TryGetEmbedded(resource, relationship, parameters, link, out embedded)) + return new HalClient(this, embedded); + return await ExecuteAsync(Construct(link, parameters), uri => _client.GetAsync(uri)); } @@ -561,23 +557,87 @@ public bool Has(string rel, string curie) || _current.Any(r => r.Links.Any(l => l.Rel == relationship)); } + private bool TryGetEmbedded(string relationship, object parameters, out IEnumerable embedded) + { + embedded = null; + + // If there are no parameters, check if there are any embedded resources with the given link relation + if (parameters == null) + return TryGetEmbedded(relationship, out embedded); + + // Check if any of the current resource's links match the given link relation + var current = _current.FirstOrDefault(r => r.Links.Any(l => l.Rel == relationship)); + if (current == null) + return false; + + // Check if any of the embedded resources match on the self link URI + var allEmbedded = _current.FirstOrDefault(r => r.Embedded.Any(e => e.Rel == relationship && e.Links.Any(s => s.Rel == "self"))); + if (allEmbedded == null) + return false; + + var linkUri = Construct(current.Links.First(l => l.Rel == relationship), parameters); + + embedded = + allEmbedded.Embedded.Where(e => + e.Rel == relationship + && e.Links.Any(s => s.Rel == "self") + && Construct(e.Links.First(s => s.Rel == "self"), parameters) == linkUri); + return embedded.Any(); + } + + private bool TryGetEmbedded(string relationship, out IEnumerable embedded) + { + var resource = _current.FirstOrDefault(r => r.Embedded.Any(e => e.Rel == relationship)); + if (resource == null) + { + embedded = null; + return false; + } + + embedded = resource.Embedded.Where(e => e.Rel == relationship); + return true; + } + + private static bool TryGetEmbedded(IResource resource, string relationship, out IEnumerable embedded) + { + if (resource.Embedded.Any(e => e.Rel == relationship)) + { + embedded = resource.Embedded.Where(e => e.Rel == relationship); + return true; + } + + embedded = null; + return false; + } + + private static bool TryGetEmbedded(IResource resource, string relationship, object parameters, ILink link, out IEnumerable embedded) + { + var linkUri = Construct(link, parameters); + embedded = + resource.Embedded.Where(e => + e.Rel == relationship + && e.Links.Any(s => s.Rel == "self") + && Construct(e.Links.First(s => s.Rel == "self"), parameters) == linkUri); + return embedded.Any(); + } + private IHalClient BuildAndExecute(string relationship, object parameters, Func> command) { var resource = _current.FirstOrDefault(r => r.Links.Any(l => l.Rel == relationship)); if (resource == null) throw new FailedToResolveRelationship(relationship); - var link = resource.Links.FirstOrDefault(l => l.Rel == relationship); + var link = resource.Links.First(l => l.Rel == relationship); return Execute(Construct(link, parameters), command); } - internal Task BuildAndExecuteAsync(string relationship, object parameters, Func> command) + private Task BuildAndExecuteAsync(string relationship, object parameters, Func> command) { var resource = _current.FirstOrDefault(r => r.Links.Any(l => l.Rel == relationship)); if (resource == null) throw new FailedToResolveRelationship(relationship); - var link = resource.Links.FirstOrDefault(l => l.Rel == relationship); + var link = resource.Links.First(l => l.Rel == relationship); return ExecuteAsync(Construct(link, parameters), command); } @@ -588,7 +648,7 @@ private IHalClient Execute(string uri, Func> c return Process(result); } - internal async Task ExecuteAsync(string uri, Func> command) + private async Task ExecuteAsync(string uri, Func> command) { var result = await command(uri); diff --git a/Src/HoneyBear.HalClient/HoneyBear.HalClient.csproj b/Src/HoneyBear.HalClient/HoneyBear.HalClient.csproj index 3d41813..101c275 100644 --- a/Src/HoneyBear.HalClient/HoneyBear.HalClient.csproj +++ b/Src/HoneyBear.HalClient/HoneyBear.HalClient.csproj @@ -15,7 +15,7 @@ A lightweight fluent .NET client for navigating and consuming HAL APIs. Includes support for .NET Standard. bin\$(Configuration)\$(TargetFramework)\HoneyBear.HalClient.xml true - https://github.com/eoin55/HoneyBear.HalClient/blob/master/LICENSE + https://github.com/eoin55/HoneyBear.HalClient/blob/master/LICENSE https://github.com/eoin55/HoneyBear.HalClient HAL JSON Hypermedia HATEOAS REST DotNetCore NetStandard https://github.com/eoin55/HoneyBear.HalClient diff --git a/Src/HoneyBear.HalClient/Models/ResourceConverterExtenstions.cs b/Src/HoneyBear.HalClient/Models/ResourceConverterExtensions.cs similarity index 97% rename from Src/HoneyBear.HalClient/Models/ResourceConverterExtenstions.cs rename to Src/HoneyBear.HalClient/Models/ResourceConverterExtensions.cs index ac1b30a..e547bf4 100644 --- a/Src/HoneyBear.HalClient/Models/ResourceConverterExtenstions.cs +++ b/Src/HoneyBear.HalClient/Models/ResourceConverterExtensions.cs @@ -11,7 +11,7 @@ namespace HoneyBear.HalClient.Models /// /// Contains a set of IResource extensions. /// - public static class ResourceConverterExtenstions + public static class ResourceConverterExtensions { internal static T Data(this IResource source) where T : class, new() diff --git a/appveyor.yml b/appveyor.yml index 6315e5e..6c57fae 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.2.{build} +version: 2.3.{build} image: Visual Studio 2017 skip_tags: true configuration: Release @@ -24,7 +24,7 @@ build_script: after_test: - ps: >- . $env:USERPROFILE\.nuget\packages\OpenCover\4.6.519\tools\OpenCover.Console.exe ` - -register:user ` + -register:Path64 ` -filter:"+[HoneyBear.HalClient]*" ` -target:"$env:USERPROFILE\.nuget\packages\NUnit.ConsoleRunner\3.8.0\tools\nunit3-console.exe" ` -targetargs:".\Src\HoneyBear.HalClient.Unit.Tests\bin\$env:CONFIGURATION\net471\HoneyBear.HalClient.Unit.Tests.dll" `