diff --git a/src/Examples/JsonApiDotNetCoreExample/Definitions/PassportHooksDefinition.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/PassportHooksDefinition.cs
index ec5cd3ed8f..5ccfcf2447 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Definitions/PassportHooksDefinition.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/PassportHooksDefinition.cs
@@ -4,7 +4,6 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Errors;
using JsonApiDotNetCore.Hooks.Internal.Execution;
-using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Serialization.Objects;
using JsonApiDotNetCoreExample.Models;
diff --git a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs
index 96717fe796..300c4620b4 100644
--- a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs
+++ b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs
@@ -56,41 +56,28 @@ public interface IJsonApiOptions
bool UseRelativeLinks { get; }
///
- /// Configures globally which links to show in the
- /// object for a requested resource. Setting can be overridden per resource by
- /// adding a to the class definition of that resource.
+ /// Configures which links to show in the
+ /// object. Defaults to .
+ /// This setting can be overruled per resource type by
+ /// adding on the class definition of a resource.
///
LinkTypes TopLevelLinks { get; }
///
- /// Configures globally which links to show in the
- /// object for a requested resource. Setting can be overridden per resource by
- /// adding a to the class definition of that resource.
+ /// Configures which links to show in the
+ /// object. Defaults to .
+ /// This setting can be overruled per resource type by
+ /// adding on the class definition of a resource.
///
LinkTypes ResourceLinks { get; }
///
- /// Configures globally which links to show in the
- /// object for a requested resource. Setting can be overridden per resource by
- /// adding a to the class definition of that resource.
- /// This option can also be specified per relationship by using the associated links argument
- /// in the constructor of .
+ /// Configures which links to show in the
+ /// object. Defaults to .
+ /// This setting can be overruled for all relationships per resource type by
+ /// adding on the class definition of a resource.
+ /// This can be further overruled per relationship by setting .
///
- ///
- ///
- /// options.RelationshipLinks = LinkTypes.None;
- ///
- ///
- /// {
- /// "type": "articles",
- /// "id": "4309",
- /// "relationships": {
- /// "author": { "data": { "type": "people", "id": "1234" }
- /// }
- /// }
- /// }
- ///
- ///
LinkTypes RelationshipLinks { get; }
///
@@ -154,13 +141,13 @@ public interface IJsonApiOptions
bool EnableLegacyFilterNotation { get; }
///
- /// Determines whether the serialization setting can be overridden by using a query string parameter.
+ /// Determines whether the serialization setting can be controlled using a query string parameter.
/// False by default.
///
bool AllowQueryStringOverrideForSerializerNullValueHandling { get; }
///
- /// Determines whether the serialization setting can be overridden by using a query string parameter.
+ /// Determines whether the serialization setting can be controlled using a query string parameter.
/// False by default.
///
bool AllowQueryStringOverrideForSerializerDefaultValueHandling { get; }
diff --git a/src/JsonApiDotNetCore/Configuration/ResourceContext.cs b/src/JsonApiDotNetCore/Configuration/ResourceContext.cs
index 632a611dd3..edde0bcc41 100644
--- a/src/JsonApiDotNetCore/Configuration/ResourceContext.cs
+++ b/src/JsonApiDotNetCore/Configuration/ResourceContext.cs
@@ -52,27 +52,33 @@ public class ResourceContext
///
/// Configures which links to show in the
- /// object for this resource. If set to ,
- /// the configuration will be read from .
- /// Defaults to .
+ /// object for this resource type.
+ /// Defaults to , which falls back to .
///
+ ///
+ /// In the process of building the resource graph, this value is set based on usage.
+ ///
public LinkTypes TopLevelLinks { get; internal set; } = LinkTypes.NotConfigured;
///
/// Configures which links to show in the
- /// object for this resource. If set to ,
- /// the configuration will be read from .
- /// Defaults to .
+ /// object for this resource type.
+ /// Defaults to , which falls back to .
///
+ ///
+ /// In the process of building the resource graph, this value is set based on usage.
+ ///
public LinkTypes ResourceLinks { get; internal set; } = LinkTypes.NotConfigured;
///
/// Configures which links to show in the
- /// for all relationships of the resource for which this attribute was instantiated.
- /// If set to , the configuration will
- /// be read from or
- /// . Defaults to .
+ /// object for all relationships of this resource type.
+ /// Defaults to , which falls back to .
+ /// This can be overruled per relationship by setting .
///
+ ///
+ /// In the process of building the resource graph, this value is set based on usage.
+ ///
public LinkTypes RelationshipLinks { get; internal set; } = LinkTypes.NotConfigured;
public override string ToString()
diff --git a/src/JsonApiDotNetCore/Resources/Annotations/HasManyAttribute.cs b/src/JsonApiDotNetCore/Resources/Annotations/HasManyAttribute.cs
index c31aa61989..869e322ee8 100644
--- a/src/JsonApiDotNetCore/Resources/Annotations/HasManyAttribute.cs
+++ b/src/JsonApiDotNetCore/Resources/Annotations/HasManyAttribute.cs
@@ -5,24 +5,17 @@ namespace JsonApiDotNetCore.Resources.Annotations
///
/// Used to expose a property on a resource class as a JSON:API to-many relationship (https://jsonapi.org/format/#document-resource-object-relationships).
///
+ ///
+ /// Articles { get; set; }
+ /// }
+ /// ]]>
+ ///
[AttributeUsage(AttributeTargets.Property)]
public class HasManyAttribute : RelationshipAttribute
{
- ///
- /// Creates a HasMany relational link to another resource.
- ///
- ///
- /// Articles { get; set; }
- /// }
- /// ]]>
- ///
- public HasManyAttribute()
- {
- Links = LinkTypes.All;
- }
}
}
diff --git a/src/JsonApiDotNetCore/Resources/Annotations/HasOneAttribute.cs b/src/JsonApiDotNetCore/Resources/Annotations/HasOneAttribute.cs
index d958a6a4ee..020bd2b5b2 100644
--- a/src/JsonApiDotNetCore/Resources/Annotations/HasOneAttribute.cs
+++ b/src/JsonApiDotNetCore/Resources/Annotations/HasOneAttribute.cs
@@ -8,9 +8,5 @@ namespace JsonApiDotNetCore.Resources.Annotations
[AttributeUsage(AttributeTargets.Property)]
public sealed class HasOneAttribute : RelationshipAttribute
{
- public HasOneAttribute()
- {
- Links = LinkTypes.NotConfigured;
- }
}
}
diff --git a/src/JsonApiDotNetCore/Resources/Annotations/RelationshipAttribute.cs b/src/JsonApiDotNetCore/Resources/Annotations/RelationshipAttribute.cs
index 21c71ae68a..4efb72832f 100644
--- a/src/JsonApiDotNetCore/Resources/Annotations/RelationshipAttribute.cs
+++ b/src/JsonApiDotNetCore/Resources/Annotations/RelationshipAttribute.cs
@@ -1,7 +1,6 @@
using System;
using System.Reflection;
using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Errors;
namespace JsonApiDotNetCore.Resources.Annotations
{
@@ -10,8 +9,6 @@ namespace JsonApiDotNetCore.Resources.Annotations
///
public abstract class RelationshipAttribute : ResourceFieldAttribute
{
- private LinkTypes _links;
-
///
/// The property name of the EF Core inverse navigation, which may or may not exist.
/// Even if it exists, it may not be exposed as a JSON:API relationship.
@@ -58,27 +55,12 @@ public abstract class RelationshipAttribute : ResourceFieldAttribute
public Type LeftType { get; internal set; }
///
- /// Configures which links to show in the object for this relationship.
- /// When not explicitly assigned, the default value depends on the relationship type (see remarks).
+ /// Configures which links to show in the
+ /// object for this relationship.
+ /// Defaults to , which falls back to
+ /// and then falls back to .
///
- ///
- /// This defaults to for and relationships.
- /// This defaults to for relationships, which means that
- /// the configuration in or is used.
- ///
- public LinkTypes Links
- {
- get => _links;
- set
- {
- if (value == LinkTypes.Paging)
- {
- throw new InvalidConfigurationException($"{LinkTypes.Paging:g} not allowed for argument {nameof(value)}");
- }
-
- _links = value;
- }
- }
+ public LinkTypes Links { get; set; } = LinkTypes.NotConfigured;
///
/// Whether or not this relationship can be included using the ?include=publicName query string parameter.
diff --git a/src/JsonApiDotNetCore/Resources/Annotations/ResourceLinksAttribute.cs b/src/JsonApiDotNetCore/Resources/Annotations/ResourceLinksAttribute.cs
index 03927c05c9..4a4e73027e 100644
--- a/src/JsonApiDotNetCore/Resources/Annotations/ResourceLinksAttribute.cs
+++ b/src/JsonApiDotNetCore/Resources/Annotations/ResourceLinksAttribute.cs
@@ -1,75 +1,34 @@
using System;
-using JsonApiDotNetCore.Errors;
+using JsonApiDotNetCore.Configuration;
namespace JsonApiDotNetCore.Resources.Annotations
{
- // TODO: There are no tests for this.
-
///
/// When put on a resource class, overrides global configuration for which links to render.
///
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
public sealed class ResourceLinksAttribute : Attribute
{
- private LinkTypes _topLevelLinks = LinkTypes.NotConfigured;
- private LinkTypes _resourceLinks = LinkTypes.NotConfigured;
- private LinkTypes _relationshipLinks = LinkTypes.NotConfigured;
-
///
/// Configures which links to show in the
- /// section for this resource.
- /// Defaults to .
+ /// object for this resource type.
+ /// Defaults to , which falls back to .
///
- public LinkTypes TopLevelLinks
- {
- get => _topLevelLinks;
- set
- {
- if (value == LinkTypes.Related)
- {
- throw new InvalidConfigurationException($"{LinkTypes.Related:g} not allowed for argument {nameof(value)}");
- }
-
- _topLevelLinks = value;
- }
- }
+ public LinkTypes TopLevelLinks { get; set; } = LinkTypes.NotConfigured;
///
/// Configures which links to show in the
- /// section for this resource.
- /// Defaults to .
+ /// object for this resource type.
+ /// Defaults to , which falls back to .
///
- public LinkTypes ResourceLinks
- {
- get => _resourceLinks;
- set
- {
- if (value == LinkTypes.Paging)
- {
- throw new InvalidConfigurationException($"{LinkTypes.Paging:g} not allowed for argument {nameof(value)}");
- }
-
- _resourceLinks = value;
- }
- }
-
+ public LinkTypes ResourceLinks { get; set; } = LinkTypes.NotConfigured;
+
///
/// Configures which links to show in the
- /// for all relationships of the resource type on which this attribute was used.
- /// Defaults to .
+ /// object for all relationships of this resource type.
+ /// Defaults to , which falls back to .
+ /// This can be overruled per relationship by setting .
///
- public LinkTypes RelationshipLinks
- {
- get => _relationshipLinks;
- set
- {
- if (value == LinkTypes.Paging)
- {
- throw new InvalidConfigurationException($"{LinkTypes.Paging:g} not allowed for argument {nameof(value)}");
- }
-
- _relationshipLinks = value;
- }
- }
+ public LinkTypes RelationshipLinks { get; set; } = LinkTypes.NotConfigured;
}
}
diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinkInclusionTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinkInclusionTests.cs
new file mode 100644
index 0000000000..760a1ebb8c
--- /dev/null
+++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinkInclusionTests.cs
@@ -0,0 +1,63 @@
+using System.Net;
+using System.Threading.Tasks;
+using FluentAssertions;
+using JsonApiDotNetCore.Serialization.Objects;
+using JsonApiDotNetCoreExampleTests.Startups;
+using TestBuildingBlocks;
+using Xunit;
+
+namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links
+{
+ public sealed class LinkInclusionTests
+ : IClassFixture, LinksDbContext>>
+ {
+ private readonly ExampleIntegrationTestContext, LinksDbContext> _testContext;
+ private readonly LinksFakers _fakers = new LinksFakers();
+
+ public LinkInclusionTests(ExampleIntegrationTestContext, LinksDbContext> testContext)
+ {
+ _testContext = testContext;
+ }
+
+ [Fact]
+ public async Task Get_primary_resource_with_include_applies_links_visibility_from_ResourceLinksAttribute()
+ {
+ // Arrange
+ var location = _fakers.PhotoLocation.Generate();
+ location.Photo = _fakers.Photo.Generate();
+ location.Album = _fakers.PhotoAlbum.Generate();
+
+ await _testContext.RunOnDatabaseAsync(async dbContext =>
+ {
+ dbContext.PhotoLocations.Add(location);
+ await dbContext.SaveChangesAsync();
+ });
+
+ var route = $"/photoLocations/{location.StringId}?include=photo,album";
+
+ // Act
+ var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync(route);
+
+ // Assert
+ httpResponse.Should().HaveStatusCode(HttpStatusCode.OK);
+
+ responseDocument.Links.Should().BeNull();
+
+ responseDocument.SingleData.Should().NotBeNull();
+ responseDocument.SingleData.Links.Should().BeNull();
+ responseDocument.SingleData.Relationships["photo"].Links.Self.Should().BeNull();
+ responseDocument.SingleData.Relationships["photo"].Links.Related.Should().NotBeNull();
+ responseDocument.SingleData.Relationships["album"].Links.Should().BeNull();
+
+ responseDocument.Included.Should().HaveCount(2);
+
+ responseDocument.Included[0].Links.Self.Should().NotBeNull();
+ responseDocument.Included[0].Relationships["location"].Links.Self.Should().NotBeNull();
+ responseDocument.Included[0].Relationships["location"].Links.Related.Should().NotBeNull();
+
+ responseDocument.Included[1].Links.Self.Should().NotBeNull();
+ responseDocument.Included[1].Relationships["photos"].Links.Self.Should().NotBeNull();
+ responseDocument.Included[1].Relationships["photos"].Links.Related.Should().NotBeNull();
+ }
+ }
+}
diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksDbContext.cs
index bb34911642..8f1e638891 100644
--- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksDbContext.cs
+++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksDbContext.cs
@@ -6,10 +6,19 @@ public sealed class LinksDbContext : DbContext
{
public DbSet PhotoAlbums { get; set; }
public DbSet Photos { get; set; }
+ public DbSet PhotoLocations { get; set; }
public LinksDbContext(DbContextOptions options)
: base(options)
{
}
+
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ builder.Entity()
+ .HasOne(photo => photo.Location)
+ .WithOne(location => location.Photo)
+ .HasForeignKey("PhotoLocationKey");
+ }
}
}
diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs
index 6e751dd00b..2bd9552fa4 100644
--- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs
+++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/LinksFakers.cs
@@ -16,7 +16,15 @@ internal sealed class LinksFakers : FakerContainer
.UseSeed(GetFakerSeed())
.RuleFor(photo => photo.Url, f => f.Image.PlaceImgUrl()));
+ private readonly Lazy> _lazyPhotoLocationFaker = new Lazy>(() =>
+ new Faker()
+ .UseSeed(GetFakerSeed())
+ .RuleFor(photoLocation => photoLocation.PlaceName, f => f.Address.FullAddress())
+ .RuleFor(photoLocation => photoLocation.Latitude, f => f.Address.Latitude())
+ .RuleFor(photoLocation => photoLocation.Longitude, f => f.Address.Longitude()));
+
public Faker PhotoAlbum => _lazyPhotoAlbumFaker.Value;
public Faker Photo => _lazyPhotoFaker.Value;
+ public Faker PhotoLocation => _lazyPhotoLocationFaker.Value;
}
}
diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs
index 8af6915e1d..592d5ccb12 100644
--- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs
+++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/Photo.cs
@@ -9,6 +9,9 @@ public sealed class Photo : Identifiable
[Attr]
public string Url { get; set; }
+ [HasOne]
+ public PhotoLocation Location { get; set; }
+
[HasOne]
public PhotoAlbum Album { get; set; }
}
diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoLocation.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoLocation.cs
new file mode 100644
index 0000000000..362c6edb36
--- /dev/null
+++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoLocation.cs
@@ -0,0 +1,24 @@
+using JsonApiDotNetCore.Resources;
+using JsonApiDotNetCore.Resources.Annotations;
+
+namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links
+{
+ [ResourceLinks(TopLevelLinks = LinkTypes.None, ResourceLinks = LinkTypes.None, RelationshipLinks = LinkTypes.Related)]
+ public sealed class PhotoLocation : Identifiable
+ {
+ [Attr]
+ public string PlaceName { get; set; }
+
+ [Attr]
+ public double Latitude { get; set; }
+
+ [Attr]
+ public double Longitude { get; set; }
+
+ [HasOne]
+ public Photo Photo { get; set; }
+
+ [HasOne(Links = LinkTypes.None)]
+ public PhotoAlbum Album { get; set; }
+ }
+}
diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoLocationsController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoLocationsController.cs
new file mode 100644
index 0000000000..1398ee84b1
--- /dev/null
+++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/Links/PhotoLocationsController.cs
@@ -0,0 +1,16 @@
+using JsonApiDotNetCore.Configuration;
+using JsonApiDotNetCore.Controllers;
+using JsonApiDotNetCore.Services;
+using Microsoft.Extensions.Logging;
+
+namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Links
+{
+ public sealed class PhotoLocationsController : JsonApiController
+ {
+ public PhotoLocationsController(IJsonApiOptions options, ILoggerFactory loggerFactory,
+ IResourceService resourceService)
+ : base(options, loggerFactory, resourceService)
+ {
+ }
+ }
+}
diff --git a/test/JsonApiDotNetCoreExampleTests/UnitTests/Links/LinkInclusionTests.cs b/test/JsonApiDotNetCoreExampleTests/UnitTests/Links/LinkInclusionTests.cs
new file mode 100644
index 0000000000..1134e7782b
--- /dev/null
+++ b/test/JsonApiDotNetCoreExampleTests/UnitTests/Links/LinkInclusionTests.cs
@@ -0,0 +1,395 @@
+using FluentAssertions;
+using JsonApiDotNetCore.Configuration;
+using JsonApiDotNetCore.Middleware;
+using JsonApiDotNetCore.Queries;
+using JsonApiDotNetCore.QueryStrings;
+using JsonApiDotNetCore.Resources;
+using JsonApiDotNetCore.Resources.Annotations;
+using JsonApiDotNetCore.Serialization.Building;
+using Microsoft.AspNetCore.Http;
+using Xunit;
+
+namespace JsonApiDotNetCoreExampleTests.UnitTests.Links
+{
+ public sealed class LinkInclusionTests
+ {
+ [Theory]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.None)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.None)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.Related)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Paging, LinkTypes.Paging)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.All)]
+ [InlineData(LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.None, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Self, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Related, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Paging, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.All, LinkTypes.None)]
+ [InlineData(LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.None, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Related, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Paging, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.All, LinkTypes.Self)]
+ [InlineData(LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.None, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Self, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Related, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Paging, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.All, LinkTypes.Related)]
+ [InlineData(LinkTypes.Paging, LinkTypes.NotConfigured, LinkTypes.Paging)]
+ [InlineData(LinkTypes.Paging, LinkTypes.None, LinkTypes.Paging)]
+ [InlineData(LinkTypes.Paging, LinkTypes.Self, LinkTypes.Paging)]
+ [InlineData(LinkTypes.Paging, LinkTypes.Related, LinkTypes.Paging)]
+ [InlineData(LinkTypes.Paging, LinkTypes.Paging, LinkTypes.Paging)]
+ [InlineData(LinkTypes.Paging, LinkTypes.All, LinkTypes.Paging)]
+ [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Paging, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.All)]
+ public void Applies_cascading_settings_for_top_level_links(LinkTypes linksInResourceContext, LinkTypes linksInOptions, LinkTypes expected)
+ {
+ // Arrange
+ var exampleResourceContext = new ResourceContext
+ {
+ PublicName = nameof(ExampleResource),
+ ResourceType = typeof(ExampleResource),
+ TopLevelLinks = linksInResourceContext
+ };
+
+ var resourceGraph = new ResourceGraph(new[]
+ {
+ exampleResourceContext
+ });
+
+ var request = new JsonApiRequest
+ {
+ PrimaryResource = exampleResourceContext,
+ PrimaryId = "1",
+ IsCollection = true,
+ Kind = EndpointKind.Relationship,
+ Relationship = new HasOneAttribute()
+ };
+
+ var paginationContext = new PaginationContext
+ {
+ PageSize = new PageSize(1),
+ PageNumber = new PageNumber(2),
+ TotalResourceCount = 10
+ };
+
+ var queryStringAccessor = new EmptyRequestQueryStringAccessor();
+
+ var options = new JsonApiOptions
+ {
+ TopLevelLinks = linksInOptions
+ };
+
+ var linkBuilder = new LinkBuilder(options, request, paginationContext, resourceGraph, queryStringAccessor);
+
+ // Act
+ var topLevelLinks = linkBuilder.GetTopLevelLinks();
+
+ // Assert
+ if (expected == LinkTypes.None)
+ {
+ topLevelLinks.Should().BeNull();
+ }
+ else
+ {
+ if (expected.HasFlag(LinkTypes.Self))
+ {
+ topLevelLinks.Self.Should().NotBeNull();
+ }
+ else
+ {
+ topLevelLinks.Self.Should().BeNull();
+ }
+
+ if (expected.HasFlag(LinkTypes.Related))
+ {
+ topLevelLinks.Related.Should().NotBeNull();
+ }
+ else
+ {
+ topLevelLinks.Related.Should().BeNull();
+ }
+
+ if (expected.HasFlag(LinkTypes.Paging))
+ {
+ topLevelLinks.First.Should().NotBeNull();
+ topLevelLinks.Last.Should().NotBeNull();
+ topLevelLinks.Prev.Should().NotBeNull();
+ topLevelLinks.Next.Should().NotBeNull();
+ }
+ else
+ {
+ topLevelLinks.First.Should().BeNull();
+ topLevelLinks.Last.Should().BeNull();
+ topLevelLinks.Prev.Should().BeNull();
+ topLevelLinks.Next.Should().BeNull();
+ }
+ }
+ }
+
+ [Theory]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.None)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.None)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.Self)]
+ [InlineData(LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.None, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Self, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.All, LinkTypes.None)]
+ [InlineData(LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.None, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.All, LinkTypes.Self)]
+ [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.Self)]
+ [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.Self)]
+ [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.Self)]
+ public void Applies_cascading_settings_for_resource_links(LinkTypes linksInResourceContext, LinkTypes linksInOptions, LinkTypes expected)
+ {
+ // Arrange
+ var exampleResourceContext = new ResourceContext
+ {
+ PublicName = nameof(ExampleResource),
+ ResourceType = typeof(ExampleResource),
+ ResourceLinks = linksInResourceContext
+ };
+
+ var resourceGraph = new ResourceGraph(new[]
+ {
+ exampleResourceContext
+ });
+
+ var request = new JsonApiRequest();
+
+ var paginationContext = new PaginationContext();
+
+ var queryStringAccessor = new EmptyRequestQueryStringAccessor();
+
+ var options = new JsonApiOptions
+ {
+ ResourceLinks = linksInOptions
+ };
+
+ var linkBuilder = new LinkBuilder(options, request, paginationContext, resourceGraph, queryStringAccessor);
+
+ // Act
+ var resourceLinks = linkBuilder.GetResourceLinks(nameof(ExampleResource), "id");
+
+ // Assert
+ if (expected == LinkTypes.Self)
+ {
+ resourceLinks.Self.Should().NotBeNull();
+ }
+ else
+ {
+ resourceLinks.Should().BeNull();
+ }
+ }
+
+ [Theory]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.None)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.None)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.Related)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.All)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.None)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.None, LinkTypes.None)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.Self, LinkTypes.None)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.Related, LinkTypes.None)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.All, LinkTypes.None)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.Self)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.None, LinkTypes.Self)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.Related, LinkTypes.Self)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.All, LinkTypes.Self)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.Related)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.None, LinkTypes.Related)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.Self, LinkTypes.Related)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.Related, LinkTypes.Related)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.All, LinkTypes.Related)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.All)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.None, LinkTypes.All)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.Self, LinkTypes.All)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.Related, LinkTypes.All)]
+ [InlineData(LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.All, LinkTypes.All)]
+ [InlineData(LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.None, LinkTypes.None, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.None, LinkTypes.Self, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.None, LinkTypes.Related, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.None, LinkTypes.All, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Self, LinkTypes.None, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Self, LinkTypes.Self, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Self, LinkTypes.Related, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Self, LinkTypes.All, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Related, LinkTypes.None, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Related, LinkTypes.Self, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Related, LinkTypes.Related, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.Related, LinkTypes.All, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.All, LinkTypes.None, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.All, LinkTypes.Self, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.All, LinkTypes.Related, LinkTypes.None)]
+ [InlineData(LinkTypes.None, LinkTypes.All, LinkTypes.All, LinkTypes.None)]
+ [InlineData(LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.None, LinkTypes.None, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.None, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.None, LinkTypes.Related, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.None, LinkTypes.All, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Self, LinkTypes.None, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Self, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Self, LinkTypes.Related, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Self, LinkTypes.All, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Related, LinkTypes.None, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Related, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Related, LinkTypes.Related, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.Related, LinkTypes.All, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.All, LinkTypes.None, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.All, LinkTypes.Self, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.All, LinkTypes.Related, LinkTypes.Self)]
+ [InlineData(LinkTypes.Self, LinkTypes.All, LinkTypes.All, LinkTypes.Self)]
+ [InlineData(LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.None, LinkTypes.None, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.None, LinkTypes.Self, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.None, LinkTypes.Related, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.None, LinkTypes.All, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Self, LinkTypes.None, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Self, LinkTypes.Self, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Self, LinkTypes.Related, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Self, LinkTypes.All, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Related, LinkTypes.None, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Related, LinkTypes.Self, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Related, LinkTypes.Related, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.Related, LinkTypes.All, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.All, LinkTypes.None, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.All, LinkTypes.Self, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.All, LinkTypes.Related, LinkTypes.Related)]
+ [InlineData(LinkTypes.Related, LinkTypes.All, LinkTypes.All, LinkTypes.Related)]
+ [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.NotConfigured, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.None, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.Self, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.Related, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.All, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.None, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.Self, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.Related, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.All, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.None, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.Self, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.Related, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.All, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.None, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.Self, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.Related, LinkTypes.All)]
+ [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.All, LinkTypes.All)]
+ public void Applies_cascading_settings_for_relationship_links(LinkTypes linksInRelationshipAttribute, LinkTypes linksInResourceContext, LinkTypes linksInOptions, LinkTypes expected)
+ {
+ // Arrange
+ var exampleResourceContext = new ResourceContext
+ {
+ PublicName = nameof(ExampleResource),
+ ResourceType = typeof(ExampleResource),
+ RelationshipLinks = linksInResourceContext
+ };
+
+ var resourceGraph = new ResourceGraph(new[]
+ {
+ exampleResourceContext
+ });
+
+ var request = new JsonApiRequest();
+
+ var paginationContext = new PaginationContext();
+
+ var queryStringAccessor = new EmptyRequestQueryStringAccessor();
+
+ var options = new JsonApiOptions
+ {
+ RelationshipLinks = linksInOptions
+ };
+
+ var linkBuilder = new LinkBuilder(options, request, paginationContext, resourceGraph, queryStringAccessor);
+
+ var relationship = new HasOneAttribute
+ {
+ Links = linksInRelationshipAttribute
+ };
+
+ // Act
+ var relationshipLinks = linkBuilder.GetRelationshipLinks(relationship, new ExampleResource());
+
+ // Assert
+ if (expected == LinkTypes.None)
+ {
+ relationshipLinks.Should().BeNull();
+ }
+ else
+ {
+ if (expected.HasFlag(LinkTypes.Self))
+ {
+ relationshipLinks.Self.Should().NotBeNull();
+ }
+ else
+ {
+ relationshipLinks.Self.Should().BeNull();
+ }
+
+ if (expected.HasFlag(LinkTypes.Related))
+ {
+ relationshipLinks.Related.Should().NotBeNull();
+ }
+ else
+ {
+ relationshipLinks.Related.Should().BeNull();
+ }
+ }
+ }
+
+ private sealed class EmptyRequestQueryStringAccessor : IRequestQueryStringAccessor
+ {
+ public IQueryCollection Query { get; } = new QueryCollection();
+ }
+
+ private sealed class ExampleResource : Identifiable
+ {
+ }
+ }
+}
diff --git a/test/UnitTests/Builders/LinkBuilderTests.cs b/test/UnitTests/Builders/LinkBuilderTests.cs
deleted file mode 100644
index 3799a6d3bf..0000000000
--- a/test/UnitTests/Builders/LinkBuilderTests.cs
+++ /dev/null
@@ -1,235 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Middleware;
-using JsonApiDotNetCore.Queries;
-using JsonApiDotNetCore.QueryStrings;
-using JsonApiDotNetCore.Resources.Annotations;
-using JsonApiDotNetCore.Serialization.Building;
-using JsonApiDotNetCoreExample.Models;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.WebUtilities;
-using Moq;
-using Xunit;
-
-namespace UnitTests
-{
- public sealed class LinkBuilderTests
- {
- private readonly IPaginationContext _paginationContext = GetPaginationContext();
- private readonly Mock _provider = new Mock();
- private readonly IRequestQueryStringAccessor _queryStringAccessor = new FakeRequestQueryStringAccessor("?foo=bar&page[size]=10&page[number]=2");
- private const string _host = "http://www.example.com";
- private const int _primaryId = 123;
- private const string _relationshipName = "author";
- private const string _topSelf = "http://www.example.com/articles?foo=bar&page[size]=10&page[number]=2";
- private const string _topResourceSelf = "http://www.example.com/articles/123?foo=bar&page[size]=10&page[number]=2";
- private const string _topRelatedSelf = "http://www.example.com/articles/123/author?foo=bar&page[size]=10&page[number]=2";
- private const string _resourceSelf = "http://www.example.com/articles/123";
- private const string _relSelf = "http://www.example.com/articles/123/relationships/author";
- private const string _relRelated = "http://www.example.com/articles/123/author";
-
- [Theory]
- [InlineData(LinkTypes.All, LinkTypes.NotConfigured, _resourceSelf)]
- [InlineData(LinkTypes.Self, LinkTypes.NotConfigured, _resourceSelf)]
- [InlineData(LinkTypes.None, LinkTypes.NotConfigured, null)]
- [InlineData(LinkTypes.All, LinkTypes.Self, _resourceSelf)]
- [InlineData(LinkTypes.Self, LinkTypes.Self, _resourceSelf)]
- [InlineData(LinkTypes.None, LinkTypes.Self, _resourceSelf)]
- [InlineData(LinkTypes.All, LinkTypes.None, null)]
- [InlineData(LinkTypes.Self, LinkTypes.None, null)]
- [InlineData(LinkTypes.None, LinkTypes.None, null)]
- public void BuildResourceLinks_GlobalAndResourceConfiguration_ExpectedResult(LinkTypes global, LinkTypes resource, object expectedResult)
- {
- // Arrange
- var config = GetConfiguration(resourceLinks: global);
- var primaryResource = GetArticleResourceContext(resourceLinks: resource);
- _provider.Setup(m => m.GetResourceContext("articles")).Returns(primaryResource);
- var builder = new LinkBuilder(config, GetRequestManager(), new PaginationContext(), _provider.Object, _queryStringAccessor);
-
- // Act
- var links = builder.GetResourceLinks("articles", _primaryId.ToString());
-
- // Assert
- if (expectedResult == null)
- Assert.Null(links);
- else
- Assert.Equal(_resourceSelf, links.Self);
- }
-
- [Theory]
- [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.NotConfigured, _relSelf, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.All, _relSelf, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.Self, _relSelf, null)]
- [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.Related, null, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.None, null, null)]
- [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.NotConfigured, _relSelf, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.All, _relSelf, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.Self, _relSelf, null)]
- [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.Related, null, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.None, null, null)]
- [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.NotConfigured, _relSelf, null)]
- [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.All, _relSelf, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.Self, _relSelf, null)]
- [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.Related, null, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.None, null, null)]
- [InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.NotConfigured, null, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.All, _relSelf, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.Self, _relSelf, null)]
- [InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.Related, null, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.None, null, null)]
- [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.NotConfigured, null, null)]
- [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.All, _relSelf, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.Self, _relSelf, null)]
- [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.Related, null, _relRelated)]
- [InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.None, null, null)]
- public void BuildRelationshipLinks_GlobalResourceAndAttrConfiguration_ExpectedLinks(
- LinkTypes global, LinkTypes resource, LinkTypes relationship, object expectedSelfLink, object expectedRelatedLink)
- {
- // Arrange
- var config = GetConfiguration(relationshipLinks: global);
- var primaryResource = GetArticleResourceContext(relationshipLinks: resource);
- _provider.Setup(m => m.GetResourceContext(typeof(Article))).Returns(primaryResource);
- var builder = new LinkBuilder(config, GetRequestManager(), new PaginationContext(), _provider.Object, _queryStringAccessor);
- var attr = new HasOneAttribute { Links = relationship, RightType = typeof(Author), PublicName = "author" };
-
- // Act
- var links = builder.GetRelationshipLinks(attr, new Article { Id = _primaryId });
-
- // Assert
- if (expectedSelfLink == null && expectedRelatedLink == null)
- {
- Assert.Null(links);
- }
- else
- {
- Assert.Equal(expectedSelfLink, links.Self);
- Assert.Equal(expectedRelatedLink, links.Related);
- }
- }
-
- [Theory]
- [InlineData(LinkTypes.All, LinkTypes.NotConfigured, _topSelf, true)]
- [InlineData(LinkTypes.All, LinkTypes.All, _topSelf, true)]
- [InlineData(LinkTypes.All, LinkTypes.Self, _topSelf, false)]
- [InlineData(LinkTypes.All, LinkTypes.Paging, null, true)]
- [InlineData(LinkTypes.All, LinkTypes.None, null, false)]
- [InlineData(LinkTypes.Self, LinkTypes.NotConfigured, _topSelf, false)]
- [InlineData(LinkTypes.Self, LinkTypes.All, _topSelf, true)]
- [InlineData(LinkTypes.Self, LinkTypes.Self, _topSelf, false)]
- [InlineData(LinkTypes.Self, LinkTypes.Paging, null, true)]
- [InlineData(LinkTypes.Self, LinkTypes.None, null, false)]
- [InlineData(LinkTypes.Paging, LinkTypes.NotConfigured, null, true)]
- [InlineData(LinkTypes.Paging, LinkTypes.All, _topSelf, true)]
- [InlineData(LinkTypes.Paging, LinkTypes.Self, _topSelf, false)]
- [InlineData(LinkTypes.Paging, LinkTypes.Paging, null, true)]
- [InlineData(LinkTypes.Paging, LinkTypes.None, null, false)]
- [InlineData(LinkTypes.None, LinkTypes.NotConfigured, null, false)]
- [InlineData(LinkTypes.None, LinkTypes.All, _topSelf, true)]
- [InlineData(LinkTypes.None, LinkTypes.Self, _topSelf, false)]
- [InlineData(LinkTypes.None, LinkTypes.Paging, null, true)]
- [InlineData(LinkTypes.None, LinkTypes.None, null, false)]
- [InlineData(LinkTypes.All, LinkTypes.Self, _topResourceSelf, false)]
- [InlineData(LinkTypes.Self, LinkTypes.Self, _topResourceSelf, false)]
- [InlineData(LinkTypes.Paging, LinkTypes.Self, _topResourceSelf, false)]
- [InlineData(LinkTypes.None, LinkTypes.Self, _topResourceSelf, false)]
- [InlineData(LinkTypes.All, LinkTypes.Self, _topRelatedSelf, false)]
- [InlineData(LinkTypes.Self, LinkTypes.Self, _topRelatedSelf, false)]
- [InlineData(LinkTypes.Paging, LinkTypes.Self, _topRelatedSelf, false)]
- [InlineData(LinkTypes.None, LinkTypes.Self, _topRelatedSelf, false)]
- public void BuildTopLevelLinks_GlobalAndResourceConfiguration_ExpectedLinks(
- LinkTypes global, LinkTypes resource, string expectedSelfLink, bool pages)
- {
- // Arrange
- var config = GetConfiguration(topLevelLinks: global);
- var primaryResource = GetArticleResourceContext(topLevelLinks: resource);
- _provider.Setup(m => m.GetResourceContext()).Returns(primaryResource);
-
- bool usePrimaryId = expectedSelfLink != null && expectedSelfLink.Contains("123");
- string relationshipName = expectedSelfLink == _topRelatedSelf ? _relationshipName : null;
- IJsonApiRequest request = GetRequestManager(primaryResource, usePrimaryId, relationshipName);
-
- var builder = new LinkBuilder(config, request, _paginationContext, _provider.Object, _queryStringAccessor);
-
- // Act
- var links = builder.GetTopLevelLinks();
-
- // Assert
- if (!pages && expectedSelfLink == null)
- {
- Assert.Null(links);
- }
- else
- {
- Assert.Equal(links.Self, expectedSelfLink);
-
- if (pages)
- {
- Assert.Equal($"{_host}/articles?foo=bar&page[size]=10", links.First);
- Assert.Equal($"{_host}/articles?foo=bar&page[size]=10", links.Prev);
- Assert.Equal($"{_host}/articles?foo=bar&page[size]=10&page[number]=3", links.Next);
- Assert.Equal($"{_host}/articles?foo=bar&page[size]=10&page[number]=3", links.Last);
- }
- else
- {
- Assert.Null(links.First);
- Assert.Null(links.Prev);
- Assert.Null(links.Next);
- Assert.Null(links.Last);
- }
- }
- }
-
- private IJsonApiRequest GetRequestManager(ResourceContext resourceContext = null, bool usePrimaryId = false, string relationshipName = null)
- {
- var mock = new Mock();
- mock.Setup(m => m.BasePath).Returns(_host);
- mock.Setup(m => m.PrimaryId).Returns(usePrimaryId ? _primaryId.ToString() : null);
- mock.Setup(m => m.Relationship).Returns(relationshipName != null ? new HasOneAttribute {PublicName = relationshipName} : null);
- mock.Setup(m => m.PrimaryResource).Returns(resourceContext);
- mock.Setup(m => m.IsCollection).Returns(true);
- return mock.Object;
- }
-
- private IJsonApiOptions GetConfiguration(LinkTypes resourceLinks = LinkTypes.All, LinkTypes topLevelLinks = LinkTypes.All, LinkTypes relationshipLinks = LinkTypes.All)
- {
- var config = new Mock();
- config.Setup(m => m.TopLevelLinks).Returns(topLevelLinks);
- config.Setup(m => m.ResourceLinks).Returns(resourceLinks);
- config.Setup(m => m.RelationshipLinks).Returns(relationshipLinks);
- config.Setup(m => m.DefaultPageSize).Returns(new PageSize(25));
- return config.Object;
- }
-
- private static IPaginationContext GetPaginationContext()
- {
- var mock = new Mock();
- mock.Setup(x => x.PageNumber).Returns(new PageNumber(2));
- mock.Setup(x => x.PageSize).Returns(new PageSize(10));
- mock.Setup(x => x.TotalPageCount).Returns(3);
-
- return mock.Object;
- }
-
- private ResourceContext GetArticleResourceContext(LinkTypes resourceLinks = LinkTypes.NotConfigured,
- LinkTypes topLevelLinks = LinkTypes.NotConfigured,
- LinkTypes relationshipLinks = LinkTypes.NotConfigured)
- {
- return new ResourceContext
- {
- ResourceLinks = resourceLinks,
- TopLevelLinks = topLevelLinks,
- RelationshipLinks = relationshipLinks,
- PublicName = "articles"
- };
- }
-
- private sealed class FakeRequestQueryStringAccessor : IRequestQueryStringAccessor
- {
- public IQueryCollection Query { get; }
-
- public FakeRequestQueryStringAccessor(string queryString)
- {
- Query = new QueryCollection(QueryHelpers.ParseQuery(queryString));
- }
- }
- }
-}
diff --git a/test/UnitTests/Builders/LinkTests.cs b/test/UnitTests/Builders/LinkTests.cs
deleted file mode 100644
index 1cf5501ec0..0000000000
--- a/test/UnitTests/Builders/LinkTests.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using JsonApiDotNetCore.Resources.Annotations;
-using Xunit;
-
-namespace UnitTests.Builders
-{
- public sealed class LinkTests
- {
- [Theory]
- [InlineData(LinkTypes.All, LinkTypes.Self, true)]
- [InlineData(LinkTypes.All, LinkTypes.Related, true)]
- [InlineData(LinkTypes.All, LinkTypes.Paging, true)]
- [InlineData(LinkTypes.None, LinkTypes.Self, false)]
- [InlineData(LinkTypes.None, LinkTypes.Related, false)]
- [InlineData(LinkTypes.None, LinkTypes.Paging, false)]
- [InlineData(LinkTypes.NotConfigured, LinkTypes.Self, false)]
- [InlineData(LinkTypes.NotConfigured, LinkTypes.Related, false)]
- [InlineData(LinkTypes.NotConfigured, LinkTypes.Paging, false)]
- [InlineData(LinkTypes.Self, LinkTypes.Self, true)]
- [InlineData(LinkTypes.Self, LinkTypes.Related, false)]
- [InlineData(LinkTypes.Self, LinkTypes.Paging, false)]
- [InlineData(LinkTypes.Self, LinkTypes.None, false)]
- [InlineData(LinkTypes.Self, LinkTypes.NotConfigured, false)]
- [InlineData(LinkTypes.Related, LinkTypes.Self, false)]
- [InlineData(LinkTypes.Related, LinkTypes.Related, true)]
- [InlineData(LinkTypes.Related, LinkTypes.Paging, false)]
- [InlineData(LinkTypes.Related, LinkTypes.None, false)]
- [InlineData(LinkTypes.Related, LinkTypes.NotConfigured, false)]
- [InlineData(LinkTypes.Paging, LinkTypes.Self, false)]
- [InlineData(LinkTypes.Paging, LinkTypes.Related, false)]
- [InlineData(LinkTypes.Paging, LinkTypes.Paging, true)]
- [InlineData(LinkTypes.Paging, LinkTypes.None, false)]
- [InlineData(LinkTypes.Paging, LinkTypes.NotConfigured, false)]
- public void LinkHasFlag_BaseLinkAndCheckLink_ExpectedResult(LinkTypes baseLink, LinkTypes checkLink, bool equal)
- {
- Assert.Equal(equal, baseLink.HasFlag(checkLink));
- }
- }
-}
diff --git a/test/UnitTests/Models/LinkTests.cs b/test/UnitTests/Models/LinkTests.cs
deleted file mode 100644
index cda2699d39..0000000000
--- a/test/UnitTests/Models/LinkTests.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using JsonApiDotNetCore.Resources.Annotations;
-using Xunit;
-
-namespace UnitTests.Models
-{
- public sealed class LinkTests
- {
- [Fact]
- public void All_Contains_All_Flags_Except_None()
- {
- // Arrange
- var e = LinkTypes.All;
-
- // Assert
- Assert.True(e.HasFlag(LinkTypes.Self));
- Assert.True(e.HasFlag(LinkTypes.Paging));
- Assert.True(e.HasFlag(LinkTypes.Related));
- Assert.True(e.HasFlag(LinkTypes.All));
- Assert.False(e.HasFlag(LinkTypes.None));
- }
-
- [Fact]
- public void None_Contains_Only_None()
- {
- // Arrange
- var e = LinkTypes.None;
-
- // Assert
- Assert.False(e.HasFlag(LinkTypes.Self));
- Assert.False(e.HasFlag(LinkTypes.Paging));
- Assert.False(e.HasFlag(LinkTypes.Related));
- Assert.False(e.HasFlag(LinkTypes.All));
- Assert.True(e.HasFlag(LinkTypes.None));
- }
-
- [Fact]
- public void Self()
- {
- // Arrange
- var e = LinkTypes.Self;
-
- // Assert
- Assert.True(e.HasFlag(LinkTypes.Self));
- Assert.False(e.HasFlag(LinkTypes.Paging));
- Assert.False(e.HasFlag(LinkTypes.Related));
- Assert.False(e.HasFlag(LinkTypes.All));
- Assert.False(e.HasFlag(LinkTypes.None));
- }
-
- [Fact]
- public void Paging()
- {
- // Arrange
- var e = LinkTypes.Paging;
-
- // Assert
- Assert.False(e.HasFlag(LinkTypes.Self));
- Assert.True(e.HasFlag(LinkTypes.Paging));
- Assert.False(e.HasFlag(LinkTypes.Related));
- Assert.False(e.HasFlag(LinkTypes.All));
- Assert.False(e.HasFlag(LinkTypes.None));
- }
-
- [Fact]
- public void Related()
- {
- // Arrange
- var e = LinkTypes.Related;
-
- // Assert
- Assert.False(e.HasFlag(LinkTypes.Self));
- Assert.False(e.HasFlag(LinkTypes.Paging));
- Assert.True(e.HasFlag(LinkTypes.Related));
- Assert.False(e.HasFlag(LinkTypes.All));
- Assert.False(e.HasFlag(LinkTypes.None));
- }
- }
-}