Skip to content

Commit

Permalink
(#118) Added explicit type exports for NSwag generation. (#120)
Browse files Browse the repository at this point in the history
* (#118) Added new NSwag schema processing for base typing

* (#118) Updated expected swagger.json
  • Loading branch information
adrianhall authored Oct 4, 2024
1 parent 99ebbf5 commit be92580
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 3 deletions.
17 changes: 17 additions & 0 deletions samples/datasync-server/Sample.Datasync.Server.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Datasync.S
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Datasync.Server.EntityFrameworkCore", "..\..\src\CommunityToolkit.Datasync.Server.EntityFrameworkCore\CommunityToolkit.Datasync.Server.EntityFrameworkCore.csproj", "{2086DD5C-C7C1-4957-B667-847C5FEE832C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Datasync.Server.NSwag", "..\..\src\CommunityToolkit.Datasync.Server.NSwag\CommunityToolkit.Datasync.Server.NSwag.csproj", "{3DD18C86-10C3-490B-A7A6-C2B83C8A3B29}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Datasync.Server.Swashbuckle", "..\..\src\CommunityToolkit.Datasync.Server.Swashbuckle\CommunityToolkit.Datasync.Server.Swashbuckle.csproj", "{FEE92211-3A57-420D-8A76-77AD23B015B6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -37,6 +41,14 @@ Global
{2086DD5C-C7C1-4957-B667-847C5FEE832C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2086DD5C-C7C1-4957-B667-847C5FEE832C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2086DD5C-C7C1-4957-B667-847C5FEE832C}.Release|Any CPU.Build.0 = Release|Any CPU
{3DD18C86-10C3-490B-A7A6-C2B83C8A3B29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DD18C86-10C3-490B-A7A6-C2B83C8A3B29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DD18C86-10C3-490B-A7A6-C2B83C8A3B29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DD18C86-10C3-490B-A7A6-C2B83C8A3B29}.Release|Any CPU.Build.0 = Release|Any CPU
{FEE92211-3A57-420D-8A76-77AD23B015B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FEE92211-3A57-420D-8A76-77AD23B015B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FEE92211-3A57-420D-8A76-77AD23B015B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FEE92211-3A57-420D-8A76-77AD23B015B6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -46,5 +58,10 @@ Global
{54E9A0B2-A0B0-4CB1-8FAD-11DB9E4535A6} = {95358590-6440-469A-8A6A-6ACC47F52966}
{DEC37ED1-B52A-4287-8F63-8210328AFF70} = {95358590-6440-469A-8A6A-6ACC47F52966}
{2086DD5C-C7C1-4957-B667-847C5FEE832C} = {95358590-6440-469A-8A6A-6ACC47F52966}
{3DD18C86-10C3-490B-A7A6-C2B83C8A3B29} = {95358590-6440-469A-8A6A-6ACC47F52966}
{FEE92211-3A57-420D-8A76-77AD23B015B6} = {95358590-6440-469A-8A6A-6ACC47F52966}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B0373E78-5E78-44A4-A907-798EAC85F597}
EndGlobalSection
EndGlobal
27 changes: 27 additions & 0 deletions samples/datasync-server/src/Sample.Datasync.Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// See the LICENSE file in the project root for more information.

using CommunityToolkit.Datasync.Server;
using CommunityToolkit.Datasync.Server.NSwag;
using CommunityToolkit.Datasync.Server.Swashbuckle;
using Microsoft.EntityFrameworkCore;
using Sample.Datasync.Server.Db;

Expand All @@ -11,10 +13,24 @@
string connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new ApplicationException("DefaultConnection is not set");

string? swaggerDriver = builder.Configuration["Swagger:Driver"];
bool nswagEnabled = swaggerDriver?.Equals("NSwag", StringComparison.InvariantCultureIgnoreCase) == true;
bool swashbuckleEnabled = swaggerDriver?.Equals("Swashbuckle", StringComparison.InvariantCultureIgnoreCase) == true;

builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDatasyncServices();
builder.Services.AddControllers();

if (nswagEnabled)
{
_ = builder.Services.AddOpenApiDocument(options => options.AddDatasyncProcessor());
}

if (swashbuckleEnabled)
{
_ = builder.Services.AddSwaggerGen(options => options.AddDatasyncControllers());
}

WebApplication app = builder.Build();

// Initialize the database
Expand All @@ -25,6 +41,17 @@
}

app.UseHttpsRedirection();

if (nswagEnabled)
{
_ = app.UseOpenApi().UseSwaggerUI();
}

if (swashbuckleEnabled)
{
_ = app.UseSwagger().UseSwaggerUI();
}

app.UseAuthorization();
app.MapControllers();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\CommunityToolkit.Datasync.Server.Abstractions\CommunityToolkit.Datasync.Server.Abstractions.csproj" />
<ProjectReference Include="..\..\..\..\src\CommunityToolkit.Datasync.Server.EntityFrameworkCore\CommunityToolkit.Datasync.Server.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\..\..\src\CommunityToolkit.Datasync.Server.NSwag\CommunityToolkit.Datasync.Server.NSwag.csproj" />
<ProjectReference Include="..\..\..\..\src\CommunityToolkit.Datasync.Server.Swashbuckle\CommunityToolkit.Datasync.Server.Swashbuckle.csproj" />
<ProjectReference Include="..\..\..\..\src\CommunityToolkit.Datasync.Server\CommunityToolkit.Datasync.Server.csproj" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Swagger": {
"Driver": "NSwag"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ private static void ProcessDatasyncOperation(OperationProcessorContext context)
string path = context.OperationDescription.Path;
Type entityType = GetTableEntityType(context.ControllerType);
JsonSchema entitySchemaRef = GetEntityReference(context, entityType);
AddMissingSchemaProperties(entitySchemaRef.Reference);

if (method.Equals("DELETE", StringComparison.InvariantCultureIgnoreCase))
{
Expand All @@ -91,20 +92,59 @@ private static void ProcessDatasyncOperation(OperationProcessorContext context)
if (method.Equals("POST", StringComparison.InvariantCultureIgnoreCase))
{
operation.AddConditionalRequestSupport(entitySchemaRef, true);
operation.TryAddConsumes("application/json");
operation.Parameters.Add(new OpenApiParameter { Schema = entitySchemaRef, Kind = OpenApiParameterKind.Body });
operation.SetResponse(HttpStatusCode.Created, entitySchemaRef);
operation.SetResponse(HttpStatusCode.BadRequest);
}

if (method.Equals("PUT", StringComparison.InvariantCultureIgnoreCase))
{
operation.AddConditionalRequestSupport(entitySchemaRef);
operation.TryAddConsumes("application/json");
operation.Parameters.Add(new OpenApiParameter { Schema = entitySchemaRef, Kind = OpenApiParameterKind.Body });
operation.SetResponse(HttpStatusCode.OK, entitySchemaRef);
operation.SetResponse(HttpStatusCode.BadRequest);
operation.SetResponse(HttpStatusCode.NotFound);
operation.SetResponse(HttpStatusCode.Gone);
}
}

private static void AddMissingSchemaProperties(JsonSchema? schema)
{
if (schema is null)
{
return;
}

if (schema.Properties.ContainsKey("id") && schema.Properties.ContainsKey("updatedAt") && schema.Properties.ContainsKey("version"))
{
// Nothing to do - the correct properties are already in the schma.
return;
}

_ = schema.Properties.TryAdd("id", new JsonSchemaProperty
{
Type = JsonObjectType.String,
Description = "The globally unique ID for the entity",
IsRequired = true
});
_ = schema.Properties.TryAdd("updatedAt", new JsonSchemaProperty
{
Type = JsonObjectType.String,
Description = "The ISO-8601 date/time string describing the last time the entity was updated with ms accuracy.",
IsRequired = false
});
_ = schema.Properties.TryAdd("version", new JsonSchemaProperty
{
Type = JsonObjectType.String,
Description = "An opaque string that changes whenever the entity changes.",
IsRequired = false
});

return;
}

/// <summary>
/// Either reads or generates the required entity type schema.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public async Task NSwag_GeneratesSwagger()

// There is an x-generator field that is library specific and completely irrelevant
// to the comparison, so this line will remove it for comparison purposes.
Regex generatorRegex = new("\"x-generator\": \"[^\\\"]+\",");
Regex generatorRegex = new("\"x-generator\": \"[^\\\"]+\",[\r\n]+");
actualContent = generatorRegex.Replace(actualContent, "", 1);
expectedContent = generatorRegex.Replace(expectedContent, "", 1);

Expand Down
73 changes: 71 additions & 2 deletions tests/CommunityToolkit.Datasync.Server.NSwag.Test/swagger.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{

"openapi": "3.0.0",
"openapi": "3.0.0",
"info": {
"title": "My Title",
"version": "1.0.0"
Expand Down Expand Up @@ -223,6 +222,15 @@
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/KitchenSink"
}
}
}
},
"responses": {
"201": {
"description": "Created",
Expand Down Expand Up @@ -576,6 +584,15 @@
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/KitchenSink"
}
}
}
},
"responses": {
"200": {
"description": "OK",
Expand Down Expand Up @@ -660,6 +677,15 @@
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TodoItem"
}
}
}
},
"responses": {
"201": {
"description": "Created",
Expand Down Expand Up @@ -1013,6 +1039,15 @@
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TodoItem"
}
}
}
},
"responses": {
"200": {
"description": "OK",
Expand Down Expand Up @@ -1085,6 +1120,23 @@
"schemas": {
"KitchenSink": {
"title": "KitchenSink",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "The globally unique ID for the entity"
},
"updatedAt": {
"type": "string",
"description": "The ISO-8601 date/time string describing the last time the entity was updated with ms accuracy."
},
"version": {
"type": "string",
"description": "An opaque string that changes whenever the entity changes."
}
},
"definitions": {
"KitchenSinkState": {
"type": "integer",
Expand Down Expand Up @@ -1246,6 +1298,23 @@
},
"TodoItem": {
"title": "TodoItem",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "The globally unique ID for the entity"
},
"updatedAt": {
"type": "string",
"description": "The ISO-8601 date/time string describing the last time the entity was updated with ms accuracy."
},
"version": {
"type": "string",
"description": "An opaque string that changes whenever the entity changes."
}
},
"definitions": {
"EntityTableData": {
"allOf": [
Expand Down

0 comments on commit be92580

Please sign in to comment.