I have an api with Swagger enabled. In one of the endpoints, I have a request with some values set as default.
[DefaultValue("test value")]
public string? Description { get; set; }
This looks fine in the Swagger page for the api. The default value is used in the Swagger example.
We use NSwag to generate an api client which is consumed in the gateway. When I generate such a client, the request object looks like this:
[System.Text.Json.Serialization.JsonPropertyName("description")]
public string? Description { get; set; } = "test value";
Assigning the value have no effect on the Swagger definition in the gateway, and the value is therefore displayed as description : "string"
How do I properly transfer the DefaultValue annotation to the autogenerated client, in such a way that I can reuse the request object and display the example with the test value string set?
The swagger.json for the api looks like this for the description field:
"properties": {
"description": {
"type": "string",
"default": "test value",
"nullable": true
},
Code based on the solution described below:
public static string AddAttributesToParametersForNSwag(string source, OpenApiDocument doc)
{
Dictionary<SyntaxNode, SyntaxNode> dict = new Dictionary<SyntaxNode, SyntaxNode>();
SyntaxTree tree = CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.LatestMajor, DocumentationMode.Parse, SourceCodeKind.Regular));
var root = (CompilationUnitSyntax)tree.GetRoot();
var classes = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>();
if (classes.Count() == 0)
{
return root.ToFullString();
}
foreach (var classDeclaration in classes.Skip(1))
{
var className = classDeclaration.Identifier.ValueText;
var classParameters = classDeclaration.DescendantNodes().OfType<PropertyDeclarationSyntax>();
foreach (var classParameter in classParameters)
{
string classParameterName = classParameter.Identifier.ValueText;
//Try to find classname as schema in the swagger document
var scheme = doc.Components.Schemas.Where(s => s.Key.ToLower() == className.ToLower()).FirstOrDefault();
if (scheme.Key == null || scheme.Value == null)
{
continue;
}
//Check if schema has the property we're looking for
var swaggerProperty = scheme.Value.Properties.Where(p => p.Key.ToLower() == classParameterName.ToLower()).FirstOrDefault();
if (swaggerProperty.Key == null || swaggerProperty.Value == null)
{
continue;
}
//Nothing is specified for default value, so no attributes to add
if (swaggerProperty.Value.Default == null)
{
continue;
}
string attributeIdentifierName = string.Empty;
if (swaggerProperty.Value.Default != null)
{
if (swaggerProperty.Value.Default.AnyType == Microsoft.OpenApi.Any.AnyType.Null)
{
attributeIdentifierName = "DefaultValue(null)";
}
else
{
//Additional types could be added here
continue;
}
}
var attributeAlreadyExist = classParameter.AttributeLists.Any(a => a.GetText().ToString().Contains("DefaultValue"));
if (attributeAlreadyExist)
{
continue;
}
var parameterAttributes = classParameter.AttributeLists.Add(
SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName(attributeIdentifierName))
)));
dict.Add(classParameter, classParameter.WithAttributeLists(parameterAttributes));
}
}
return root.ReplaceNodes(dict.Keys, (original, rewritten) => dict[original]).NormalizeWhitespace().ToFullString();
}
}