Skip to content

Commit

Permalink
feat: Added GenerateSuperTypeForJsonSerializerContext.
Browse files Browse the repository at this point in the history
  • Loading branch information
HavenDV committed May 19, 2024
1 parent 3cdbf82 commit 8e221c4
Show file tree
Hide file tree
Showing 20 changed files with 763 additions and 11 deletions.
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,46 @@ dotnet tool install --global openapigenerator.cli --prerelease
oag --help
oag generate openapi.yaml
```
It will generate the code in the "openapi" subdirectory.
It will generate the code in the "openapi" subdirectory.

## Trimming support
Since there are two source generators involved, we will have to create a second project so that the generator for the JsonSerializerContext will “see” our models
- Create new project for your models. And disable methods/constructors generation:
```xml
<PropertyGroup Label="OpenApiGenerator">
<OpenApiGenerator_GenerateSdk>false</OpenApiGenerator_GenerateSdk>
<OpenApiGenerator_GenerateModels>true</OpenApiGenerator_GenerateModels>
<OpenApiGenerator_GenerateSuperTypeForJsonSerializerContext>true</OpenApiGenerator_GenerateSuperTypeForJsonSerializerContext>
</PropertyGroup>
```
- Reference this project in your main project.
- Add `SourceGenerationContext.cs` file to your main project with the following content:
```csharp
using System.Text.Json.Serialization;

namespace Namespace;

[JsonSourceGenerationOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(OpenApiGeneratorTrimmableSupport))]
internal sealed partial class SourceGenerationContext : JsonSerializerContext;
```
- Add the following settings to your main csproj file:
```xml
<PropertyGroup Label="OpenApiGenerator">
<OpenApiGenerator_GenerateSdk>false</OpenApiGenerator_GenerateSdk>
<OpenApiGenerator_GenerateMethods>true</OpenApiGenerator_GenerateMethods>
<OpenApiGenerator_GenerateConstructors>true</OpenApiGenerator_GenerateConstructors>
<OpenApiGenerator_JsonSerializerContext>Namespace.SourceGenerationContext</OpenApiGenerator_JsonSerializerContext>
</PropertyGroup>
```
- Add these settings to your new and main csproj file to enable trimming(or use Directory.Build.props file):
```xml
<PropertyGroup Label="Trimmable" Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))">
<IsAotCompatible>true</IsAotCompatible>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<IsTrimmable>true</IsTrimmable>
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>
```
- It's all! Now you can build your project and use the generated code with full trimming/nativeAOT support.
2 changes: 1 addition & 1 deletion src/libs/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
</ItemGroup>

<PropertyGroup Label="Versioning">
<Version>0.5.0</Version>
<Version>0.6.0</Version>
<MinVerMinimumMajorMinor>0.1</MinVerMinimumMajorMinor>
<MinVerTagPrefix>v</MinVerTagPrefix>
<MinVerDefaultPreReleaseIdentifiers>dev</MinVerDefaultPreReleaseIdentifiers>
Expand Down
1 change: 1 addition & 0 deletions src/libs/OpenApiGenerator.Cli/Commands/GenerateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ private static async Task HandleAsync(
IncludeOperationIds: [],
ExcludeOperationIds: [],
JsonSerializerContext: string.Empty,
GenerateSuperTypeForJsonSerializerContext: false,
GenerateModels: false,
ModelStyle: default,
IncludeModels: [],
Expand Down
23 changes: 23 additions & 0 deletions src/libs/OpenApiGenerator.Core/Generation/Sources.Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,29 @@ namespace {modelData.Namespace}
}}".RemoveBlankLinesWhereOnlyWhitespaces();
}

public static string GenerateSuperClass(
EquatableArray<ModelData> models,
CancellationToken cancellationToken = default)
{
if (models.IsEmpty)
{
return string.Empty;
}

return $@"
#nullable enable
namespace {models[0].Namespace}
{{
public sealed partial class OpenApiGeneratorTrimmableSupport
{{
{models.Select(model => @$"
{string.Empty.ToXmlDocumentationSummary(level: 8)}
public {model.ClassName}? {model.ClassName} {{ get; set; }}").Inject()}
}}
}}".RemoveBlankLinesWhereOnlyWhitespaces();
}

private static string GenerateModel(
ModelData modelData,
int level,
Expand Down
17 changes: 17 additions & 0 deletions src/libs/OpenApiGenerator.Core/Generators/ModelGeneratorMethods.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Immutable;
using OpenApiGenerator.Core.Extensions;
using OpenApiGenerator.Core.Generation;
using OpenApiGenerator.Core.Json;
using OpenApiGenerator.Core.Models;

namespace OpenApiGenerator.Core.Generators;
Expand Down Expand Up @@ -89,6 +90,22 @@ public static FileWithName GetSourceCode(
Name: $"{modelData.FileNameWithoutExtension}.g.cs",
Text: Sources.GenerateModel(modelData, cancellationToken: cancellationToken));
}

public static FileWithName GetSourceCodeForSuperClass(
EquatableArray<ModelData> models,
CancellationToken cancellationToken = default)
{
if (models.IsEmpty ||
!models[0].GenerateSuperTypeForJsonSerializerContext ||
models[0].JsonSerializerType == JsonSerializerType.NewtonsoftJson)
{
return FileWithName.Empty;
}

return new FileWithName(
Name: "OpenApiGeneratorTrimmableSupport.g.cs",
Text: Sources.GenerateSuperClass(models, cancellationToken: cancellationToken));
}

#endregion
}
3 changes: 3 additions & 0 deletions src/libs/OpenApiGenerator.Core/Models/ModelData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public readonly record struct ModelData(
NamingConvention NamingConvention,
ModelStyle Style,
JsonSerializerType JsonSerializerType,
bool GenerateSuperTypeForJsonSerializerContext,
SdkFeatureUsage UseRequiredKeyword,
ImmutableArray<PropertyData> Properties,
string Summary,
Expand Down Expand Up @@ -51,6 +52,7 @@ public static ModelData FromKey(
Namespace: settings.Namespace,
NamingConvention: settings.NamingConvention,
JsonSerializerType: settings.JsonSerializerType,
GenerateSuperTypeForJsonSerializerContext: settings.GenerateSuperTypeForJsonSerializerContext,
UseRequiredKeyword: settings.UseRequiredKeyword,
Style: settings.ModelStyle,
Properties: ImmutableArray<PropertyData>.Empty,
Expand All @@ -74,6 +76,7 @@ public static ModelData FromSchema(
Namespace: settings.Namespace,
NamingConvention: settings.NamingConvention,
JsonSerializerType: settings.JsonSerializerType,
GenerateSuperTypeForJsonSerializerContext: settings.GenerateSuperTypeForJsonSerializerContext,
UseRequiredKeyword: settings.UseRequiredKeyword,
Style: settings.ModelStyle,
Properties: ImmutableArray<PropertyData>.Empty,
Expand Down
1 change: 1 addition & 0 deletions src/libs/OpenApiGenerator.Core/Models/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public readonly record struct Settings(
ImmutableArray<string> IncludeOperationIds,
ImmutableArray<string> ExcludeOperationIds,
string JsonSerializerContext,
bool GenerateSuperTypeForJsonSerializerContext,

bool GenerateModels,
ModelStyle ModelStyle,
Expand Down
27 changes: 25 additions & 2 deletions src/libs/OpenApiGenerator/Generators/ModelGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using H.Generators.Extensions;
using Microsoft.CodeAnalysis;
using OpenApiGenerator.Core.Generators;
using OpenApiGenerator.Core.Json;
using OpenApiGenerator.Core.Models;
using FileWithName = H.Generators.Extensions.FileWithName;

Expand All @@ -21,13 +22,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
{
var settings = context.DetectSettings();

context.AdditionalTextsProvider
var models = context.AdditionalTextsProvider
.Where(static text => text.Path.EndsWith(".yaml", StringComparison.InvariantCultureIgnoreCase))
.Combine(settings)
.SelectAndReportExceptions(PrepareData, context, Id)
.SelectAndReportExceptions(PrepareData, context, Id);

models
.SelectMany(static (x, _) => x)
.SelectAndReportExceptions(GetSourceCode, context, Id)
.AddSource(context);
models
.Select(static (x, _) => x)
.SelectAndReportExceptions(GetSourceCodeForSuperType, context, Id)
.AddSource(context);
}

private static Core.Models.EquatableArray<ModelData> PrepareData(
Expand All @@ -50,6 +57,22 @@ private static FileWithName GetSourceCode(
Name: fileWithName.Name,
Text: fileWithName.Text);
}

private static FileWithName GetSourceCodeForSuperType(
Core.Models.EquatableArray<ModelData> models,
CancellationToken cancellationToken = default)
{
if (models.IsEmpty)
{
return FileWithName.Empty;
}

var fileWithName = ModelGeneratorMethods.GetSourceCodeForSuperClass(models, cancellationToken);

return new FileWithName(
Name: fileWithName.Name,
Text: fileWithName.Text);
}

#endregion
}
2 changes: 2 additions & 0 deletions src/libs/OpenApiGenerator/OpenApiGenerator.props
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
<CompilerVisibleProperty Include="OpenApiGenerator_ExcludeOperationIds"/>
<!-- Full name with namespace of JsonSerializerContext to enable trimming support for System.Text.Json. Default: Empty -->
<CompilerVisibleProperty Include="OpenApiGenerator_JsonSerializerContext"/>
<!-- Allow you to specify only one type for JsonSerializerContext to enable trimming support for System.Text.Json. true/false. Default: false -->
<CompilerVisibleProperty Include="OpenApiGenerator_GenerateSuperTypeForJsonSerializerContext"/>

<!-- Models generation -->
<!-- true/false. Default: false -->
Expand Down
1 change: 1 addition & 0 deletions src/libs/OpenApiGenerator/OptionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public static Settings GetSettings(
ExcludeOperationIds: (options.GetGlobalOption(nameof(Settings.ExcludeOperationIds), prefix)?.Split(';') ??
[]).ToImmutableArray(),
JsonSerializerContext: options.GetGlobalOption(nameof(Settings.JsonSerializerContext), prefix) ?? string.Empty,
GenerateSuperTypeForJsonSerializerContext: options.GetBoolGlobalOption(nameof(Settings.GenerateSuperTypeForJsonSerializerContext), prefix),

GenerateModels: options.GetBoolGlobalOption(nameof(Settings.GenerateModels), prefix),
ModelStyle: options.GetEnumGlobalOption<ModelStyle>(nameof(Settings.ModelStyle), prefix),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<OpenApiGenerator_Namespace>OpenApiGenerator.IntegrationTests</OpenApiGenerator_Namespace>
<OpenApiGenerator_GenerateSdk>false</OpenApiGenerator_GenerateSdk>
<OpenApiGenerator_GenerateModels>true</OpenApiGenerator_GenerateModels>
<OpenApiGenerator_GenerateSuperTypeForJsonSerializerContext>true</OpenApiGenerator_GenerateSuperTypeForJsonSerializerContext>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,5 @@
namespace OpenApiGenerator.IntegrationTests;

[JsonSourceGenerationOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(FullResponse))]
[JsonSerializable(typeof(AsnResponse))]
[JsonSerializable(typeof(RangesResponse))]
[JsonSerializable(typeof(DomainsResponse))]
[JsonSerializable(typeof(AbuseResponse))]
[JsonSerializable(typeof(PrivacyResponse))]
[JsonSerializable(typeof(OpenApiGeneratorTrimmableSupport))]
internal sealed partial class SourceGenerationContext : JsonSerializerContext;
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//HintName: OpenApiGeneratorTrimmableSupport.g.cs

#nullable enable

namespace G
{
public sealed partial class OpenApiGeneratorTrimmableSupport
{
/// <summary>
///
/// </summary>
public GenerateCompletionRequest? GenerateCompletionRequest { get; set; }
/// <summary>
///
/// </summary>
public GenerateCompletionRequestFormat? GenerateCompletionRequestFormat { get; set; }
/// <summary>
///
/// </summary>
public RequestOptions? RequestOptions { get; set; }
/// <summary>
///
/// </summary>
public ResponseFormat? ResponseFormat { get; set; }
/// <summary>
///
/// </summary>
public GenerateCompletionResponse? GenerateCompletionResponse { get; set; }
/// <summary>
///
/// </summary>
public GenerateChatCompletionRequest? GenerateChatCompletionRequest { get; set; }
/// <summary>
///
/// </summary>
public GenerateChatCompletionRequestFormat? GenerateChatCompletionRequestFormat { get; set; }
/// <summary>
///
/// </summary>
public GenerateChatCompletionResponse? GenerateChatCompletionResponse { get; set; }
/// <summary>
///
/// </summary>
public Message? Message { get; set; }
/// <summary>
///
/// </summary>
public MessageRole? MessageRole { get; set; }
/// <summary>
///
/// </summary>
public GenerateEmbeddingRequest? GenerateEmbeddingRequest { get; set; }
/// <summary>
///
/// </summary>
public GenerateEmbeddingResponse? GenerateEmbeddingResponse { get; set; }
/// <summary>
///
/// </summary>
public CreateModelRequest? CreateModelRequest { get; set; }
/// <summary>
///
/// </summary>
public CreateModelResponse? CreateModelResponse { get; set; }
/// <summary>
///
/// </summary>
public CreateModelResponseStatus? CreateModelResponseStatus { get; set; }
/// <summary>
///
/// </summary>
public CreateModelStatus? CreateModelStatus { get; set; }
/// <summary>
///
/// </summary>
public ModelsResponse? ModelsResponse { get; set; }
/// <summary>
///
/// </summary>
public Model? Model { get; set; }
/// <summary>
///
/// </summary>
public ModelInfoRequest? ModelInfoRequest { get; set; }
/// <summary>
///
/// </summary>
public ModelInfo? ModelInfo { get; set; }
/// <summary>
///
/// </summary>
public CopyModelRequest? CopyModelRequest { get; set; }
/// <summary>
///
/// </summary>
public DeleteModelRequest? DeleteModelRequest { get; set; }
/// <summary>
///
/// </summary>
public PullModelRequest? PullModelRequest { get; set; }
/// <summary>
///
/// </summary>
public PullModelResponse? PullModelResponse { get; set; }
/// <summary>
///
/// </summary>
public PullModelResponseStatus? PullModelResponseStatus { get; set; }
/// <summary>
///
/// </summary>
public PullModelStatus? PullModelStatus { get; set; }
/// <summary>
///
/// </summary>
public PushModelRequest? PushModelRequest { get; set; }
/// <summary>
///
/// </summary>
public PushModelResponse? PushModelResponse { get; set; }
/// <summary>
///
/// </summary>
public PushModelResponseStatus? PushModelResponseStatus { get; set; }
/// <summary>
///
/// </summary>
public PushModelStatus? PushModelStatus { get; set; }
}
}
3 changes: 2 additions & 1 deletion src/tests/OpenApiGenerator.SnapshotTests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ public Task Ollama(JsonSerializerType jsonSerializerType)
text: H.Resources.ollamacurated_yaml.AsString())
], new Dictionary<string, string>
{
//["build_property.OpenApiGenerator_JsonSerializerContext"] = "SourceGenerationContext",
["build_property.OpenApiGenerator_GenerateSuperTypeForJsonSerializerContext"] = "true",
// //["build_property.OpenApiGenerator_JsonSerializerContext"] = "SourceGenerationContext",text",
//["build_property.OpenApiGenerator_GenerateConstructors"] = "true",
//["build_property.OpenApiGenerator_GenerateMethods"] = "true",
//["build_property.OpenApiGenerator_IncludeOperationIds"] = "ListModels",
Expand Down
1 change: 1 addition & 0 deletions src/tests/OpenApiGenerator.UnitTests/CacheTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class CacheTests
IncludeOperationIds: ImmutableArray.Create(["123", "456"]),
ExcludeOperationIds: [],
JsonSerializerContext: string.Empty,
GenerateSuperTypeForJsonSerializerContext: false,
GenerateModels: false,
ModelStyle: default,
IncludeModels: ImmutableArray.Create(["123", "456"]),
Expand Down
Loading

0 comments on commit 8e221c4

Please sign in to comment.