Skip to content

Commit 194423a

Browse files
authored
Merge pull request #12 from MobileTeleSystems/feature/preprocessor_log
add logging to preprocessors
2 parents d682f32 + 33bab54 commit 194423a

File tree

12 files changed

+250
-25
lines changed

12 files changed

+250
-25
lines changed

src/ApiCodeGenerator.Abstraction/GeneratorContext.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using Newtonsoft.Json;
5-
using Newtonsoft.Json.Linq;
65

76
namespace ApiCodeGenerator.Abstraction
87
{
@@ -20,16 +19,18 @@ internal GeneratorContext(
2019
Variables = variables;
2120
}
2221

23-
public IReadOnlyDictionary<string, string> Variables { get; set; }
22+
public IReadOnlyDictionary<string, string> Variables { get; }
2423

25-
public IExtensions Extensions { get; set; }
24+
public IExtensions Extensions { get; }
2625

27-
public TextReader? DocumentReader { get; set; }
26+
public TextReader? DocumentReader { get; internal set; }
2827

29-
public Preprocessors? Preprocessors { get; set; }
28+
public Preprocessors? Preprocessors { get; internal set; }
3029

3130
public string? DocumentPath { get; internal set; }
3231

32+
public ILogger? Logger { get; internal set; }
33+
3334
public T? GetSettings<T>(JsonSerializer? jsonSerializer = null, IReadOnlyDictionary<string, string>? additionalVariables = null)
3435
where T : class
3536
=> (T?)_settingsFactory(typeof(T), jsonSerializer, additionalVariables);

src/ApiCodeGenerator.Abstraction/ILogger.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace ApiCodeGenerator.Abstraction
66
{
7-
internal interface ILogger
7+
public interface ILogger
88
{
99
void LogError(string? sourceFile, string message, params object[] messageArgs);
1010

src/ApiCodeGenerator.AsyncApi/AsyncApiContentGenerator.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,19 @@ public static async Task<IContentGenerator> CreateAsync(GeneratorContext context
6868

6969
protected static T InvokePreprocessors<T>(T data,
7070
Preprocessors? preprocessors,
71-
string? filePath)
71+
string? filePath,
72+
ILogger? logger)
7273
{
7374
if (preprocessors?.TryGetValue(typeof(T), out var documentPreprocessors) == true)
7475
{
75-
foreach (var processor in documentPreprocessors.OfType<Func<T, string?, T>>())
76+
foreach (var processor in documentPreprocessors)
7677
{
77-
data = processor.Invoke(data, filePath);
78+
data = processor switch
79+
{
80+
Func<T, string?, T> p => p.Invoke(data, filePath),
81+
Func<T, string?, ILogger?, T> p => p.Invoke(data, filePath, logger),
82+
_ => data,
83+
};
7884
}
7985
}
8086

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

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

94-
document = InvokePreprocessors<AsyncApiDocument>(document, context.Preprocessors, context.DocumentPath);
100+
document = InvokePreprocessors<AsyncApiDocument>(document, context.Preprocessors, context.DocumentPath, context.Logger);
95101
return document;
96102
}
97103

src/ApiCodeGenerator.Core/GenerationTask.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ public async Task<bool> ExecuteAsync(string nswagFilePath,
203203
DocumentReader = result?.Reader,
204204
Preprocessors = PreprocessorHelper.GetPreprocessors(_extensions, documentGenerator?.Preprocessors, Log),
205205
DocumentPath = result?.FilePath,
206+
Logger = Log,
206207
};
207208
}
208209

src/ApiCodeGenerator.Core/NswagDocument/PreprocessorHelper.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ private static (Type DataType, Delegate Processor)? CreatePreprocessor(object pr
7676
return (retType, method.CreateDelegate(delegateType, processor));
7777
}
7878

79+
if (retType != typeof(void)
80+
&& param.Length == 3
81+
&& param[0].ParameterType == retType
82+
&& param[1].ParameterType == typeof(string)
83+
&& param[2].ParameterType == typeof(ILogger))
84+
{
85+
var delegateType = typeof(Func<,,,>).MakeGenericType(retType, typeof(string), typeof(ILogger), retType);
86+
return (retType, method.CreateDelegate(delegateType, processor));
87+
}
88+
7989
return null;
8090
}
8191
}

