From cf738fda1d7c2c4eaf5c82fca625020ddb4e1411 Mon Sep 17 00:00:00 2001 From: jaredcnance Date: Sun, 17 Sep 2017 14:33:53 -0500 Subject: [PATCH 1/2] feat(LinkBuilder): handle relative url config --- src/JsonApiDotNetCore/Builders/LinkBuilder.cs | 14 +++--- .../Configuration/JsonApiOptions.cs | 1 + test/UnitTests/Builders/LinkBuilder_Tests.cs | 48 +++++++++++++++++++ 3 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 test/UnitTests/Builders/LinkBuilder_Tests.cs diff --git a/src/JsonApiDotNetCore/Builders/LinkBuilder.cs b/src/JsonApiDotNetCore/Builders/LinkBuilder.cs index 83b77c169c..dced1225a9 100644 --- a/src/JsonApiDotNetCore/Builders/LinkBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/LinkBuilder.cs @@ -9,28 +9,30 @@ public class LinkBuilder public LinkBuilder(IJsonApiContext context) { - _context = context; + _context = context; } public string GetBasePath(HttpContext context, string entityName) { var r = context.Request; - return $"{r.Scheme}://{r.Host}{GetNamespaceFromPath(r.Path, entityName)}"; + return (_context.Options.RelativeLinks) + ? $"{GetNamespaceFromPath(r.Path, entityName)}" + : $"{r.Scheme}://{r.Host}{GetNamespaceFromPath(r.Path, entityName)}"; } private string GetNamespaceFromPath(string path, string entityName) { var nSpace = string.Empty; var segments = path.Split('/'); - - for(var i = 1; i < segments.Length; i++) + + for (var i = 1; i < segments.Length; i++) { - if(segments[i].ToLower() == entityName) + if (segments[i].ToLower() == entityName) break; nSpace += $"/{segments[i]}"; } - + return nSpace; } diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs index d651845773..2e24d52d53 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs @@ -14,6 +14,7 @@ public class JsonApiOptions public bool IncludeTotalRecordCount { get; set; } public bool AllowClientGeneratedIds { get; set; } public IContextGraph ContextGraph { get; set; } + public bool RelativeLinks { get; set; } public IContractResolver JsonContractResolver { get; set; } = new DasherizedResolver(); internal IContextGraphBuilder ContextGraphBuilder { get; } = new ContextGraphBuilder(); diff --git a/test/UnitTests/Builders/LinkBuilder_Tests.cs b/test/UnitTests/Builders/LinkBuilder_Tests.cs new file mode 100644 index 0000000000..69b135de03 --- /dev/null +++ b/test/UnitTests/Builders/LinkBuilder_Tests.cs @@ -0,0 +1,48 @@ +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Services; +using Microsoft.AspNetCore.Http; +using Moq; +using Xunit; + +namespace UnitTests +{ + public class LinkBuilder_Tests + { + [Theory] + [InlineData("http", "localhost", "/api/v1/articles", false, "http://localhost/api/v1")] + [InlineData("https", "localhost", "/api/v1/articles", false, "https://localhost/api/v1")] + [InlineData("http", "example.com", "/api/v1/articles", false, "http://example.com/api/v1")] + [InlineData("https", "example.com", "/api/v1/articles", false, "https://example.com/api/v1")] + [InlineData("https", "example.com", "/articles", false, "https://example.com")] + [InlineData("https", "example.com", "/articles", true, "")] + [InlineData("https", "example.com", "/api/v1/articles", true, "/api/v1")] + public void GetBasePath_Returns_Path_Before_Resource(string scheme, + string host, string path, bool isRelative, string expectedPath) + { + // arrange + const string resource = "articles"; + var jsonApiContextMock = new Mock(); + jsonApiContextMock.Setup(m => m.Options).Returns(new JsonApiOptions + { + RelativeLinks = isRelative + }); + + var requestMock = new Mock(); + requestMock.Setup(m => m.Scheme).Returns(scheme); + requestMock.Setup(m => m.Host).Returns(new HostString(host)); + requestMock.Setup(m => m.Path).Returns(new PathString(path)); + + var contextMock = new Mock(); + contextMock.Setup(m => m.Request).Returns(requestMock.Object); + + var linkBuilder = new LinkBuilder(jsonApiContextMock.Object); + + // act + var basePath = linkBuilder.GetBasePath(contextMock.Object, resource); + + // assert + Assert.Equal(expectedPath, basePath); + } + } +} From e54d01b63ff0dcb00b48cdb0f693aedae197f681 Mon Sep 17 00:00:00 2001 From: jaredcnance Date: Sun, 17 Sep 2017 14:42:11 -0500 Subject: [PATCH 2/2] chore(*): document and bump version --- docs/Options.md | 36 +++++++++++++++++++ .../JsonApiDotNetCore.csproj | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/Options.md b/docs/Options.md index ffba0bd685..681c502557 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -45,3 +45,39 @@ public IServiceProvider ConfigureServices(IServiceCollection services) { // ... } ``` + +## Relative Links + +All links are absolute by default. However, you can configure relative links: + +```csharp +public IServiceProvider ConfigureServices(IServiceCollection services) { + services.AddJsonApi( + opt => opt.RelativeLinks = true); + // ... +} +``` + + +```http +GET /api/v1/articles/4309 HTTP/1.1 +Accept: application/vnd.api+json +``` + +```json +{ + "type": "articles", + "id": "4309", + "attributes": { + "name": "Voluptas iure est molestias." + }, + "relationships": { + "author": { + "links": { + "self": "/api/v1/articles/4309/relationships/author", + "related": "/api/v1/articles/4309/author" + } + } + } +} +``` \ No newline at end of file diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj index 80f2a0cd77..30ddeca2ae 100755 --- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj +++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj @@ -1,6 +1,6 @@  - 2.1.3 + 2.1.4 netstandard1.6 JsonApiDotNetCore JsonApiDotNetCore