From dd58ae514863ec38cce525362649a6087a2f1c01 Mon Sep 17 00:00:00 2001 From: Eoin O'Connor Date: Mon, 16 May 2016 22:19:09 +1000 Subject: [PATCH] [NoTicket] - Extended the HalClient to allow navigation of the links of embedded resources. --- README.md | 2 +- .../HalClientTestContext.cs | 137 ++++++++++++++++++ .../HalClientUnitTests.cs | 60 ++++++++ Src/HoneyBear.HalClient/HalClient.cs | 57 ++++++++ Src/HoneyBear.HalClient/IHalClient.cs | 42 ++++++ 5 files changed, 297 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d919fc..651fb37 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![Build Status](https://ci.appveyor.com/api/projects/status/github/eoin55/HoneyBear.HalClient?branch=master&svg=true) [![Coverage Status](https://coveralls.io/repos/eoin55/HoneyBear.HalClient/badge.svg?branch=master&service=github)](https://coveralls.io/github/eoin55/HoneyBear.HalClient?branch=master) -[![NuGet version](https://badge.fury.io/nu/honeybear.halclient.svg)](https://badge.fury.io/nu/honeybear.halclient) +[![NuGet Version](https://img.shields.io/nuget/v/HoneyBear.HalClient.svg)](https://www.nuget.org/packages/HoneyBear.HalClient/) A lightweight fluent .NET client for navigating and consuming HAL APIs. diff --git a/Src/HoneyBear.HalClient.Unit.Tests/HalClientTestContext.cs b/Src/HoneyBear.HalClient.Unit.Tests/HalClientTestContext.cs index 1a9f39d..a352742 100644 --- a/Src/HoneyBear.HalClient.Unit.Tests/HalClientTestContext.cs +++ b/Src/HoneyBear.HalClient.Unit.Tests/HalClientTestContext.cs @@ -104,6 +104,17 @@ public void ArrangePagedResource() ArrangeGet($"/v1/order?userRef={UserRef}", CreatePagedResourceJson()); } + public void ArrangePagedResourceWithEmbeddedArrayOfResources() + { + ArrangeGet($"/v1/order?userRef={UserRef}", CreatePagedResourceWithEmbeddedArrayOfResourcesJson()); + } + + public void ArrangePagedResourceWithLinkedArrayOfResources() + { + ArrangeGet($"/v1/order?userRef={UserRef}", CreatePagedResourceWithLinkedArrayOfResourcesJson()); + ArrangeGet($"/v1/orderitem?orderRef={OrderRef}", CreatePagedResourceWithArrayOfResourcesJson()); + } + public void ArrangeDefaultPagedResource() { ArrangeGet("/v1/order", CreateDefaultPagedResourceJson()); @@ -191,6 +202,17 @@ public void AssertThatEmbeddedPagedResourceIsPresent() _order.AsSource().OfLikeness().ShouldEqual(resource); } + public void AssertThatResourceArrayIsPresent() + { + var resource = _result.Items().Data().First(); + var expected = + _orderItem + .AsSource() + .OfLikeness() + .WithCollectionInnerLikeness(d => d.SerialNumbers, s => s.SerialNumbers); + expected.ShouldEqual(resource); + } + public void AssertThatSingleEmbeddedResourceIsPresent() { var resource = _result.Items().Data().First(); @@ -446,6 +468,121 @@ private object CreatePagedResourceJson() => } }; + private object CreatePagedResourceWithEmbeddedArrayOfResourcesJson() => + new + { + _paged.PageNumber, + _paged.PageSize, + _paged.KnownPagesAvailable, + _paged.TotalItemsCount, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/order?userRef={UserRef}"} + }, + _embedded = + new + { + retail_order = + new[] + { + new + { + _order.OrderRef, + _order.OrderNumber, + _order.Status, + _order.Total, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/order/{OrderRef}"} + }, + _embedded = + new + { + retail_orderitem_query = CreatePagedResourceWithArrayOfResourcesJson() + } + } + } + } + }; + + private object CreatePagedResourceWithLinkedArrayOfResourcesJson() => + new + { + _paged.PageNumber, + _paged.PageSize, + _paged.KnownPagesAvailable, + _paged.TotalItemsCount, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/order?userRef={UserRef}"} + }, + _embedded = + new + { + retail_order = + new[] + { + new + { + _order.OrderRef, + _order.OrderNumber, + _order.Status, + _order.Total, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/order/{OrderRef}"}, + retail_orderitem_query = new {href = $"/v1/orderitem?orderRef={OrderRef}"} + } + } + } + } + }; + + private object CreatePagedResourceWithArrayOfResourcesJson() => + new + { + _paged.PageNumber, + _paged.PageSize, + _paged.KnownPagesAvailable, + _paged.TotalItemsCount, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/orderitem?orderRef={OrderRef}"} + }, + _embedded = + new + { + retail_orderitem = + new[] + { + new + { + _orderItem.OrderItemRef, + _orderItem.Status, + _orderItem.Total, + _orderItem.Quantity, + _orderItem.SerialNumbers, + _links = + new + { + curies = _curies, + self = new {href = $"/v1/orderitem/{_orderItem.OrderItemRef}"} + } + } + } + } + }; + private object CreateDefaultPagedResourceJson() => new { diff --git a/Src/HoneyBear.HalClient.Unit.Tests/HalClientUnitTests.cs b/Src/HoneyBear.HalClient.Unit.Tests/HalClientUnitTests.cs index dd4be49..9ab41ec 100644 --- a/Src/HoneyBear.HalClient.Unit.Tests/HalClientUnitTests.cs +++ b/Src/HoneyBear.HalClient.Unit.Tests/HalClientUnitTests.cs @@ -1,6 +1,8 @@ using System; +using System.Linq; using System.Net.Http; using HoneyBear.HalClient.Models; +using HoneyBear.HalClient.Unit.Tests.ProxyResources; using NUnit.Framework; namespace HoneyBear.HalClient.Unit.Tests @@ -92,6 +94,64 @@ public void Navigate_to_paged_embedded_resource() _context.AssertThatEmbeddedPagedResourceIsPresent(); } + [Test] + public void Navigate_to_paged_embedded_resource_and_navigate_to_embedded_resource_array() + { + _context + .ArrangeHomeResource() + .ArrangePagedResourceWithEmbeddedArrayOfResources(); + + Func act = + sut => + { + var order = + sut + .Root(HalClientTestContext.RootUri) + .Get("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, HalClientTestContext.Curie) + .Get("order", HalClientTestContext.Curie) + .Items() + .First(); + + sut + .Get(order, "orderitem-query", HalClientTestContext.Curie) + .Get("orderitem", HalClientTestContext.Curie); + + return sut; + }; + _context.Act(act); + + _context.AssertThatResourceArrayIsPresent(); + } + + [Test] + public void Navigate_to_paged_embedded_resource_and_navigate_to_linked_resource_array() + { + _context + .ArrangeHomeResource() + .ArrangePagedResourceWithLinkedArrayOfResources(); + + Func act = + sut => + { + var order = + sut + .Root(HalClientTestContext.RootUri) + .Get("order-queryby-user", new {userRef = HalClientTestContext.UserRef}, HalClientTestContext.Curie) + .Get("order", HalClientTestContext.Curie) + .Items() + .First(); + + sut + .Get(order, "orderitem-query", HalClientTestContext.Curie) + .Get("orderitem", HalClientTestContext.Curie); + + return sut; + }; + _context.Act(act); + + _context.AssertThatResourceArrayIsPresent(); + } + [Test] public void Create_resource() { diff --git a/Src/HoneyBear.HalClient/HalClient.cs b/Src/HoneyBear.HalClient/HalClient.cs index db33845..88c4a1e 100644 --- a/Src/HoneyBear.HalClient/HalClient.cs +++ b/Src/HoneyBear.HalClient/HalClient.cs @@ -170,6 +170,63 @@ public IHalClient Get(string rel, object parameters, string curie) return BuildAndExecute(relationship, parameters, uri => _client.GetAsync(uri)); } + /// + /// Navigates the given link relation and stores the the returned resource(s). + /// + /// The current . + /// The link relation to follow. + /// The updated . + /// + public IHalClient Get(IResource resource, string rel) => Get(resource, rel, null, null); + + /// + /// Navigates the given link relation and stores the the returned resource(s). + /// + /// The current . + /// The link relation to follow. + /// The curie of the link relation. + /// The updated . + /// + public IHalClient Get(IResource resource, string rel, string curie) => Get(resource, rel, null, curie); + + /// + /// Navigates the given templated link relation and stores the the returned resource(s). + /// + /// The current . + /// The templated link relation to follow. + /// An anonymous object containing the template parameters to apply. + /// The updated . + /// + /// + public IHalClient Get(IResource resource, string rel, object parameters) => Get(resource, rel, parameters, null); + + /// + /// Navigates the given templated link relation and stores the the returned resource(s). + /// + /// The current . + /// The templated link relation to follow. + /// An anonymous object containing the template parameters to apply. + /// The curie of the link relation. + /// The updated . + /// + /// + public IHalClient Get(IResource resource, string rel, object parameters, string curie) + { + var relationship = Relationship(rel, curie); + + if (resource.Embedded.Any(e => e.Rel == relationship)) + { + _current = resource.Embedded.Where(e => e.Rel == relationship); + return this; + } + + var link = resource.Links.FirstOrDefault(l => l.Rel == relationship); + if (link == null) + throw new FailedToResolveRelationship(relationship); + + return Execute(Construct(link, parameters), uri => _client.GetAsync(uri)); + } + /// /// Makes a HTTP POST request to the given link relation on the most recently navigated resource. /// diff --git a/Src/HoneyBear.HalClient/IHalClient.cs b/Src/HoneyBear.HalClient/IHalClient.cs index e23132e..6dcd433 100644 --- a/Src/HoneyBear.HalClient/IHalClient.cs +++ b/Src/HoneyBear.HalClient/IHalClient.cs @@ -75,6 +75,48 @@ public interface IHalClient /// IHalClient Get(string rel, object parameters, string curie); + /// + /// Navigates the given link relation and stores the the returned resource(s). + /// + /// The current . + /// The link relation to follow. + /// The updated . + /// + IHalClient Get(IResource resource, string rel); + + /// + /// Navigates the given link relation and stores the the returned resource(s). + /// + /// The current . + /// The link relation to follow. + /// The curie of the link relation. + /// The updated . + /// + IHalClient Get(IResource resource, string rel, string curie); + + /// + /// Navigates the given templated link relation and stores the the returned resource(s). + /// + /// The current . + /// The templated link relation to follow. + /// An anonymous object containing the template parameters to apply. + /// The updated . + /// + /// + IHalClient Get(IResource resource, string rel, object parameters); + + /// + /// Navigates the given templated link relation and stores the the returned resource(s). + /// + /// The current . + /// The templated link relation to follow. + /// An anonymous object containing the template parameters to apply. + /// The curie of the link relation. + /// The updated . + /// + /// + IHalClient Get(IResource resource, string rel, object parameters, string curie); + /// /// Makes a HTTP POST request to the given link relation on the most recently navigated resource. ///