src/ApiCodeGenerator.OpenApi/ContentGeneratorBase.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,19 @@ public static async Task<IContentGenerator> CreateAsync(GeneratorContext context
7878

7979
protected static T InvokePreprocessors<T>(T data,
8080
Preprocessors? preprocessors,
81-
string? filePath)
81+
string? filePath,
82+
ILogger? logger)
8283
{
8384
if (preprocessors?.TryGetValue(typeof(T), out var openApiDocumentPreprocessors) == true)
8485
{
85-
foreach (var processor in openApiDocumentPreprocessors.OfType<Func<T, string?, T>>())
86+
foreach (var processor in openApiDocumentPreprocessors)
8687
{
87-
data = processor.Invoke(data, filePath);
88+
data = processor switch
89+
{
90+
Func<T, string?, T> p => p.Invoke(data, filePath),
91+
Func<T, string?, ILogger?, T> p => p.Invoke(data, filePath, logger),
92+
_ => data,
93+
};
8894
}
8995
}
9096

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

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

127-
openApiDocument = InvokePreprocessors<OpenApiDocument>(openApiDocument, context.Preprocessors, context.DocumentPath);
133+
openApiDocument = InvokePreprocessors<OpenApiDocument>(openApiDocument, context.Preprocessors, context.DocumentPath, context.Logger);
128134
return openApiDocument;
129135
}
130-
131136
}
132137
}

test/ApiCodeGenerator.AsyncApi.Tests/ApiCodeGenerator.AsyncApi.Tests.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
<ItemGroup>
2525
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
26+
<PackageReference Include="Moq" Version="4.18.2" />
2627
<PackageReference Include="NUnit" Version="3.13.3" />
2728
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
2829
<PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
@@ -34,4 +35,4 @@
3435
<ProjectReference Include="../../src/ApiCodeGenerator.Core/ApiCodeGenerator.Core.csproj" />
3536
</ItemGroup>
3637

37-
</Project>
38+
</Project>

test/ApiCodeGenerator.AsyncApi.Tests/AsyncApiContentGeneratorTests.cs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
using System.Collections.ObjectModel;
12
using ApiCodeGenerator.AsyncApi.DOM;
3+
using Moq;
4+
using Newtonsoft.Json.Linq;
25
using NUnit.Framework.Constraints;
36

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

89+
[Test]
90+
public async Task LoadApiDocument_WithTextPreprocess()
91+
{
92+
const string schemaName = nameof(schemaName);
93+
var settingsJson = new JObject();
94+
95+
Func<string, string?, string> dlgt = new FakeTextPreprocessor("{}").Process;
96+
97+
var context = CreateContext(settingsJson);
98+
99+
context.Preprocessors = new Preprocessors(
100+
new Dictionary<Type, Delegate[]> { [typeof(string)] = [dlgt] });
101+
102+
var gen = (FakeContentGenerator)await FakeContentGenerator.CreateAsync(context);
103+
104+
var apiDocument = gen.Document;
105+
106+
Assert.NotNull(apiDocument);
107+
Assert.That(apiDocument?.Components?.Schemas, Does.ContainKey(schemaName));
108+
var sch = apiDocument?.Components?.Schemas[schemaName].ToJson(Newtonsoft.Json.Formatting.None);
109+
Assert.That(sch, Is.EqualTo("{\"$schema\":\"http://json-schema.org/draft-04/schema#\",\"processed\":{}}"));
110+
}
111+
112+
[Test]
113+
public async Task LoadApiDocument_WithTextPreprocess_Log()
114+
{
115+
const string schemaName = nameof(schemaName);
116+
const string filePath = "cd4bed67-1cc0-44a2-8dd1-30a0bd0c1dee";
117+
var settingsJson = new JObject();
118+
119+
Func<string, string?, ILogger?, string> dlgt = new FakeTextPreprocessor("{}").Process;
120+
121+
var logger = new Mock<ILogger>();
122+
var context = CreateContext(settingsJson);
123+
context.Logger = logger.Object;
124+
context.DocumentPath = filePath;
125+
126+
context.Preprocessors = new Preprocessors(
127+
new Dictionary<Type, Delegate[]> { [typeof(string)] = [dlgt] });
128+
129+
var gen = (FakeContentGenerator)await FakeContentGenerator.CreateAsync(context);
130+
131+
var apiDocument = gen.Document;
132+
133+
Assert.NotNull(apiDocument);
134+
Assert.That(apiDocument?.Components?.Schemas, Does.ContainKey(schemaName));
135+
var sch = apiDocument?.Components?.Schemas[schemaName].ToJson(Newtonsoft.Json.Formatting.None);
136+
Assert.That(sch, Is.EqualTo("{\"$schema\":\"http://json-schema.org/draft-04/schema#\",\"processed\":{}}"));
137+
logger.Verify(l => l.LogWarning(filePath, It.IsAny<string>()));
138+
}
139+
140+
[Test]
141+
public async Task LoadApiDocument_WithModelPreprocess()
142+
{
143+
const string schemaName = nameof(schemaName);
144+
var settingsJson = new JObject();
145+
146+
Func<AsyncApiDocument, string?, AsyncApiDocument> dlgt = new FakeModelPreprocessor("{}").Process;
147+
148+
var context = CreateContext(settingsJson);
149+
context.DocumentReader = new StringReader("{\"components\":{\"schemas\":{\"" + schemaName + "\":{\"$schema\":\"http://json-schema.org/draft-04/schema#\"}}}}");
150+
151+
context.Preprocessors = new Preprocessors(
152+
new Dictionary<Type, Delegate[]>
153+
{
154+
[typeof(AsyncApiDocument)] = [dlgt],
155+
});
156+
157+
var gen = (FakeContentGenerator)await FakeContentGenerator.CreateAsync(context);
158+
159+
var apiDocument = gen.Document;
160+
161+
Assert.NotNull(apiDocument);
162+
Assert.That(apiDocument?.Components?.Schemas, Does.ContainKey(schemaName));
163+
var sch = apiDocument?.Components?.Schemas[schemaName].ToJson(Newtonsoft.Json.Formatting.None);
164+
Assert.That(sch, Is.EqualTo("{\"$schema\":\"http://json-schema.org/draft-04/schema#\",\"properties\":{\"processedModel\":{}}}"));
165+
}
166+
86167
private static Func<Type, Newtonsoft.Json.JsonSerializer?, IReadOnlyDictionary<string, string>?, object?> GetSettingsFactory(string json)
87168
=> (t, s, v) => (s ?? new()).Deserialize(new StringReader(json), t);
88169

