Skip to content
Closed
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
@@ -1,3 +1,4 @@
using JsonApiDotNetCoreExample.DocAnnotations;
using Microsoft.AspNetCore.Mvc;

namespace JsonApiDotNetCoreExample.Controllers;
Expand Down Expand Up @@ -38,6 +39,8 @@ public async Task<IActionResult> PostAsync([FromBody] string? name)
[HttpPut]
[EndpointDescription("Returns another greeting text.")]
[ProducesResponseType<string>(StatusCodes.Status200OK, "text/plain")]
[RequiresAdmin]
[ExpiresOn("2030-01-01")]
public IActionResult Put([FromQuery] string? name)
{
string result = $"Hi, {name}";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
using System.ComponentModel.DataAnnotations;
using JsonApiDotNetCore.AtomicOperations;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCoreExample.DocAnnotations;
using Microsoft.AspNetCore.Mvc;

namespace JsonApiDotNetCoreExample.Controllers;

public sealed class OperationsController(
IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request,
ITargetedFields targetedFields, IAtomicOperationFilter operationFilter)
: JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields, operationFilter);
: JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields, operationFilter)
{
[HttpPost]
[RequiresAdmin]
public override Task<IActionResult> PostOperationsAsync([Required] IList<OperationContainer> operations, CancellationToken cancellationToken)
{
return base.PostOperationsAsync(operations, cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace JsonApiDotNetCoreExample.DocAnnotations;

[AttributeUsage(AttributeTargets.Method)]
internal sealed class ExpiresOnAttribute(string dateValue) : Attribute
{
public DateOnly Value { get; } = DateOnly.Parse(dateValue);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace JsonApiDotNetCoreExample.DocAnnotations;

[AttributeUsage(AttributeTargets.Method)]
internal sealed class RequiresAdminAttribute : Attribute;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Reflection;
using JetBrains.Annotations;
using JsonApiDotNetCoreExample.DocAnnotations;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace JsonApiDotNetCoreExample;

[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
internal sealed class DynamicDocumentationOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (context.MethodInfo.GetCustomAttribute<RequiresAdminAttribute>() != null)
{
UpdateDescription(operation, "**CAUTION**: This endpoint requires admin permissions.");
}

var expiresAtAttribute = context.MethodInfo.GetCustomAttribute<ExpiresOnAttribute>();

if (expiresAtAttribute != null)
{
UpdateDescription(operation, $"**NOTE: This endpoint will no longer be available after {expiresAtAttribute.Value:yyyy-MM-dd}.**");
}
}

private static void UpdateDescription(OpenApiOperation operation, string text)
{
if (string.IsNullOrEmpty(operation.Description))
{
operation.Description = text;
}
else
{
operation.Description += $"\n\n{text}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"tags": [
"nonJsonApi"
],
"description": "Returns another greeting text.",
"description": "Returns another greeting text.\n\n**CAUTION**: This endpoint requires admin permissions.\n\n**NOTE: This endpoint will no longer be available after 2030-01-01.**",
"parameters": [
{
"name": "name",
Expand Down Expand Up @@ -153,6 +153,7 @@
"operations"
],
"summary": "Performs multiple mutations in a linear and atomic manner.",
"description": "**CAUTION**: This endpoint requires admin permissions.",
"operationId": "postOperations",
"requestBody": {
"description": "An array of mutation operations. For syntax, see the [Atomic Operations documentation](https://jsonapi.org/ext/atomic/).",
Expand Down
3 changes: 3 additions & 0 deletions src/Examples/JsonApiDotNetCoreExample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Scalar.AspNetCore;
using Swashbuckle.AspNetCore.SwaggerGen;

[assembly: ExcludeFromCodeCoverage]

Expand All @@ -32,6 +33,8 @@ static WebApplication CreateWebApplication(string[] args)
// Add services to the container.
ConfigureServices(builder);

builder.Services.AddOptions<SwaggerGenOptions>().Configure(options => options.OperationFilter<DynamicDocumentationOperationFilter>());

WebApplication app = builder.Build();

// Configure the HTTP request pipeline.
Expand Down
Loading