Skip to content

Commit b39e2cc

Browse files
authored
Merge pull request #1516 from json-api-dotnet/linktypes-describedby
Add support for configuring the visibility of the "describedby" link
2 parents 2fcfc65 + 86ff196 commit b39e2cc

File tree

6 files changed

+42
-17
lines changed

6 files changed

+42
-17
lines changed

Diff for: src/JsonApiDotNetCore.Annotations/Resources/Annotations/LinkTypes.shared.cs

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ namespace JsonApiDotNetCore.Resources.Annotations;
33
[Flags]
44
public enum LinkTypes
55
{
6-
Self = 1 << 0,
7-
Related = 1 << 1,
8-
Pagination = 1 << 2,
9-
NotConfigured = 1 << 3,
10-
None = 1 << 4,
11-
All = Self | Related | Pagination
6+
NotConfigured = 0,
7+
None = 1 << 0,
8+
Self = 1 << 1,
9+
Related = 1 << 2,
10+
DescribedBy = 1 << 3,
11+
Pagination = 1 << 4,
12+
All = Self | Related | DescribedBy | Pagination
1213
}

Diff for: src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ internal set
7070
/// Configures which links to write in the relationship-level links object for this relationship. Defaults to <see cref="LinkTypes.NotConfigured" />,
7171
/// which falls back to <see cref="ResourceLinksAttribute.RelationshipLinks" /> and then falls back to RelationshipLinks in global options.
7272
/// </summary>
73-
public LinkTypes Links { get; set; } = LinkTypes.NotConfigured;
73+
public LinkTypes Links { get; set; }
7474

7575
/// <summary>
7676
/// Whether or not this relationship can be included using the <c>include</c> query string parameter. This is <c>true</c> by default.

Diff for: src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.netstandard.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace JsonApiDotNetCore.Resources.Annotations;
99
public abstract class RelationshipAttribute : ResourceFieldAttribute
1010
{
1111
/// <summary />
12-
public LinkTypes Links { get; set; } = LinkTypes.NotConfigured;
12+
public LinkTypes Links { get; set; }
1313

1414
/// <summary />
1515
[Obsolete("Use AllowInclude in Capabilities instead.")]

Diff for: src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceLinksAttribute.shared.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,18 @@ public sealed class ResourceLinksAttribute : Attribute
1313
/// Configures which links to write in the top-level links object for this resource type. Defaults to <see cref="LinkTypes.NotConfigured" />, which falls
1414
/// back to TopLevelLinks in global options.
1515
/// </summary>
16-
public LinkTypes TopLevelLinks { get; set; } = LinkTypes.NotConfigured;
16+
public LinkTypes TopLevelLinks { get; set; }
1717

1818
/// <summary>
1919
/// Configures which links to write in the resource-level links object for this resource type. Defaults to <see cref="LinkTypes.NotConfigured" />, which
2020
/// falls back to ResourceLinks in global options.
2121
/// </summary>
22-
public LinkTypes ResourceLinks { get; set; } = LinkTypes.NotConfigured;
22+
public LinkTypes ResourceLinks { get; set; }
2323

2424
/// <summary>
2525
/// Configures which links to write in the relationship-level links object for all relationships of this resource type. Defaults to
2626
/// <see cref="LinkTypes.NotConfigured" />, which falls back to RelationshipLinks in global options. This can be overruled per relationship by setting
2727
/// <see cref="RelationshipAttribute.Links" />.
2828
/// </summary>
29-
public LinkTypes RelationshipLinks { get; set; } = LinkTypes.NotConfigured;
29+
public LinkTypes RelationshipLinks { get; set; }
3030
}

Diff for: src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs

+8-5
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,15 @@ private static string NoAsyncSuffix(string actionName)
100100
SetPaginationInTopLevelLinks(resourceType!, links);
101101
}
102102

