Skip to content

Commit

Permalink
Merge pull request #159 from JakeGinnivan/kuehlmeyer-master
Browse files Browse the repository at this point in the history
Kuehlmeyer master
  • Loading branch information
panmanphil committed Oct 23, 2020
2 parents a0ce8fc + 373759f commit ddd9b98
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 8 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Expand Up @@ -115,3 +115,7 @@ WebApi.Hal*.nupkg
.vscode/
.DS_Store
*~
*.bak

beer.db

Expand Up @@ -3,9 +3,11 @@
"self": {
"href": "/api/organisations"
},
"organisation": {
"href": "/api/organisations/1"
}
"organisation": [
{
"href": "/api/organisations/1"
}
]
},
"_embedded": {
"organisation": [
Expand Down
154 changes: 153 additions & 1 deletion WebApi.Hal.Tests/HalResourceTest.cs
@@ -1,8 +1,14 @@
using System.Buffers;
using System;
using System.Buffers;
using System.IO;
using System.Linq;
using Assent;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WebApi.Hal.Tests.Representations;
using Xunit;

Expand Down Expand Up @@ -113,5 +119,151 @@ public void organisation_get_xml_test()
this.Assent(serialisedResult);
}
}

[Fact]
public void organisation_get_json_with_multi_link_linkrel()
{
// arrange
var mediaFormatter = new JsonHalMediaTypeOutputFormatter(
new JsonSerializerSettings { Formatting = Formatting.Indented }, ArrayPool<char>.Shared,
new MvcOptions());
var resourceWithAppPath = new OrganisationWithLinkTitleRepresentation(1, "Org Name");
resourceWithAppPath.Links.Add(new Link("multi-rel-with-single-link", "~/api/organisations/test")
{
IsMultiLink = true
});
resourceWithAppPath.Links.Add(new Link("multi-rel-with-multiple-links", "~/api/organisations/test1")
{
IsMultiLink = true
});
resourceWithAppPath.Links.Add(new Link("multi-rel-with-multiple-links", "~/api/organisations/test2")
{
IsMultiLink = true
});
resourceWithAppPath.Links.Add(new Link("multi-rel-with-multiple-links-with-is-multilink-false", "~/api/organisations/test-f1"));
resourceWithAppPath.Links.Add(new Link("multi-rel-with-multiple-links-with-is-multilink-false", "~/api/organisations/test-f2"));

// act
using (var stream = new StringWriter())
{
mediaFormatter.WriteObject(stream, resourceWithAppPath);

string serialisedResult = stream.ToString();

// assert
this.Assent(serialisedResult);
}
}

public class OrganisationRepresentationWithEmbeddedResources : OrganisationWithAppPathRepresentation
{
public OrganisationRepresentationWithEmbeddedResources(int id, string name) : base(id, name)
{
}

public TestRepresentation[] EmbeddedMultiResource { get; set; }

public TestRepresentation EmbeddedSingleResource { get; set; }
}

public class TestRepresentation : Representation
{
}

[Fact]
public void organisation_get_json_with_multi_link_embedded()
{
// arrange
var mediaFormatter = new JsonHalMediaTypeOutputFormatter(
new JsonSerializerSettings { Formatting = Formatting.Indented }, ArrayPool<char>.Shared,
new MvcOptions());
var org = new OrganisationRepresentationWithEmbeddedResources(1, "Org Name");

org.EmbeddedSingleResource = new TestRepresentation
{
Rel = "single-resource",
Href = "~/single"
};
org.EmbeddedMultiResource = new[] { new TestRepresentation
{
Rel = "multi-resource",
Href = "~/multi"
} };

// act
using (var stream = new StringWriter())
{
mediaFormatter.WriteObject(stream, org);

string serialisedResult = stream.ToString();

// assert
this.Assent(serialisedResult);
}
}

