Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ namespace NoEntityFrameworkExample;
/// <summary>
/// Inserts a null check on member dereference and extension method invocation, to prevent a <see cref="NullReferenceException" /> from being thrown when
/// the expression is compiled and executed.
/// </summary>
/// For example,
/// <code><![CDATA[
/// <example>
/// For example, <code><![CDATA[
/// Database.TodoItems.Where(todoItem => todoItem.Assignee.Id == todoItem.Owner.Id)
/// ]]> </code>
/// would throw if the database contains a
/// TodoItem that doesn't have an assignee.
/// <example></example>
/// ]]></code> would throw if the database
/// contains a TodoItem that doesn't have an assignee.
/// </example>
/// </summary>
public sealed class NullSafeExpressionRewriter : ExpressionVisitor
{
private const string MinValueName = nameof(long.MinValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ private static IReadOnlySet<AttrAttribute> GetAttributesInTypeOrDerived(Resource
}

// Hiding base members using the 'new' keyword instead of 'override' (effectively breaking inheritance) is currently not supported.
// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/knowing-when-to-use-override-and-new-keywords
// https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/knowing-when-to-use-override-and-new-keywords
HashSet<AttrAttribute> attributesInDerivedTypes = [];

foreach (AttrAttribute attributeInDerivedType in resourceType.DirectlyDerivedTypes
Expand Down Expand Up @@ -330,7 +330,7 @@ private static IReadOnlySet<RelationshipAttribute> GetRelationshipsInTypeOrDeriv
}

// Hiding base members using the 'new' keyword instead of 'override' (effectively breaking inheritance) is currently not supported.
// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/knowing-when-to-use-override-and-new-keywords
// https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/knowing-when-to-use-override-and-new-keywords
HashSet<RelationshipAttribute> relationshipsInDerivedTypes = [];

foreach (RelationshipAttribute relationshipInDerivedType in resourceType.DirectlyDerivedTypes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ namespace JsonApiDotNetCore.Resources.Annotations;
/// </summary>
/// <remarks>
/// This is intended for calculated properties that are exposed as JSON:API attributes, which depend on a related entity to always be loaded.
/// <example><![CDATA[
/// public class User : Identifiable
/// <example>
/// <code><![CDATA[
/// public class User : Identifiable<long>
/// {
/// [Attr(AttrCapabilities.AllowFilter | AttrCapabilities.AllowSort)]
/// [NotMapped]
Expand All @@ -25,12 +26,13 @@ namespace JsonApiDotNetCore.Resources.Annotations;
/// public string Last { get; set; }
/// }
///
/// public class Blog : Identifiable
/// public class Blog : Identifiable<long>
/// {
/// [HasOne]
/// public User Author { get; set; }
/// }
/// ]]></example>
/// ]]></code>
/// </example>
/// </remarks>
[PublicAPI]
[AttributeUsage(AttributeTargets.Property)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace JsonApiDotNetCore.Resources.Annotations;
/// </summary>
/// <example>
/// <code><![CDATA[
/// public class Author : Identifiable
/// public class Author : Identifiable<long>
/// {
/// [HasMany]
/// public ISet<Article> Articles { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace JsonApiDotNetCore.Resources.Annotations;
/// </summary>
/// <example>
/// <code><![CDATA[
/// public class Article : Identifiable
/// public class Article : Identifiable<long>
/// {
/// [HasOne]
/// public Author Author { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ public abstract class RelationshipAttribute : ResourceFieldAttribute
/// </summary>
/// <example>
/// <code><![CDATA[
/// public class Article : Identifiable
/// public class Article : Identifiable<long>
/// {
/// [HasOne] // InverseNavigationProperty: Person.Articles
/// public Person Owner { get; set; }
/// }
///
/// public class Person : Identifiable
/// public class Person : Identifiable<long>
/// {
/// [HasMany] // InverseNavigationProperty: Article.Owner
/// public ICollection<Article> Articles { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,12 @@ public static bool CanContainNull(Type type)

/// <summary>
/// Gets the name of a type, including the names of its generic type arguments, without any namespaces.
/// <example>
/// <code><![CDATA[
/// </summary>
/// <returns>
/// Example return value: <code><![CDATA[
/// KeyValuePair<TimeSpan, Nullable<DateTimeOffset>>
/// ]]></code>
/// </example>
/// </summary>
/// </returns>
public static string GetFriendlyTypeName(Type type)
{
ArgumentNullException.ThrowIfNull(type);
Expand Down
2 changes: 1 addition & 1 deletion src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public interface IJsonApiOptions
/// Enables to customize the settings that are used by the <see cref="JsonSerializer" />.
/// </summary>
/// <example>
/// The next example sets the naming convention to camel casing.
/// The following example sets the naming convention to camel casing.
/// <code><![CDATA[
/// options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
/// options.SerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ namespace JsonApiDotNetCore.Controllers.Annotations;
/// <summary>
/// Used on an ASP.NET controller class to indicate which query string parameters are blocked.
/// </summary>
/// <example><![CDATA[
/// <example>
/// <code><![CDATA[
/// [DisableQueryString(JsonApiQueryStringParameters.Sort | JsonApiQueryStringParameters.Page)]
/// public class CustomersController : JsonApiController<Customer> { }
/// ]]></example>
/// <example><![CDATA[
/// ]]></code>
/// <code><![CDATA[
/// [DisableQueryString("skipCache")]
/// public class CustomersController : JsonApiController<Customer> { }
/// ]]></example>
/// ]]></code>
/// </example>
[PublicAPI]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class DisableQueryStringAttribute : Attribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ namespace JsonApiDotNetCore.Controllers.Annotations;
/// <summary>
/// Used on an ASP.NET controller class to indicate that a custom route is used instead of the built-in routing convention.
/// </summary>
/// <example><![CDATA[
/// <example>
/// <code><![CDATA[
/// [DisableRoutingConvention, Route("some/custom/route/to/customers")]
/// public class CustomersController : JsonApiController<Customer> { }
/// ]]></example>
/// ]]></code>
/// </example>
[PublicAPI]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class DisableRoutingConventionAttribute : Attribute;
64 changes: 46 additions & 18 deletions src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,12 @@ protected BaseJsonApiController(IJsonApiOptions options, IResourceGraph resource
}

/// <summary>
/// Gets a collection of primary resources. Example: <code><![CDATA[
/// Gets a collection of primary resources.
/// <para>
/// Example endpoint: <code language="http"><![CDATA[
/// GET /articles HTTP/1.1
/// ]]></code>
/// </para>
/// </summary>
public virtual async Task<IActionResult> GetAsync(CancellationToken cancellationToken)
{
Expand All @@ -105,9 +108,12 @@ public virtual async Task<IActionResult> GetAsync(CancellationToken cancellation
}

/// <summary>
/// Gets a single primary resource by ID. Example: <code><![CDATA[
/// Gets a single primary resource by ID.
/// <para>
/// Example endpoint: <code language="http"><![CDATA[
/// GET /articles/1 HTTP/1.1
/// ]]></code>
/// </para>
/// </summary>
public virtual async Task<IActionResult> GetAsync([DisallowNull] TId id, CancellationToken cancellationToken)
{
Expand All @@ -127,12 +133,15 @@ public virtual async Task<IActionResult> GetAsync([DisallowNull] TId id, Cancell
}

/// <summary>
/// Gets a secondary resource or collection of secondary resources. Example: <code><![CDATA[
/// Gets a secondary resource or collection of secondary resources.
/// <para>
/// Example endpoints: <code language="http"><![CDATA[
/// GET /articles/1/author HTTP/1.1
/// ]]></code> Example:
/// <code><![CDATA[
/// ]]></code>
/// <code language="http"><![CDATA[
/// GET /articles/1/revisions HTTP/1.1
/// ]]></code>
/// </para>
/// </summary>
public virtual async Task<IActionResult> GetSecondaryAsync([DisallowNull] TId id, [PreserveEmptyString] string relationshipName,
CancellationToken cancellationToken)
Expand All @@ -156,13 +165,15 @@ public virtual async Task<IActionResult> GetSecondaryAsync([DisallowNull] TId id
}

/// <summary>
/// Gets a relationship value, which can be a <c>null</c>, a single object or a collection. Example:
/// <code><![CDATA[
/// Gets a relationship value, which can be a <c>null</c>, a single object or a collection.
/// <para>
/// Example endpoints: <code language="http"><![CDATA[
/// GET /articles/1/relationships/author HTTP/1.1
/// ]]></code> Example:
/// <code><![CDATA[
/// ]]></code>
/// <code language="http"><![CDATA[
/// GET /articles/1/relationships/revisions HTTP/1.1
/// ]]></code>
/// </para>
/// </summary>
public virtual async Task<IActionResult> GetRelationshipAsync([DisallowNull] TId id, [PreserveEmptyString] string relationshipName,
CancellationToken cancellationToken)
Expand All @@ -186,9 +197,12 @@ public virtual async Task<IActionResult> GetRelationshipAsync([DisallowNull] TId
}

/// <summary>
/// Creates a new resource with attributes, relationships or both. Example: <code><![CDATA[
/// Creates a new resource with attributes, relationships or both.
/// <para>
/// Example endpoint: <code language="http"><![CDATA[
/// POST /articles HTTP/1.1
/// ]]></code>
/// </para>
/// </summary>
public virtual async Task<IActionResult> PostAsync([FromBody] TResource resource, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -233,9 +247,12 @@ private string GetLocationUrl(string resourceId)
}

/// <summary>
/// Adds resources to a to-many relationship. Example: <code><![CDATA[
/// Adds resources to a to-many relationship.
/// <para>
/// Example endpoint: <code language="http"><![CDATA[
/// POST /articles/1/revisions HTTP/1.1
/// ]]></code>
/// </para>
/// </summary>
/// <param name="id">
/// Identifies the left side of the relationship.
Expand Down Expand Up @@ -274,9 +291,12 @@ public virtual async Task<IActionResult> PostRelationshipAsync([DisallowNull] TI

/// <summary>
/// Updates the attributes and/or relationships of an existing resource. Only the values of sent attributes are replaced. And only the values of sent
/// relationships are replaced. Example: <code><![CDATA[
/// relationships are replaced.
/// <para>
/// Example endpoint: <code language="http"><![CDATA[
/// PATCH /articles/1 HTTP/1.1
/// ]]></code>
/// </para>
/// </summary>
public virtual async Task<IActionResult> PatchAsync([DisallowNull] TId id, [FromBody] TResource resource, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -304,13 +324,15 @@ public virtual async Task<IActionResult> PatchAsync([DisallowNull] TId id, [From
}

/// <summary>
/// Performs a complete replacement of a relationship on an existing resource. Example:
/// <code><![CDATA[
/// Performs a complete replacement of a relationship on an existing resource.
/// <para>
/// Example endpoints: <code language="http"><![CDATA[
/// PATCH /articles/1/relationships/author HTTP/1.1
/// ]]></code> Example:
/// <code><![CDATA[
/// ]]></code>
/// <code language="http"><![CDATA[
/// PATCH /articles/1/relationships/revisions HTTP/1.1
/// ]]></code>
/// </para>
/// </summary>
/// <param name="id">
/// Identifies the left side of the relationship.
Expand Down Expand Up @@ -347,9 +369,12 @@ public virtual async Task<IActionResult> PatchRelationshipAsync([DisallowNull] T
}

/// <summary>
/// Deletes an existing resource. Example: <code><![CDATA[
/// Deletes an existing resource.
/// <para>
/// Example endpoint: <code language="http"><![CDATA[
/// DELETE /articles/1 HTTP/1.1
/// ]]></code>
/// </para>
/// </summary>
public virtual async Task<IActionResult> DeleteAsync([DisallowNull] TId id, CancellationToken cancellationToken)
{
Expand All @@ -369,9 +394,12 @@ public virtual async Task<IActionResult> DeleteAsync([DisallowNull] TId id, Canc
}

/// <summary>
/// Removes resources from a to-many relationship. Example: <code><![CDATA[
/// Removes resources from a to-many relationship.
/// <para>
/// Example endpoint: <code language="http"><![CDATA[
/// DELETE /articles/1/relationships/revisions HTTP/1.1
/// ]]></code>
/// </para>
/// </summary>
/// <param name="id">
/// Identifies the left side of the relationship.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ protected BaseJsonApiOperationsController(IJsonApiOptions options, IResourceGrap
/// none of the operations returns any data, then HTTP 201 is returned instead of 200.
/// </summary>
/// <example>
/// The next example creates a new resource.
/// <code><![CDATA[
/// The following example creates a new resource.
/// <code language="http"><![CDATA[
/// POST /operations HTTP/1.1
/// Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
///
Expand All @@ -71,8 +71,8 @@ protected BaseJsonApiOperationsController(IJsonApiOptions options, IResourceGrap
/// ]]></code>
/// </example>
/// <example>
/// The next example updates an existing resource.
/// <code><![CDATA[
/// The following example updates an existing resource.
/// <code language="http"><![CDATA[
/// POST /operations HTTP/1.1
/// Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
///
Expand All @@ -91,8 +91,8 @@ protected BaseJsonApiOperationsController(IJsonApiOptions options, IResourceGrap
/// ]]></code>
/// </example>
/// <example>
/// The next example deletes an existing resource.
/// <code><![CDATA[
/// The following example deletes an existing resource.
/// <code language="http"><![CDATA[
/// POST /operations HTTP/1.1
/// Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
///
Expand Down
6 changes: 4 additions & 2 deletions src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ namespace JsonApiDotNetCore.Middleware;
/// example, when a controller directly inherits from <see cref="CoreJsonApiController" />), the serializer naming convention is applied on the
/// controller type name (camel-case by default).
/// </summary>
/// <example><![CDATA[
/// <example>
/// <code><![CDATA[
/// // controller name is ignored when resource type is available:
/// public class RandomNameController<SomeResource> : JsonApiController<SomeResource> { } // => /someResources
///
Expand All @@ -26,7 +27,8 @@ namespace JsonApiDotNetCore.Middleware;
///
/// // unable to determine resource type:
/// public class SomeVeryCustomController<SomeResource> : CoreJsonApiController { } // => /someVeryCustom
/// ]]></example>
/// ]]></code>
/// </example>
[PublicAPI]
public sealed partial class JsonApiRoutingConvention : IJsonApiRoutingConvention
{
Expand Down
2 changes: 1 addition & 1 deletion test/TestBuildingBlocks/FakerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static Faker<T> MakeDeterministic<T>(this Faker<T> faker, DateTime? syste
faker.UseSeed(seed);

// Setting the system DateTime to kind Utc, so that faker calls like PastOffset() don't depend on the system time zone.
// See https://docs.microsoft.com/en-us/dotnet/api/system.datetimeoffset.op_implicit?view=net-6.0#remarks
// See https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.op_implicit#remarks
faker.UseDateTimeReference(systemTimeUtc ?? IntegrationTest.DefaultDateTimeUtc.UtcDateTime);

return faker;
Expand Down