Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport various fixes and enhancements from other work in progress #1685

Merged
merged 1 commit into from
Feb 22, 2025
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 @@ -27,6 +27,7 @@
<None Include="..\..\package-icon.png" Visible="false" Pack="True" PackagePath="" />
<None Include="..\..\PackageReadme.md" Visible="false" Pack="True" PackagePath="" />
<None Include="Build\*.props" Pack="True" PackagePath="build" />
<None Include="Templates\*.liquid" Pack="True" PackagePath="Templates" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public ConfigureSwaggerGenOptions(OpenApiOperationIdSelector operationIdSelector

public void Configure(SwaggerGenOptions options)
{
ArgumentNullException.ThrowIfNull(options);

options.SupportNonNullableReferenceTypes();
options.UseAllOfToExtendReferenceSchemas();

Expand All @@ -57,10 +59,10 @@ public void Configure(SwaggerGenOptions options)
options.CustomOperationIds(_operationIdSelector.GetOpenApiOperationId);
options.CustomSchemaIds(_schemaIdSelector.GetSchemaId);

options.OperationFilter<DocumentationOpenApiOperationFilter>();
options.DocumentFilter<ServerDocumentFilter>();
options.DocumentFilter<EndpointOrderingFilter>();
options.DocumentFilter<StringEnumOrderingFilter>();
options.OperationFilter<DocumentationOpenApiOperationFilter>();
options.DocumentFilter<UnusedComponentSchemaCleaner>();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ .. AddJsonApiMetadataToAction(endpoint, endpointMetadataContainer.ResponseMetada

if (replacementDescriptorsForEndpoint.Count > 0)
{
newDescriptors.InsertRange(newDescriptors.IndexOf(endpoint) - 1, replacementDescriptorsForEndpoint);
newDescriptors.InsertRange(newDescriptors.IndexOf(endpoint), replacementDescriptorsForEndpoint);
newDescriptors.Remove(endpoint);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,5 +280,10 @@ public static JsonApiEndpointWrapper FromActionModel(ActionModel actionModel)
JsonApiEndpoints endpoint = EndpointResolver.Instance.GetEndpoint(actionModel.ActionMethod);
return new JsonApiEndpointWrapper(false, endpoint);
}

public override string ToString()
{
return IsAtomicOperationsEndpoint ? "PostOperations" : Value.ToString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,19 @@ public OpenApiSchema GenerateSchema(Type bodyType, SchemaRepository schemaReposi

_linksVisibilitySchemaGenerator.UpdateSchemaForTopLevel(bodyType, fullSchema, schemaRepository);

SetJsonApiVersion(fullSchema);
SetJsonApiVersion(fullSchema, schemaRepository);

return referenceSchema;
}

protected abstract OpenApiSchema GenerateBodySchema(Type bodyType, SchemaRepository schemaRepository);

private void SetJsonApiVersion(OpenApiSchema fullSchema)
private void SetJsonApiVersion(OpenApiSchema fullSchema, SchemaRepository schemaRepository)
{
if (fullSchema.Properties.ContainsKey(JsonApiPropertyName.Jsonapi) && !_options.IncludeJsonApiVersion)
{
fullSchema.Properties.Remove(JsonApiPropertyName.Jsonapi);
schemaRepository.Schemas.Remove(JsonApiPropertyName.Jsonapi);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,17 @@ public void UpdateSchemaForRelationship(Type modelType, OpenApiSchema fullSchema
private void UpdateLinksProperty(OpenApiSchema fullSchemaForLinksContainer, LinkTypes visibleLinkTypes, LinkTypes possibleLinkTypes,
SchemaRepository schemaRepository)
{
OpenApiSchema referenceSchemaForLinks = fullSchemaForLinksContainer.Properties[JsonApiPropertyName.Links].UnwrapLastExtendedSchema();

if ((visibleLinkTypes & possibleLinkTypes) == 0)
{
fullSchemaForLinksContainer.Required.Remove(JsonApiPropertyName.Links);
fullSchemaForLinksContainer.Properties.Remove(JsonApiPropertyName.Links);

schemaRepository.Schemas.Remove(referenceSchemaForLinks.Reference.Id);
}
else if (visibleLinkTypes != possibleLinkTypes)
{
OpenApiSchema referenceSchemaForLinks = fullSchemaForLinksContainer.Properties[JsonApiPropertyName.Links].UnwrapLastExtendedSchema();
string linksSchemaId = referenceSchemaForLinks.Reference.Id;

if (schemaRepository.Schemas.TryGetValue(linksSchemaId, out OpenApiSchema? fullSchemaForLinks))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,15 @@ private sealed class OpenApiEnumVisitor : OpenApiVisitorBase
{
public override void Visit(OpenApiSchema schema)
{
if (schema.Enum.Count > 0)
if (HasSortAnnotation(schema))
{
if (HasSortAnnotation(schema))
if (schema.Enum.Count > 1)
{
if (schema.Enum.Count > 1)
{
OrderEnumMembers(schema);
}
OrderEnumMembers(schema);
}

schema.Extensions.Remove(RequiresSortKey);
}

schema.Extensions.Remove(RequiresSortKey);
}

private static bool HasSortAnnotation(OpenApiSchema schema)
Expand Down
14 changes: 14 additions & 0 deletions test/OpenApiTests/AtomicOperations/OperationsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ public OperationsTests(OpenApiTestContext<OpenApiStartup<OperationsDbContext>, O
[Fact]
public async Task Operations_endpoint_is_exposed()
{
// Act
JsonElement document = await _testContext.GetSwaggerDocumentAsync();

// Assert
document.Should().ContainPath("paths./operations.post").Should().BeJson("""
{
"tags": [
Expand Down Expand Up @@ -125,8 +127,10 @@ public async Task Operations_endpoint_is_exposed()
[Fact]
public async Task Operations_request_component_schemas_are_exposed()
{
// Act
JsonElement document = await _testContext.GetSwaggerDocumentAsync();

// Assert
document.Should().ContainPath("components.schemas").With(schemasElement =>
{
schemasElement.Should().ContainPath("operationsRequestDocument").Should().BeJson("""
Expand Down Expand Up @@ -241,8 +245,10 @@ public async Task Operations_request_component_schemas_are_exposed()
[Fact]
public async Task Operations_response_component_schemas_are_exposed()
{
// Act
JsonElement document = await _testContext.GetSwaggerDocumentAsync();

// Assert
document.Should().ContainPath("components.schemas").With(schemasElement =>
{
schemasElement.Should().ContainPath("operationsResponseDocument").Should().BeJson("""
Expand Down Expand Up @@ -314,8 +320,10 @@ public async Task Operations_response_component_schemas_are_exposed()
[Fact]
public async Task Course_operation_component_schemas_are_exposed()
{
// Act
JsonElement document = await _testContext.GetSwaggerDocumentAsync();

// Assert
document.Should().ContainPath("components.schemas").With(schemasElement =>
{
// resource operations
Expand Down Expand Up @@ -782,8 +790,10 @@ public async Task Course_operation_component_schemas_are_exposed()
[Fact]
public async Task Student_operation_component_schemas_are_exposed()
{
// Act
JsonElement document = await _testContext.GetSwaggerDocumentAsync();

// Assert
document.Should().ContainPath("components.schemas").With(schemasElement =>
{
// resource operations
Expand Down Expand Up @@ -1342,8 +1352,10 @@ public async Task Student_operation_component_schemas_are_exposed()
[Fact]
public async Task Teacher_operation_component_schemas_are_exposed()
{
// Act
JsonElement document = await _testContext.GetSwaggerDocumentAsync();

// Assert
document.Should().ContainPath("components.schemas").With(schemasElement =>
{
// resource operations
Expand Down Expand Up @@ -1846,8 +1858,10 @@ public async Task Teacher_operation_component_schemas_are_exposed()
[Fact]
public async Task Enrollment_operation_component_schemas_are_exposed()
{
// Act
JsonElement document = await _testContext.GetSwaggerDocumentAsync();

// Assert
document.Should().ContainPath("components.schemas").With(schemasElement =>
{
// resource operations
Expand Down
76 changes: 76 additions & 0 deletions test/OpenApiTests/JsonPathBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Collections.ObjectModel;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Resources.Annotations;

#pragma warning disable AV1008 // Class should not be static

namespace OpenApiTests;

internal static class JsonPathBuilder
{
public static readonly IReadOnlyCollection<JsonApiEndpoints> KnownEndpoints =
[
JsonApiEndpoints.GetCollection,
JsonApiEndpoints.GetSingle,
JsonApiEndpoints.GetSecondary,
JsonApiEndpoints.GetRelationship,
JsonApiEndpoints.Post,
JsonApiEndpoints.PostRelationship,
JsonApiEndpoints.Patch,
JsonApiEndpoints.PatchRelationship,
JsonApiEndpoints.Delete,
JsonApiEndpoints.DeleteRelationship
];

public static IReadOnlyDictionary<JsonApiEndpoints, ReadOnlyCollection<string>> GetEndpointPaths(ResourceType resourceType)
{
var endpointToPathMap = new Dictionary<JsonApiEndpoints, List<string>>
{
[JsonApiEndpoints.GetCollection] =
[
$"paths./{resourceType.PublicName}.get",
$"paths./{resourceType.PublicName}.head"
],
[JsonApiEndpoints.GetSingle] =
[
$"paths./{resourceType.PublicName}/{{id}}.get",
$"paths./{resourceType.PublicName}/{{id}}.head"
],
[JsonApiEndpoints.GetSecondary] = [],
[JsonApiEndpoints.GetRelationship] = [],
[JsonApiEndpoints.Post] = [$"paths./{resourceType.PublicName}.post"],
[JsonApiEndpoints.PostRelationship] = [],
[JsonApiEndpoints.Patch] = [$"paths./{resourceType.PublicName}/{{id}}.patch"],
[JsonApiEndpoints.PatchRelationship] = [],
[JsonApiEndpoints.Delete] = [$"paths./{resourceType.PublicName}/{{id}}.delete"],
[JsonApiEndpoints.DeleteRelationship] = []
};

foreach (RelationshipAttribute relationship in resourceType.Relationships)
{
endpointToPathMap[JsonApiEndpoints.GetSecondary].AddRange([
$"paths./{resourceType.PublicName}/{{id}}/{relationship.PublicName}.get",
$"paths./{resourceType.PublicName}/{{id}}/{relationship.PublicName}.head"
]);

endpointToPathMap[JsonApiEndpoints.GetRelationship].AddRange([
$"paths./{resourceType.PublicName}/{{id}}/relationships/{relationship.PublicName}.get",
$"paths./{resourceType.PublicName}/{{id}}/relationships/{relationship.PublicName}.head"
]);

endpointToPathMap[JsonApiEndpoints.PatchRelationship].Add($"paths./{resourceType.PublicName}/{{id}}/relationships/{relationship.PublicName}.patch");

if (relationship is HasManyAttribute)
{
endpointToPathMap[JsonApiEndpoints.PostRelationship].Add(
$"paths./{resourceType.PublicName}/{{id}}/relationships/{relationship.PublicName}.post");

endpointToPathMap[JsonApiEndpoints.DeleteRelationship].Add(
$"paths./{resourceType.PublicName}/{{id}}/relationships/{relationship.PublicName}.delete");
}
}

return endpointToPathMap.ToDictionary(pair => pair.Key, pair => pair.Value.AsReadOnly()).AsReadOnly();
}
}
2 changes: 2 additions & 0 deletions test/OpenApiTests/QueryStrings/QueryStringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public async Task Endpoints_have_query_string_parameter(string endpointPath)
// Act
JsonElement document = await _testContext.GetSwaggerDocumentAsync();

// Assert
document.Should().ContainPath($"paths.{endpointPath}").With(verbElement =>
{
verbElement.Should().ContainPath("parameters").With(parametersElement =>
Expand Down Expand Up @@ -76,6 +77,7 @@ public async Task Endpoints_do_not_have_query_string_parameter(string endpointPa
// Act
JsonElement document = await _testContext.GetSwaggerDocumentAsync();

// Assert
document.Should().ContainPath($"paths.{endpointPath}").With(verbElement =>
{
verbElement.Should().ContainPath("parameters").With(parametersElement =>
Expand Down
Loading
Loading