private class JsonHalMediaTypeInputFormatterWithCreateSerializer : JsonHalMediaTypeInputFormatter
{
public JsonHalMediaTypeInputFormatterWithCreateSerializer(ILogger logger, JsonSerializerSettings serializerSettings, ArrayPool<char> charPool, ObjectPoolProvider objectPoolProvider, MvcOptions mvcOptions, MvcNewtonsoftJsonOptions mvcJsonOptions)
: base(logger, serializerSettings, charPool, objectPoolProvider, mvcOptions, mvcJsonOptions)
{
}

public new JsonSerializer CreateJsonSerializer()
{
return base.CreateJsonSerializer();
}
}

[Fact]
public void organisation_parse_json_with_multi_link_linkrel()
{
// arrange
var mediaFormatter = new JsonHalMediaTypeOutputFormatter(
new JsonSerializerSettings { Formatting = Formatting.Indented }, ArrayPool<char>.Shared,
new MvcOptions());
var resourceWithAppPath = new OrganisationWithLinkTitleRepresentation(1, "Org Name");
resourceWithAppPath.Links.Add(new Link("multi-rel-with-single-link", "~/api/organisations/test")
{
IsMultiLink = true
});
resourceWithAppPath.Links.Add(new Link("multi-rel-with-multiple-links", "~/api/organisations/test1")
{
IsMultiLink = true
});
resourceWithAppPath.Links.Add(new Link("multi-rel-with-multiple-links", "~/api/organisations/test2")
{
IsMultiLink = true
});
resourceWithAppPath.Links.Add(new Link("multi-rel-with-multiple-links-with-is-multilink-false", "~/api/organisations/test1"));
resourceWithAppPath.Links.Add(new Link("multi-rel-with-multiple-links-with-is-multilink-false", "~/api/organisations/test2"));

string serialisedResource;
// serialize
using (var stream = new StringWriter())
{
mediaFormatter.WriteObject(stream, resourceWithAppPath);

serialisedResource = stream.ToString();
}

// parse again
var inputFormatter = new JsonHalMediaTypeInputFormatterWithCreateSerializer(
NullLogger.Instance,
new JsonSerializerSettings { Formatting = Formatting.Indented }, ArrayPool<char>.Shared,
new DefaultObjectPoolProvider(), new MvcOptions(), new MvcNewtonsoftJsonOptions());
var inputSerializer = inputFormatter.CreateJsonSerializer();
using (var stream = new StringReader(serialisedResource))
{
using (var jsonReader = new JsonTextReader(stream))
{
var parsedResource = inputSerializer.Deserialize<OrganisationWithLinkTitleRepresentation>(jsonReader);
Assert.True(parsedResource.Links.Where(l => l.Rel == "multi-rel-with-single-link").All(l => l.IsMultiLink));
Assert.True(parsedResource.Links.Where(l => l.Rel == "multi-rel-with-multiple-links").All(l => l.IsMultiLink));
Assert.True(parsedResource.Links.Where(l => l.Rel == "multi-rel-with-multiple-links-with-is-multilink-false").All(l => l.IsMultiLink));
Assert.True(parsedResource.Links.Where(l => l.Rel == "someRel").All(l => !l.IsMultiLink));
}
}
}
}
}
@@ -0,0 +1,35 @@
{
"Id": 1,
"Name": "Org Name",
"_links": {
"self": {
"href": "/api/organisations/1"
},
"multi-resource": [
{
"href": "/multi"
}
],
"single-resource": {
"href": "/single"
}
},
"_embedded": {
"multi-resource": [
{
"_links": {
"self": {
"href": "/multi"
}
}
}
],
"single-resource": {
"_links": {
"self": {
"href": "/single"
}
}
}
}
}
@@ -0,0 +1,31 @@
{
"Id": 1,
"Name": "Org Name",
"_links": {
"multi-rel-with-single-link": [
{
"href": "/api/organisations/test"
}
],
"multi-rel-with-multiple-links": [
{
"href": "/api/organisations/test1"
},
{
"href": "/api/organisations/test2"
}
],
"multi-rel-with-multiple-links-with-is-multilink-false": [
{
"href": "/api/organisations/test-f1"
},
{
"href": "/api/organisations/test-f2"
}
],
"somerel": {
"href": "someHref",
"title": "someTitle"
}
}
}
1 change: 1 addition & 0 deletions WebApi.Hal.Tests/WebApi.Hal.Tests.csproj
Expand Up @@ -12,6 +12,7 @@

