diff --git a/src/ApiCodeGenerator.Abstraction/GeneratorContext.cs b/src/ApiCodeGenerator.Abstraction/GeneratorContext.cs index 37e308a..ef7a716 100644 --- a/src/ApiCodeGenerator.Abstraction/GeneratorContext.cs +++ b/src/ApiCodeGenerator.Abstraction/GeneratorContext.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace ApiCodeGenerator.Abstraction { @@ -20,16 +19,18 @@ internal GeneratorContext( Variables = variables; } - public IReadOnlyDictionary Variables { get; set; } + public IReadOnlyDictionary 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(JsonSerializer? jsonSerializer = null, IReadOnlyDictionary? additionalVariables = null) where T : class => (T?)_settingsFactory(typeof(T), jsonSerializer, additionalVariables); diff --git a/src/ApiCodeGenerator.Abstraction/ILogger.cs b/src/ApiCodeGenerator.Abstraction/ILogger.cs index 9600565..714fb9a 100644 --- a/src/ApiCodeGenerator.Abstraction/ILogger.cs +++ b/src/ApiCodeGenerator.Abstraction/ILogger.cs @@ -4,7 +4,7 @@ namespace ApiCodeGenerator.Abstraction { - internal interface ILogger + public interface ILogger { void LogError(string? sourceFile, string message, params object[] messageArgs); diff --git a/src/ApiCodeGenerator.AsyncApi/AsyncApiContentGenerator.cs b/src/ApiCodeGenerator.AsyncApi/AsyncApiContentGenerator.cs index 6e3e41e..621f670 100644 --- a/src/ApiCodeGenerator.AsyncApi/AsyncApiContentGenerator.cs +++ b/src/ApiCodeGenerator.AsyncApi/AsyncApiContentGenerator.cs @@ -68,13 +68,19 @@ public static async Task CreateAsync(GeneratorContext context protected static T InvokePreprocessors(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>()) + foreach (var processor in documentPreprocessors) { - data = processor.Invoke(data, filePath); + data = processor switch + { + Func p => p.Invoke(data, filePath), + Func p => p.Invoke(data, filePath, logger), + _ => data, + }; } } @@ -84,14 +90,14 @@ protected static T InvokePreprocessors(T data, private static async Task LoadDocumentAsync(GeneratorContext context) { var data = await context.DocumentReader!.ReadToEndAsync(); - data = InvokePreprocessors(data, context.Preprocessors, context.DocumentPath); + data = InvokePreprocessors(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(document, context.Preprocessors, context.DocumentPath); + document = InvokePreprocessors(document, context.Preprocessors, context.DocumentPath, context.Logger); return document; } diff --git a/src/ApiCodeGenerator.Core/GenerationTask.cs b/src/ApiCodeGenerator.Core/GenerationTask.cs index 72248da..b848016 100644 --- a/src/ApiCodeGenerator.Core/GenerationTask.cs +++ b/src/ApiCodeGenerator.Core/GenerationTask.cs @@ -203,6 +203,7 @@ public async Task ExecuteAsync(string nswagFilePath, DocumentReader = result?.Reader, Preprocessors = PreprocessorHelper.GetPreprocessors(_extensions, documentGenerator?.Preprocessors, Log), DocumentPath = result?.FilePath, + Logger = Log, }; } diff --git a/src/ApiCodeGenerator.Core/NswagDocument/PreprocessorHelper.cs b/src/ApiCodeGenerator.Core/NswagDocument/PreprocessorHelper.cs index e348163..c2feb42 100644 --- a/src/ApiCodeGenerator.Core/NswagDocument/PreprocessorHelper.cs +++ b/src/ApiCodeGenerator.Core/NswagDocument/PreprocessorHelper.cs @@ -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; } } diff --git a/src/ApiCodeGenerator.OpenApi/ContentGeneratorBase.cs b/src/ApiCodeGenerator.OpenApi/ContentGeneratorBase.cs index ce0a599..f92eecb 100644 --- a/src/ApiCodeGenerator.OpenApi/ContentGeneratorBase.cs +++ b/src/ApiCodeGenerator.OpenApi/ContentGeneratorBase.cs @@ -78,13 +78,19 @@ public static async Task CreateAsync(GeneratorContext context protected static T InvokePreprocessors(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>()) + foreach (var processor in openApiDocumentPreprocessors) { - data = processor.Invoke(data, filePath); + data = processor switch + { + Func p => p.Invoke(data, filePath), + Func p => p.Invoke(data, filePath, logger), + _ => data, + }; } } @@ -118,15 +124,14 @@ protected static TSettings ParseSettings(GeneratorContext context, IReadOnlyDict protected static async Task ReadAndProcessOpenApiDocument(GeneratorContext context) { var documentStr = context.DocumentReader!.ReadToEnd(); - documentStr = InvokePreprocessors(documentStr, context.Preprocessors, context.DocumentPath); + documentStr = InvokePreprocessors(documentStr, context.Preprocessors, context.DocumentPath, context.Logger); var openApiDocument = !(documentStr.StartsWith("{") && documentStr.EndsWith("}")) ? await OpenApiYamlDocument.FromYamlAsync(documentStr) : await OpenApiDocument.FromJsonAsync(documentStr); - openApiDocument = InvokePreprocessors(openApiDocument, context.Preprocessors, context.DocumentPath); + openApiDocument = InvokePreprocessors(openApiDocument, context.Preprocessors, context.DocumentPath, context.Logger); return openApiDocument; } - } } diff --git a/test/ApiCodeGenerator.AsyncApi.Tests/ApiCodeGenerator.AsyncApi.Tests.csproj b/test/ApiCodeGenerator.AsyncApi.Tests/ApiCodeGenerator.AsyncApi.Tests.csproj index a17c9c9..98da215 100644 --- a/test/ApiCodeGenerator.AsyncApi.Tests/ApiCodeGenerator.AsyncApi.Tests.csproj +++ b/test/ApiCodeGenerator.AsyncApi.Tests/ApiCodeGenerator.AsyncApi.Tests.csproj @@ -23,6 +23,7 @@ + @@ -34,4 +35,4 @@ - \ No newline at end of file + diff --git a/test/ApiCodeGenerator.AsyncApi.Tests/AsyncApiContentGeneratorTests.cs b/test/ApiCodeGenerator.AsyncApi.Tests/AsyncApiContentGeneratorTests.cs index f07eaaf..58ef864 100644 --- a/test/ApiCodeGenerator.AsyncApi.Tests/AsyncApiContentGeneratorTests.cs +++ b/test/ApiCodeGenerator.AsyncApi.Tests/AsyncApiContentGeneratorTests.cs @@ -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; @@ -83,6 +86,84 @@ public async Task ParameterNameReplacementOn() Assert.IsInstanceOf(settings.ParameterNameGenerator); } + [Test] + public async Task LoadApiDocument_WithTextPreprocess() + { + const string schemaName = nameof(schemaName); + var settingsJson = new JObject(); + + Func dlgt = new FakeTextPreprocessor("{}").Process; + + var context = CreateContext(settingsJson); + + context.Preprocessors = new Preprocessors( + new Dictionary { [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 dlgt = new FakeTextPreprocessor("{}").Process; + + var logger = new Mock(); + var context = CreateContext(settingsJson); + context.Logger = logger.Object; + context.DocumentPath = filePath; + + context.Preprocessors = new Preprocessors( + new Dictionary { [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())); + } + + [Test] + public async Task LoadApiDocument_WithModelPreprocess() + { + const string schemaName = nameof(schemaName); + var settingsJson = new JObject(); + + Func 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 + { + [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?, object?> GetSettingsFactory(string json) => (t, s, v) => (s ?? new()).Deserialize(new StringReader(json), t); @@ -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(new Dictionary())) + { + DocumentReader = new StringReader("{}"), + }; + } } diff --git a/test/ApiCodeGenerator.AsyncApi.Tests/Infrastructure/FakeModelPreprocessor.cs b/test/ApiCodeGenerator.AsyncApi.Tests/Infrastructure/FakeModelPreprocessor.cs new file mode 100644 index 0000000..e52128d --- /dev/null +++ b/test/ApiCodeGenerator.AsyncApi.Tests/Infrastructure/FakeModelPreprocessor.cs @@ -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; + } + } +} diff --git a/test/ApiCodeGenerator.AsyncApi.Tests/Infrastructure/FakeTextPreprocessor.cs b/test/ApiCodeGenerator.AsyncApi.Tests/Infrastructure/FakeTextPreprocessor.cs new file mode 100644 index 0000000..05ba3e6 --- /dev/null +++ b/test/ApiCodeGenerator.AsyncApi.Tests/Infrastructure/FakeTextPreprocessor.cs @@ -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); + } + } +} diff --git a/test/ApiCodeGenerator.OpenApi.Tests/ContentGeneratorFactoryTests.cs b/test/ApiCodeGenerator.OpenApi.Tests/ContentGeneratorFactoryTests.cs index 160b885..2e9ce91 100644 --- a/test/ApiCodeGenerator.OpenApi.Tests/ContentGeneratorFactoryTests.cs +++ b/test/ApiCodeGenerator.OpenApi.Tests/ContentGeneratorFactoryTests.cs @@ -82,13 +82,12 @@ public async Task LoadOpenApiDocument_WithTextPreprocess() const string schemaName = nameof(schemaName); var settingsJson = new JObject(); - Func dlgt = - (doc, fileName) => new Infrastructure.FakeTextPreprocessor("{}").Process(doc, fileName); + Func dlgt = new FakeTextPreprocessor("{}").Process; var context = CreateContext(settingsJson); context.Preprocessors = new Preprocessors( - new Dictionary { [typeof(string)] = new Delegate[] { dlgt } }); + new Dictionary { [typeof(string)] = [dlgt] }); var gen = (CSharpClientContentGenerator)await CSharpClientContentGenerator.CreateAsync(context); @@ -100,14 +99,41 @@ public async Task LoadOpenApiDocument_WithTextPreprocess() Assert.That(sch, Is.EqualTo("{\"$schema\":\"http://json-schema.org/draft-04/schema#\",\"additionalProperties\":false,\"processed\":{}}")); } + [Test] + public async Task LoadOpenApiDocument_WithTextPreprocess_Log() + { + const string schemaName = nameof(schemaName); + const string filePath = "cd4bed67-1cc0-44a2-8dd1-30a0bd0c1dee"; + var settingsJson = new JObject(); + + Func dlgt = new FakeTextPreprocessor("{}").Process; + + var logger = new Mock(); + var context = CreateContext(settingsJson); + context.Logger = logger.Object; + context.DocumentPath = filePath; + + context.Preprocessors = new Preprocessors( + new Dictionary { [typeof(string)] = [dlgt] }); + + var gen = (CSharpClientContentGenerator)await CSharpClientContentGenerator.CreateAsync(context); + + var openApiDocument = GetDocument(gen.Generator); + + Assert.NotNull(openApiDocument); + Assert.That(openApiDocument?.Definitions, Does.ContainKey(schemaName)); + var sch = openApiDocument?.Definitions[schemaName].ToJson(Newtonsoft.Json.Formatting.None); + Assert.That(sch, Is.EqualTo("{\"$schema\":\"http://json-schema.org/draft-04/schema#\",\"additionalProperties\":false,\"processed\":{}}")); + logger.Verify(l => l.LogWarning(filePath, It.IsAny())); + } + [Test] public async Task LoadOpenApiDocument_WithModelPreprocess() { const string schemaName = nameof(schemaName); var settingsJson = new JObject(); - Func dlgt = - (doc, fileName) => new Infrastructure.FakeModelPreprocessor("{}").Process(doc, fileName); + Func dlgt = new FakeModelPreprocessor("{}").Process; var context = CreateContext(settingsJson); context.DocumentReader = new StringReader("{\"definitions\":{\"" + schemaName + "\":{\"$schema\":\"http://json-schema.org/draft-04/schema#\"}}}"); @@ -146,7 +172,7 @@ private GeneratorContext CreateContext(JObject settingsJson, Core.ExtensionManag { extension ??= new(); return new GeneratorContext( - (t, s, v) => settingsJson.ToObject(t, s ?? new()), + (t, s, _) => settingsJson.ToObject(t, s ?? new()), extension, _variables) { diff --git a/test/ApiCodeGenerator.OpenApi.Tests/Infrastructure/FakeTextPreprocessor.cs b/test/ApiCodeGenerator.OpenApi.Tests/Infrastructure/FakeTextPreprocessor.cs index 06855c3..7feedc3 100644 --- a/test/ApiCodeGenerator.OpenApi.Tests/Infrastructure/FakeTextPreprocessor.cs +++ b/test/ApiCodeGenerator.OpenApi.Tests/Infrastructure/FakeTextPreprocessor.cs @@ -20,8 +20,14 @@ public FakeTextPreprocessor(string settingsJson) public string Process(string data, string? fileName) { - Invocactions.Add(new(Settings, true, new object?[] { data, fileName })); + Invocactions.Add(new(Settings, true, [data, fileName])); return "{\"definitions\":{\"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); + } } }