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

Add support for customizable type and contract generator settings #188

Merged
merged 23 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6a3b2cc
Add ContractsGeneratorSettings to RefitGeneratorSettings
christianhelle Oct 11, 2023
6991505
Switch JSON deserialization library to System.Text.Json
christianhelle Oct 11, 2023
63da2d2
Add support for customizing the NSwag CSharpGeneratorSettings from CS…
christianhelle Oct 11, 2023
f98b0d0
Rename `ContractsGeneratorSettings` to `CodeGeneratorSettings`
christianhelle Oct 11, 2023
c71890b
Add codeGeneratorSettings in petstore.refitter
christianhelle Oct 11, 2023
09e55fe
Refactor serialization to use the new Serializer class
christianhelle Oct 11, 2023
11c9a77
Update RefitGeneratorSettings and add NSwagCodeGeneratorSettings
christianhelle Oct 11, 2023
a9e55dd
Update generator settings type in MapCSharpGeneratorSettings
christianhelle Oct 11, 2023
57d0a37
Add new tests for default types in RefitGeneratorSettings
christianhelle Oct 11, 2023
ba8d0e6
Add spacing for readability in test methods
christianhelle Oct 11, 2023
fb48670
Split settings into respective classes for better maintainability
christianhelle Oct 11, 2023
9d47c6d
Remove unnecessary properties from NSwagCodeGeneratorSettings
christianhelle Oct 11, 2023
8db73f9
Add "codeGeneratorSettings" to README and docs
christianhelle Oct 11, 2023
f220ea8
Update Serializer.cs with comments and remove CamelCase naming
christianhelle Oct 11, 2023
947771d
Update documentation to include `codeGeneratorSettings`
christianhelle Oct 11, 2023
e4c02ee
Add explanations for Refitter file properties
christianhelle Oct 11, 2023
ebab1ea
Refactor NSwagCodeGeneratorSettings to CodeGeneratorSettings
christianhelle Oct 11, 2023
f0c6b41
Add tests for CustomCodeGenerator with System.DateTime
christianhelle Oct 11, 2023
599c844
Remove UseIsoDateFormat from settings in CustomCodeGeneratorWithDateT…
christianhelle Oct 11, 2023
8e2e9f1
Remove redundant date format test
christianhelle Oct 11, 2023
eef96ca
Add Serializer tests
christianhelle Oct 12, 2023
a4f5799
Add new configuration options to CodeGeneratorSettings
christianhelle Oct 12, 2023
e2c24b8
Update docfx article on .refitter file format [skip ci]
christianhelle Oct 12, 2023
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
68 changes: 67 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ The following is an example `.refitter` file
"^/pet/.*",
"^/store/.*"
],
"dependencyInjectionSettings": {
"dependencyInjectionSettings": { // Optional
"baseUrl": "https://petstore3.swagger.io/api/v3", // Optional. Leave this blank to set the base address manually
"httpMessageHandlers": [ // Optional
"AuthorizationMessageHandler",
Expand All @@ -161,6 +161,41 @@ The following is an example `.refitter` file
"usePolly": true, // Optional. Set this to true, to configure Polly with a retry policy that uses a jittered backoff. Default=false
"pollyMaxRetryCount": 3, // Optional. Default=6
"firstBackoffRetryInSeconds": 0.5 // Optional. Default=1.0
},
"codeGeneratorSettings": { // Optional. Default settings are the values set in this example
"namespace": "GeneratedCode",
"requiredPropertiesMustBeDefined": true,
"generateDataAnnotations": true,
"anyType": "object",
"dateType": "System.DateTimeOffset",
"dateTimeType": "System.DateTimeOffset",
"timeType": "System.TimeSpan",
"timeSpanType": "System.TimeSpan",
"arrayType": "System.Collections.Generic.ICollection",
"dictionaryType": "System.Collections.Generic.IDictionary",
"arrayInstanceType": "System.Collections.ObjectModel.Collection",
"dictionaryInstanceType": "System.Collections.Generic.Dictionary",
"arrayBaseType": "System.Collections.ObjectModel.Collection",
"dictionaryBaseType": "System.Collections.Generic.Dictionary",
"propertySetterAccessModifier": "",
"generateImmutableArrayProperties": false,
"generateImmutableDictionaryProperties": false,
"handleReferences": false,
"jsonSerializerSettingsTransformationMethod": null,
"generateJsonMethods": false,
"enforceFlagEnums": false,
"inlineNamedDictionaries": false,
"inlineNamedTuples": true,
"inlineNamedArrays": false,
"generateOptionalPropertiesAsNullable": false,
"generateNullableReferenceTypes": false,
"generateNativeRecords": false,
"generateDefaultValues": true,
"inlineNamedAny": false,
"excludedTypeNames": [
"ExcludedTypeFoo",
"ExcludedTypeBar"
]
}
}
```
Expand Down Expand Up @@ -192,6 +227,37 @@ The following is an example `.refitter` file
- `usePolly` - Set this to true to configure the HttpClient to use Polly using a retry policy with a jittered backoff
- `pollyMaxRetryCount` - This is the max retry count used in the Polly retry policy. Default is 6
- `firstBackoffRetryInSeconds` - This is the duration of the initial retry backoff. Default is 1 second
- `codeGeneratorSettings` - Setting this allows customization of the NSwag generated types and contracts
- `namespace` - Default is `GeneratedCode`,
- `requiredPropertiesMustBeDefined` - Default is true,
- `generateDataAnnotations` - Default is true,
- `anyType` - Default is `object`,
- `dateType` - Default is `System.DateTimeOffset`,
- `dateTimeType` - Default is `System.DateTimeOffset`,
- `timeType` - Default is `System.TimeSpan`,
- `timeSpanType` - Default is `System.TimeSpan`,
- `arrayType` - Default is `System.Collections.Generic.ICollection`,
- `dictionaryType` - Default is `System.Collections.Generic.IDictionary`,
- `arrayInstanceType` - Default is `System.Collections.ObjectModel.Collection`,
- `dictionaryInstanceType` - Default is `System.Collections.Generic.Dictionary`,
- `arrayBaseType` - Default is `System.Collections.ObjectModel.Collection`,
- `dictionaryBaseType` - Default is `System.Collections.Generic.Dictionary`,
- `propertySetterAccessModifier` - Default is ``,
- `generateImmutableArrayProperties` - Default is false,
- `generateImmutableDictionaryProperties` - Default is false,
- `handleReferences` - Default is false,
- `jsonSerializerSettingsTransformationMethod` - Default is null,
- `generateJsonMethods` - Default is false,
- `enforceFlagEnums` - Default is false,
- `inlineNamedDictionaries` - Default is false,
- `inlineNamedTuples` - Default is true,
- `inlineNamedArrays` - Default is false,
- `generateOptionalPropertiesAsNullable` - Default is false,
- `generateNullableReferenceTypes` - Default is false,
- `generateNativeRecords` - Default is false
- `generateDefaultValues` - Default is true
- `inlineNamedAny` - Default is false
- `excludedTypeNames` - Default is empty


# Using the generated code
Expand Down
72 changes: 71 additions & 1 deletion docs/docfx_project/articles/refitter-file-format.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## .Refitter File format

The `.refitter` file is a JSON serialized version of the [RefitGeneratorSettings](/api/Refitter.Core.RefitGeneratorSettings.html)

The following is an example `.refitter` file

```js
Expand Down Expand Up @@ -46,10 +48,47 @@ The following is an example `.refitter` file
"usePolly": true, // Optional. Set this to true, to configure Polly with a retry policy that uses a jittered backoff. Default=false
"pollyMaxRetryCount": 3, // Optional. Default=6
"firstBackoffRetryInSeconds": 0.5 // Optional. Default=1.0
},
"codeGeneratorSettings": { // Optional. Default settings are the values set in this example
"namespace": "GeneratedCode",
"requiredPropertiesMustBeDefined": true,
"generateDataAnnotations": true,
"anyType": "object",
"dateType": "System.DateTimeOffset",
"dateTimeType": "System.DateTimeOffset",
"timeType": "System.TimeSpan",
"timeSpanType": "System.TimeSpan",
"arrayType": "System.Collections.Generic.ICollection",
"dictionaryType": "System.Collections.Generic.IDictionary",
"arrayInstanceType": "System.Collections.ObjectModel.Collection",
"dictionaryInstanceType": "System.Collections.Generic.Dictionary",
"arrayBaseType": "System.Collections.ObjectModel.Collection",
"dictionaryBaseType": "System.Collections.Generic.Dictionary",
"propertySetterAccessModifier": "",
"generateImmutableArrayProperties": false,
"generateImmutableDictionaryProperties": false,
"handleReferences": false,
"jsonSerializerSettingsTransformationMethod": null,
"generateJsonMethods": false,
"enforceFlagEnums": false,
"inlineNamedDictionaries": false,
"inlineNamedTuples": true,
"inlineNamedArrays": false,
"generateOptionalPropertiesAsNullable": false,
"generateNullableReferenceTypes": false,
"generateNativeRecords": false,
"generateDefaultValues": true,
"inlineNamedAny": false,
"excludedTypeNames": [
"ExcludedTypeFoo",
"ExcludedTypeBar"
]
}
}
```

Here are some basic explanations of each property:

- `openApiPath` - points to the OpenAPI Specifications file. This can be the path to a file stored on disk, relative to the `.refitter` file. This can also be a URL to a remote file that will be downloaded over HTTP/HTTPS
- `namespace` - the namespace used in the generated code. If not specified, this defaults to `GeneratedCode`
- `naming.useOpenApiTitle` - a boolean indicating whether the OpenApi title should be used. Default is `true`
Expand All @@ -76,4 +115,35 @@ The following is an example `.refitter` file
- `httpMessageHandlers` - A collection of `HttpMessageHandler` that is added to the HttpClient pipeline
- `usePolly` - Set this to true to configure the HttpClient to use Polly using a retry policy with a jittered backoff
- `pollyMaxRetryCount` - This is the max retry count used in the Polly retry policy. Default is 6
- `firstBackoffRetryInSeconds` - This is the duration of the initial retry backoff. Default is 1 second
- `firstBackoffRetryInSeconds` - This is the duration of the initial retry backoff. Default is 1 second
- `codeGeneratorSettings` - Setting this allows customization of the NSwag generated types and contracts
- `namespace` - Default is `GeneratedCode`,
- `requiredPropertiesMustBeDefined` - Default is true,
- `generateDataAnnotations` - Default is true,
- `anyType` - Default is `object`,
- `dateType` - Default is `System.DateTimeOffset`,
- `dateTimeType` - Default is `System.DateTimeOffset`,
- `timeType` - Default is `System.TimeSpan`,
- `timeSpanType` - Default is `System.TimeSpan`,
- `arrayType` - Default is `System.Collections.Generic.ICollection`,
- `dictionaryType` - Default is `System.Collections.Generic.IDictionary`,
- `arrayInstanceType` - Default is `System.Collections.ObjectModel.Collection`,
- `dictionaryInstanceType` - Default is `System.Collections.Generic.Dictionary`,
- `arrayBaseType` - Default is `System.Collections.ObjectModel.Collection`,
- `dictionaryBaseType` - Default is `System.Collections.Generic.Dictionary`,
- `propertySetterAccessModifier` - Default is ``,
- `generateImmutableArrayProperties` - Default is false,
- `generateImmutableDictionaryProperties` - Default is false,
- `handleReferences` - Default is false,
- `jsonSerializerSettingsTransformationMethod` - Default is null,
- `generateJsonMethods` - Default is false,
- `enforceFlagEnums` - Default is false,
- `inlineNamedDictionaries` - Default is false,
- `inlineNamedTuples` - Default is true,
- `inlineNamedArrays` - Default is false,
- `generateOptionalPropertiesAsNullable` - Default is false,
- `generateNullableReferenceTypes` - Default is false,
- `generateNativeRecords` - Default is false
- `generateDefaultValues` - Default is true
- `inlineNamedAny` - Default is false
- `excludedTypeNames` - Default is empty
85 changes: 64 additions & 21 deletions src/Refitter.Core/CSharpClientGeneratorFactory.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,79 @@
using System.Diagnostics;

using NJsonSchema.CodeGeneration.CSharp;

using NSwag;
using NSwag.CodeGeneration.CSharp;

namespace Refitter.Core;

internal class CSharpClientGeneratorFactory
internal class CSharpClientGeneratorFactory(RefitGeneratorSettings settings, OpenApiDocument document)
{
private readonly RefitGeneratorSettings settings;
private readonly OpenApiDocument document;

public CSharpClientGeneratorFactory(RefitGeneratorSettings settings, OpenApiDocument document)
public CustomCSharpClientGenerator Create()
{
this.settings = settings;
this.document = document;
var generator = new CustomCSharpClientGenerator(
document,
new CSharpClientGeneratorSettings
{
GenerateClientClasses = false,
GenerateDtoTypes = true,
GenerateClientInterfaces = false,
GenerateExceptionClasses = false,
CodeGeneratorSettings =
{
PropertyNameGenerator = new CustomCSharpPropertyNameGenerator(),
},
CSharpGeneratorSettings =
{
Namespace = settings.Namespace,
JsonLibrary = CSharpJsonLibrary.SystemTextJson,
TypeAccessModifier = settings.TypeAccessibility.ToString().ToLowerInvariant(),
}
});

MapCSharpGeneratorSettings(
settings.CodeGeneratorSettings,
generator.Settings.CSharpGeneratorSettings);

return generator;
}

public CustomCSharpClientGenerator Create() =>
new(document, new CSharpClientGeneratorSettings
private static void MapCSharpGeneratorSettings(
CodeGeneratorSettings? source,
CSharpGeneratorSettings destination)
{
if (source is null)
{
GenerateClientClasses = false,
GenerateDtoTypes = true,
GenerateClientInterfaces = false,
GenerateExceptionClasses = false,
CodeGeneratorSettings =
return;
}

var defaultInstance = new CodeGeneratorSettings();
foreach (var property in source.GetType().GetProperties())
{
if (property.PropertyType != typeof(string) &&
property.PropertyType != typeof(bool))
{
PropertyNameGenerator = new CustomCSharpPropertyNameGenerator(),
},
CSharpGeneratorSettings =
continue;
}

var value = property.GetValue(source);
if (value == null)
{
Namespace = settings.Namespace,
JsonLibrary = CSharpJsonLibrary.SystemTextJson,
TypeAccessModifier = settings.TypeAccessibility.ToString().ToLowerInvariant()
continue;
}
});

if (value.Equals(property.GetValue(defaultInstance)))
{
continue;
}

var settingsProperty = destination.GetType().GetProperty(property.Name);
if (settingsProperty == null)
{
continue;
}

settingsProperty.SetValue(destination, value);
}
}
}
34 changes: 34 additions & 0 deletions src/Refitter.Core/Serializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Text.Json;

using JsonSerializerOptions = System.Text.Json.JsonSerializerOptions;

namespace Refitter.Core;

/// <summary>
/// Provides methods for serializing and deserializing objects to and from JSON.
/// This serializer is configured to be case-insensitive.
/// </summary>
public static class Serializer
{
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
{
PropertyNameCaseInsensitive = true
};

/// <summary>
/// Deserializes the JSON string to the specified type.
/// </summary>
/// <typeparam name="T">The type to deserialize the JSON string to.</typeparam>
/// <param name="json">The JSON string to deserialize.</param>
/// <returns>The deserialized object of type T.</returns>
public static T Deserialize<T>(string json) =>
JsonSerializer.Deserialize<T>(json, JsonSerializerOptions)!;

/// <summary>
/// Serializes the specified object to a JSON string.
/// </summary>
/// <param name="any">The object to serialize.</param>
/// <returns>The JSON string representation of the object.</returns>
public static string Serialize(object any) =>
JsonSerializer.Serialize(any, JsonSerializerOptions);
}
Loading