<ItemGroup>
<PackageReference Include="Assent" Version="1.6.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.9" />
<PackageReference Include="xunit" Version="2.4.1" />
Expand Down
1 change: 1 addition & 0 deletions WebApi.Hal.Web/WebApi.Hal.Web.csproj
Expand Up @@ -12,6 +12,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.9" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
Expand Down
6 changes: 4 additions & 2 deletions WebApi.Hal/JsonConverters/LinksConverter.cs
Expand Up @@ -24,13 +24,15 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s

writer.WritePropertyName(rel.Key);

if ((count > 1) || (rel.Key == Link.RelForCuries))
bool serializeAsArray = (count > 1) || (rel.Any(l => l.IsMultiLink)) || (rel.Key == Link.RelForCuries);

if (serializeAsArray)
writer.WriteStartArray();

foreach (var link in rel)
WriteLink(writer, link);

if ((count > 1) || (rel.Key == Link.RelForCuries))
if ((count > 1) || (rel.Any(l => l.IsMultiLink)) || (rel.Key == Link.RelForCuries))
writer.WriteEndArray();
}

Expand Down
1 change: 1 addition & 0 deletions WebApi.Hal/JsonConverters/ResourceConverter.cs
Expand Up @@ -161,6 +161,7 @@ static void CreateLinks(JProperty rel, IResource resource)
foreach (var link in arr.Select(item => item.ToObject<Link>()))
{
link.Rel = rel.Name;
link.IsMultiLink = true;
resource.Links.Add(link);
}
}
Expand Down
1 change: 1 addition & 0 deletions WebApi.Hal/Link.cs
Expand Up @@ -76,6 +76,7 @@ public string Rel
public string Name { get; set; }
public string Profile { get; set; }
public string HrefLang { get; set; }
public bool IsMultiLink { get; set; }

public bool IsTemplated => !string.IsNullOrEmpty(Href) && isTemplatedRegex.IsMatch(Href);

Expand Down
3 changes: 3 additions & 0 deletions WebApi.Hal/Representation.cs
Expand Up @@ -146,7 +146,10 @@ private void ProcessPropertyValue(IHypermediaResolver resolver, List<CuriesLink>
var link = representation.ToLink(resolver);

if (link != null)
{
link.IsMultiLink = true;
Links.Add(link); // add a link to embedded to the container ...
}
}

Embedded.Add(embeddedResource);
Expand Down
5 changes: 3 additions & 2 deletions WebApi.Hal/WebApi.Hal.csproj
Expand Up @@ -4,13 +4,14 @@

<PropertyGroup>
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>
<Version>4.0.0</Version>
<PackageVersion>4.0.0</PackageVersion>
<Version>4.0.1</Version>
<PackageVersion>4.0.1</PackageVersion>
<Copyright>Copyright © Jake Ginnivan 2020</Copyright>
<Description>Adds support for the Hal Media Type (and Hypermedia) to Asp.net</Description>
<ProjectUrl>https://github.com/JakeGinnivan/WebApi.Hal</ProjectUrl>
<IsPackable>true</IsPackable>
<PackageReleaseNotes>
4.0.1 ability to mark a link-rel as multi-link to ensure that it always serialzies to an array, even if there's only one link at runtime
4.0.0 update to support .net core 3.1
3.10 updates to support multithreaded usage
3.0.0 first .net standard release
Expand Down

0 comments on commit ddd9b98

Please sign in to comment.