Skip to content
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
11 changes: 6 additions & 5 deletions src/ApiCodeGenerator.Abstraction/GeneratorContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace ApiCodeGenerator.Abstraction
{
Expand All @@ -20,16 +19,18 @@ internal GeneratorContext(
Variables = variables;
}

public IReadOnlyDictionary<string, string> Variables { get; set; }
public IReadOnlyDictionary<string, string> Variables { get; }

public IExtensions Extensions { get; set; }
public IExtensions Extensions { get; }

public TextReader? DocumentReader { get; set; }
public TextReader? DocumentReader { get; internal set; }

public Preprocessors? Preprocessors { get; set; }
public Preprocessors? Preprocessors { get; internal set; }

public string? DocumentPath { get; internal set; }

public ILogger? Logger { get; internal set; }

public T? GetSettings<T>(JsonSerializer? jsonSerializer = null, IReadOnlyDictionary<string, string>? additionalVariables = null)
where T : class
=> (T?)_settingsFactory(typeof(T), jsonSerializer, additionalVariables);
Expand Down
2 changes: 1 addition & 1 deletion src/ApiCodeGenerator.Abstraction/ILogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace ApiCodeGenerator.Abstraction
{
internal interface ILogger
public interface ILogger
{
void LogError(string? sourceFile, string message, params object[] messageArgs);

Expand Down
16 changes: 11 additions & 5 deletions src/ApiCodeGenerator.AsyncApi/AsyncApiContentGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,19 @@ public static async Task<IContentGenerator> CreateAsync(GeneratorContext context

protected static T InvokePreprocessors<T>(T data,
Preprocessors? preprocessors,
string? filePath)
string? filePath,
ILogger? logger)
{
if (preprocessors?.TryGetValue(typeof(T), out var documentPreprocessors) == true)
{
foreach (var processor in documentPreprocessors.OfType<Func<T, string?, T>>())
foreach (var processor in documentPreprocessors)
{
data = processor.Invoke(data, filePath);
data = processor switch
{
Func<T, string?, T> p => p.Invoke(data, filePath),
Func<T, string?, ILogger?, T> p => p.Invoke(data, filePath, logger),
_ => data,
};
}
}

Expand All @@ -84,14 +90,14 @@ protected static T InvokePreprocessors<T>(T data,
private static async Task<AsyncApiDocument> LoadDocumentAsync(GeneratorContext context)
{
var data = await context.DocumentReader!.ReadToEndAsync();
data = InvokePreprocessors<string>(data, context.Preprocessors, context.DocumentPath);
data = InvokePreprocessors<string>(data, context.Preprocessors, context.DocumentPath, context.Logger);

var documentTask = data.StartsWith("{")
? AsyncApiDocument.FromJsonAsync(data)
: AsyncApiDocument.FromYamlAsync(data);
var document = await documentTask.ConfigureAwait(false);

document = InvokePreprocessors<AsyncApiDocument>(document, context.Preprocessors, context.DocumentPath);
document = InvokePreprocessors<AsyncApiDocument>(document, context.Preprocessors, context.DocumentPath, context.Logger);
return document;
}

Expand Down
1 change: 1 addition & 0 deletions src/ApiCodeGenerator.Core/GenerationTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ public async Task<bool> ExecuteAsync(string nswagFilePath,
DocumentReader = result?.Reader,
Preprocessors = PreprocessorHelper.GetPreprocessors(_extensions, documentGenerator?.Preprocessors, Log),
DocumentPath = result?.FilePath,
Logger = Log,
};
}

Expand Down
10 changes: 10 additions & 0 deletions src/ApiCodeGenerator.Core/NswagDocument/PreprocessorHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ private static (Type DataType, Delegate Processor)? CreatePreprocessor(object pr
return (retType, method.CreateDelegate(delegateType, processor));
}

if (retType != typeof(void)
&& param.Length == 3
&& param[0].ParameterType == retType
&& param[1].ParameterType == typeof(string)
&& param[2].ParameterType == typeof(ILogger))
{
var delegateType = typeof(Func<,,,>).MakeGenericType(retType, typeof(string), typeof(ILogger), retType);
return (retType, method.CreateDelegate(delegateType, processor));
}

return null;
}
}
Expand Down
17 changes: 11 additions & 6 deletions src/ApiCodeGenerator.OpenApi/ContentGeneratorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,19 @@ public static async Task<IContentGenerator> CreateAsync(GeneratorContext context

protected static T InvokePreprocessors<T>(T data,
Preprocessors? preprocessors,
string? filePath)
string? filePath,
ILogger? logger)
{
if (preprocessors?.TryGetValue(typeof(T), out var openApiDocumentPreprocessors) == true)
{
foreach (var processor in openApiDocumentPreprocessors.OfType<Func<T, string?, T>>())
foreach (var processor in openApiDocumentPreprocessors)
{
data = processor.Invoke(data, filePath);
data = processor switch
{
Func<T, string?, T> p => p.Invoke(data, filePath),
Func<T, string?, ILogger?, T> p => p.Invoke(data, filePath, logger),
_ => data,
};
}
}

Expand Down Expand Up @@ -118,15 +124,14 @@ protected static TSettings ParseSettings(GeneratorContext context, IReadOnlyDict
protected static async Task<OpenApiDocument> ReadAndProcessOpenApiDocument(GeneratorContext context)
{
var documentStr = context.DocumentReader!.ReadToEnd();
documentStr = InvokePreprocessors<string>(documentStr, context.Preprocessors, context.DocumentPath);
documentStr = InvokePreprocessors<string>(documentStr, context.Preprocessors, context.DocumentPath, context.Logger);

var openApiDocument = !(documentStr.StartsWith("{") && documentStr.EndsWith("}"))
? await OpenApiYamlDocument.FromYamlAsync(documentStr)
: await OpenApiDocument.FromJsonAsync(documentStr);

openApiDocument = InvokePreprocessors<OpenApiDocument>(openApiDocument, context.Preprocessors, context.DocumentPath);
openApiDocument = InvokePreprocessors<OpenApiDocument>(openApiDocument, context.Preprocessors, context.DocumentPath, context.Logger);
return openApiDocument;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
Expand All @@ -34,4 +35,4 @@
<ProjectReference Include="../../src/ApiCodeGenerator.Core/ApiCodeGenerator.Core.csproj" />
</ItemGroup>

</Project>
</Project>
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System.Collections.ObjectModel;
using ApiCodeGenerator.AsyncApi.DOM;
using Moq;
using Newtonsoft.Json.Linq;
using NUnit.Framework.Constraints;

namespace ApiCodeGenerator.AsyncApi.Tests;
Expand Down Expand Up @@ -83,6 +86,84 @@ public async Task ParameterNameReplacementOn()
Assert.IsInstanceOf<ParameterNameGeneratorWithReplace>(settings.ParameterNameGenerator);
}

[Test]
public async Task LoadApiDocument_WithTextPreprocess()
{
const string schemaName = nameof(schemaName);
var settingsJson = new JObject();

Func<string, string?, string> dlgt = new FakeTextPreprocessor("{}").Process;

var context = CreateContext(settingsJson);

context.Preprocessors = new Preprocessors(
new Dictionary<Type, Delegate[]> { [typeof(string)] = [dlgt] });

var gen = (FakeContentGenerator)await FakeContentGenerator.CreateAsync(context);

var apiDocument = gen.Document;

Assert.NotNull(apiDocument);
Assert.That(apiDocument?.Components?.Schemas, Does.ContainKey(schemaName));
var sch = apiDocument?.Components?.Schemas[schemaName].ToJson(Newtonsoft.Json.Formatting.None);
Assert.That(sch, Is.EqualTo("{\"$schema\":\"http://json-schema.org/draft-04/schema#\",\"processed\":{}}"));
}

[Test]
public async Task LoadApiDocument_WithTextPreprocess_Log()
{
const string schemaName = nameof(schemaName);
const string filePath = "cd4bed67-1cc0-44a2-8dd1-30a0bd0c1dee";
var settingsJson = new JObject();

Func<string, string?, ILogger?, string> dlgt = new FakeTextPreprocessor("{}").Process;

var logger = new Mock<ILogger>();
var context = CreateContext(settingsJson);
context.Logger = logger.Object;
context.DocumentPath = filePath;

context.Preprocessors = new Preprocessors(
new Dictionary<Type, Delegate[]> { [typeof(string)] = [dlgt] });

var gen = (FakeContentGenerator)await FakeContentGenerator.CreateAsync(context);

var apiDocument = gen.Document;

Assert.NotNull(apiDocument);
Assert.That(apiDocument?.Components?.Schemas, Does.ContainKey(schemaName));
var sch = apiDocument?.Components?.Schemas[schemaName].ToJson(Newtonsoft.Json.Formatting.None);
Assert.That(sch, Is.EqualTo("{\"$schema\":\"http://json-schema.org/draft-04/schema#\",\"processed\":{}}"));
logger.Verify(l => l.LogWarning(filePath, It.IsAny<string>()));
}

[Test]
public async Task LoadApiDocument_WithModelPreprocess()
{
const string schemaName = nameof(schemaName);
var settingsJson = new JObject();

Func<AsyncApiDocument, string?, AsyncApiDocument> dlgt = new FakeModelPreprocessor("{}").Process;

var context = CreateContext(settingsJson);
context.DocumentReader = new StringReader("{\"components\":{\"schemas\":{\"" + schemaName + "\":{\"$schema\":\"http://json-schema.org/draft-04/schema#\"}}}}");

context.Preprocessors = new Preprocessors(
new Dictionary<Type, Delegate[]>
{
[typeof(AsyncApiDocument)] = [dlgt],
});

var gen = (FakeContentGenerator)await FakeContentGenerator.CreateAsync(context);

var apiDocument = gen.Document;

Assert.NotNull(apiDocument);
Assert.That(apiDocument?.Components?.Schemas, Does.ContainKey(schemaName));
var sch = apiDocument?.Components?.Schemas[schemaName].ToJson(Newtonsoft.Json.Formatting.None);
Assert.That(sch, Is.EqualTo("{\"$schema\":\"http://json-schema.org/draft-04/schema#\",\"properties\":{\"processedModel\":{}}}"));
}

private static Func<Type, Newtonsoft.Json.JsonSerializer?, IReadOnlyDictionary<string, string>?, object?> GetSettingsFactory(string json)
=> (t, s, v) => (s ?? new()).Deserialize(new StringReader(json), t);

Expand Down Expand Up @@ -186,4 +267,16 @@ private void ValidateDocument(AsyncApiDocument document)
&& a.Default == "def"
&& a.Examples?.FirstOrDefault() == "exam"));
}

private GeneratorContext CreateContext(JObject settingsJson, Core.ExtensionManager.Extensions? extension = null)
{
extension ??= new();
return new GeneratorContext(
(t, s, _) => settingsJson.ToObject(t, s ?? new()),
extension,
new ReadOnlyDictionary<string, string>(new Dictionary<string, string>()))
{
DocumentReader = new StringReader("{}"),
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ApiCodeGenerator.AsyncApi.DOM;
using Newtonsoft.Json.Linq;
using NJsonSchema;

namespace ApiCodeGenerator.AsyncApi.Tests.Infrastructure
{
internal class FakeModelPreprocessor
{
public FakeModelPreprocessor(string settingsJson)
{
Settings = settingsJson;
}

public static List<(JToken Settings, bool AsText, object?[] Arguments)> Invocactions { get; } = new();

public JToken Settings { get; }

public AsyncApiDocument Process(AsyncApiDocument document, string? fileName)
{
Invocactions.Add(new(Settings, true, [document, fileName]));
document.Components?.Schemas.Values.First().Properties.Add(
"processedModel",
new JsonSchemaProperty());
return document;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

namespace ApiCodeGenerator.AsyncApi.Tests.Infrastructure
{
internal class FakeTextPreprocessor
{
public FakeTextPreprocessor(string settingsJson)
{
Settings = settingsJson;
}

public static List<(JToken Settings, bool AsText, object?[] Arguments)> Invocactions { get; } = new();

public JToken Settings { get; }

public string Process(string data, string? fileName)
{
Invocactions.Add(new(Settings, true, [data, fileName]));
return """
{
"components":{
"schemas":{
"schemaName":{
"$schema":"http://json-schema.org/draft-04/schema#",
"processed":{}
}
}
}
}
""";
}

public string Process(string data, string? fileName, ILogger? logger)
{
logger?.LogWarning(fileName, "test");
return Process(data, fileName);
}
}
}
Loading
Loading