@@ -186,4 +267,16 @@ private void ValidateDocument(AsyncApiDocument document)
186267
&& a.Default == "def"
187268
&& a.Examples?.FirstOrDefault() == "exam"));
188269
}
270+
271+
private GeneratorContext CreateContext(JObject settingsJson, Core.ExtensionManager.Extensions? extension = null)
272+
{
273+
extension ??= new();
274+
return new GeneratorContext(
275+
(t, s, _) => settingsJson.ToObject(t, s ?? new()),
276+
extension,
277+
new ReadOnlyDictionary<string, string>(new Dictionary<string, string>()))
278+
{
279+
DocumentReader = new StringReader("{}"),
280+
};
281+
}
189282
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using ApiCodeGenerator.AsyncApi.DOM;
7+
using Newtonsoft.Json.Linq;
8+
using NJsonSchema;
9+
10+
namespace ApiCodeGenerator.AsyncApi.Tests.Infrastructure
11+
{
12+
internal class FakeModelPreprocessor
13+
{
14+
public FakeModelPreprocessor(string settingsJson)
15+
{
16+
Settings = settingsJson;
17+
}
18+
19+
public static List<(JToken Settings, bool AsText, object?[] Arguments)> Invocactions { get; } = new();
20+
21+
public JToken Settings { get; }
22+
23+
public AsyncApiDocument Process(AsyncApiDocument document, string? fileName)
24+
{
25+
Invocactions.Add(new(Settings, true, [document, fileName]));
26+
document.Components?.Schemas.Values.First().Properties.Add(
27+
"processedModel",
28+
new JsonSchemaProperty());
29+
return document;
30+
}
31+
}
32+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Newtonsoft.Json.Linq;
7+
8+
namespace ApiCodeGenerator.AsyncApi.Tests.Infrastructure
9+
{
10+
internal class FakeTextPreprocessor
11+
{
12+
public FakeTextPreprocessor(string settingsJson)
13+
{
14+
Settings = settingsJson;
15+
}
16+
17+
public static List<(JToken Settings, bool AsText, object?[] Arguments)> Invocactions { get; } = new();
18+
19+
public JToken Settings { get; }
20+
21+
public string Process(string data, string? fileName)
22+
{
23+
Invocactions.Add(new(Settings, true, [data, fileName]));
24+
return """
25+
{
26+
"components":{
27+
"schemas":{
28+
"schemaName":{
29+
"$schema":"http://json-schema.org/draft-04/schema#",
30+
"processed":{}
31+
}
32+
}
33+
}
34+
}
35+
""";
36+
}
37+
38+
public string Process(string data, string? fileName, ILogger? logger)
39+
{
40+
logger?.LogWarning(fileName, "test");
41+
return Process(data, fileName);
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)