103-
string? documentDescriptionUrl = _documentDescriptionLinkProvider.GetUrl();
104-
105-
if (!string.IsNullOrEmpty(documentDescriptionUrl))
103+
if (ShouldIncludeTopLevelLink(LinkTypes.DescribedBy, resourceType))
106104
{
107-
var requestUri = new Uri(HttpContext.Request.GetEncodedUrl());
108-
links.DescribedBy = UriNormalizer.Normalize(documentDescriptionUrl, _options.UseRelativeLinks, requestUri);
105+
string? documentDescriptionUrl = _documentDescriptionLinkProvider.GetUrl();
106+
107+
if (!string.IsNullOrEmpty(documentDescriptionUrl))
108+
{
109+
var requestUri = new Uri(HttpContext.Request.GetEncodedUrl());
110+
links.DescribedBy = UriNormalizer.Normalize(documentDescriptionUrl, _options.UseRelativeLinks, requestUri);
111+
}
109112
}
110113

111114
return links.HasValue() ? links : null;

Diff for: test/JsonApiDotNetCoreTests/UnitTests/Links/LinkInclusionTests.cs

+22-1
Original file line numberDiff line numberDiff line change
@@ -22,36 +22,49 @@ public sealed class LinkInclusionTests
2222
[InlineData(LinkTypes.NotConfigured, LinkTypes.None, LinkTypes.None)]
2323
[InlineData(LinkTypes.NotConfigured, LinkTypes.Self, LinkTypes.Self)]
2424
[InlineData(LinkTypes.NotConfigured, LinkTypes.Related, LinkTypes.Related)]
25+
[InlineData(LinkTypes.NotConfigured, LinkTypes.DescribedBy, LinkTypes.DescribedBy)]
2526
[InlineData(LinkTypes.NotConfigured, LinkTypes.Pagination, LinkTypes.Pagination)]
2627
[InlineData(LinkTypes.NotConfigured, LinkTypes.All, LinkTypes.All)]
2728
[InlineData(LinkTypes.None, LinkTypes.NotConfigured, LinkTypes.None)]
2829
[InlineData(LinkTypes.None, LinkTypes.None, LinkTypes.None)]
2930
[InlineData(LinkTypes.None, LinkTypes.Self, LinkTypes.None)]
3031
[InlineData(LinkTypes.None, LinkTypes.Related, LinkTypes.None)]
32+
[InlineData(LinkTypes.None, LinkTypes.DescribedBy, LinkTypes.None)]
3133
[InlineData(LinkTypes.None, LinkTypes.Pagination, LinkTypes.None)]
3234
[InlineData(LinkTypes.None, LinkTypes.All, LinkTypes.None)]
3335
[InlineData(LinkTypes.Self, LinkTypes.NotConfigured, LinkTypes.Self)]
3436
[InlineData(LinkTypes.Self, LinkTypes.None, LinkTypes.Self)]
3537
[InlineData(LinkTypes.Self, LinkTypes.Self, LinkTypes.Self)]
3638
[InlineData(LinkTypes.Self, LinkTypes.Related, LinkTypes.Self)]
39+
[InlineData(LinkTypes.Self, LinkTypes.DescribedBy, LinkTypes.Self)]
3740
[InlineData(LinkTypes.Self, LinkTypes.Pagination, LinkTypes.Self)]
3841
[InlineData(LinkTypes.Self, LinkTypes.All, LinkTypes.Self)]
3942
[InlineData(LinkTypes.Related, LinkTypes.NotConfigured, LinkTypes.Related)]
4043
[InlineData(LinkTypes.Related, LinkTypes.None, LinkTypes.Related)]
4144
[InlineData(LinkTypes.Related, LinkTypes.Self, LinkTypes.Related)]
4245
[InlineData(LinkTypes.Related, LinkTypes.Related, LinkTypes.Related)]
46+
[InlineData(LinkTypes.Related, LinkTypes.DescribedBy, LinkTypes.Related)]
4347
[InlineData(LinkTypes.Related, LinkTypes.Pagination, LinkTypes.Related)]
4448
[InlineData(LinkTypes.Related, LinkTypes.All, LinkTypes.Related)]
49+
[InlineData(LinkTypes.DescribedBy, LinkTypes.NotConfigured, LinkTypes.DescribedBy)]
50+
[InlineData(LinkTypes.DescribedBy, LinkTypes.None, LinkTypes.DescribedBy)]
51+
[InlineData(LinkTypes.DescribedBy, LinkTypes.Self, LinkTypes.DescribedBy)]
52+
[InlineData(LinkTypes.DescribedBy, LinkTypes.Related, LinkTypes.DescribedBy)]
53+
[InlineData(LinkTypes.DescribedBy, LinkTypes.DescribedBy, LinkTypes.DescribedBy)]
54+
[InlineData(LinkTypes.DescribedBy, LinkTypes.Pagination, LinkTypes.DescribedBy)]
55+
[InlineData(LinkTypes.DescribedBy, LinkTypes.All, LinkTypes.DescribedBy)]
4556
[InlineData(LinkTypes.Pagination, LinkTypes.NotConfigured, LinkTypes.Pagination)]
4657
[InlineData(LinkTypes.Pagination, LinkTypes.None, LinkTypes.Pagination)]
4758
[InlineData(LinkTypes.Pagination, LinkTypes.Self, LinkTypes.Pagination)]
4859
[InlineData(LinkTypes.Pagination, LinkTypes.Related, LinkTypes.Pagination)]
60+
[InlineData(LinkTypes.Pagination, LinkTypes.DescribedBy, LinkTypes.Pagination)]
4961
[InlineData(LinkTypes.Pagination, LinkTypes.Pagination, LinkTypes.Pagination)]
5062
[InlineData(LinkTypes.Pagination, LinkTypes.All, LinkTypes.Pagination)]
5163
[InlineData(LinkTypes.All, LinkTypes.NotConfigured, LinkTypes.All)]
5264
[InlineData(LinkTypes.All, LinkTypes.None, LinkTypes.All)]
5365
[InlineData(LinkTypes.All, LinkTypes.Self, LinkTypes.All)]
5466
[InlineData(LinkTypes.All, LinkTypes.Related, LinkTypes.All)]
67+
[InlineData(LinkTypes.All, LinkTypes.DescribedBy, LinkTypes.All)]
5568
[InlineData(LinkTypes.All, LinkTypes.Pagination, LinkTypes.All)]
5669
[InlineData(LinkTypes.All, LinkTypes.All, LinkTypes.All)]
5770
public void Applies_cascading_settings_for_top_level_links(LinkTypes linksInResourceType, LinkTypes linksInOptions, LinkTypes expected)
@@ -88,7 +101,7 @@ public void Applies_cascading_settings_for_top_level_links(LinkTypes linksInReso
88101
var linkGenerator = new FakeLinkGenerator();
89102
var controllerResourceMapping = new FakeControllerResourceMapping();
90103
var paginationParser = new PaginationParser();
91-
var documentDescriptionLinkProvider = new NoDocumentDescriptionLinkProvider();
104+
var documentDescriptionLinkProvider = new NonEmptyDocumentDescriptionLinkProvider();
92105

93106
var linkBuilder = new LinkBuilder(options, request, paginationContext, httpContextAccessor, linkGenerator, controllerResourceMapping, paginationParser,
94107
documentDescriptionLinkProvider);
@@ -435,4 +448,12 @@ public override string GetUriByAddress<TAddress>(TAddress address, RouteValueDic
435448
throw new NotImplementedException();
436449
}
437450
}
451+
452+
private sealed class NonEmptyDocumentDescriptionLinkProvider : IDocumentDescriptionLinkProvider
453+
{
454+
public string GetUrl()
455+
{
456+
return "openapi.yaml";
457+
}
458+
}
438459
}

0 commit comments

Comments
 (0)