diff --git a/src/ApiCodeGenerator.AsyncApi/ApiCodeGenerator.AsyncApi.csproj b/src/ApiCodeGenerator.AsyncApi/ApiCodeGenerator.AsyncApi.csproj
index e1feae4..eb28084 100644
--- a/src/ApiCodeGenerator.AsyncApi/ApiCodeGenerator.AsyncApi.csproj
+++ b/src/ApiCodeGenerator.AsyncApi/ApiCodeGenerator.AsyncApi.csproj
@@ -27,7 +27,8 @@
-
+
+
diff --git a/src/ApiCodeGenerator.AsyncApi/AsyncApiContentGenerator.cs b/src/ApiCodeGenerator.AsyncApi/AsyncApiContentGenerator.cs
index 621f670..4c81399 100644
--- a/src/ApiCodeGenerator.AsyncApi/AsyncApiContentGenerator.cs
+++ b/src/ApiCodeGenerator.AsyncApi/AsyncApiContentGenerator.cs
@@ -92,10 +92,23 @@ private static async Task LoadDocumentAsync(GeneratorContext c
var data = await context.DocumentReader!.ReadToEndAsync();
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);
+ AsyncApiDocument document;
+ try
+ {
+ document = await AsyncApiDocument.FromJsonAsync(data, context.DocumentPath).ConfigureAwait(false);
+ }
+ catch (JsonException ex)
+ {
+ try
+ {
+ document = await AsyncApiDocument.FromYamlAsync(data, context.DocumentPath).ConfigureAwait(false);
+ }
+ catch (YamlDotNet.Core.YamlException ex2)
+ {
+ throw new InvalidOperationException(
+ $"Can not read document as JSON ({ex.Message}) or YAML ({ex2.Message}).");
+ }
+ }
document = InvokePreprocessors(document, context.Preprocessors, context.DocumentPath, context.Logger);
return document;
diff --git a/src/ApiCodeGenerator.AsyncApi/DOM/AsyncApiDocument.cs b/src/ApiCodeGenerator.AsyncApi/DOM/AsyncApiDocument.cs
index 1d6d54f..940d204 100644
--- a/src/ApiCodeGenerator.AsyncApi/DOM/AsyncApiDocument.cs
+++ b/src/ApiCodeGenerator.AsyncApi/DOM/AsyncApiDocument.cs
@@ -2,12 +2,13 @@
using Newtonsoft.Json.Linq;
using NJsonSchema;
using NJsonSchema.Generation;
+using NJsonSchema.Yaml;
using YamlDotNet.Serialization;
namespace ApiCodeGenerator.AsyncApi.DOM
{
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
- public class AsyncApiDocument
+ public class AsyncApiDocument : IDocumentPathProvider
{
private static readonly JsonSerializerSettings JSONSERIALIZERSETTINGS = new()
{
@@ -41,14 +42,27 @@ public class AsyncApiDocument
[JsonProperty("externalDocs", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
public ICollection? ExternalDocs { get; set; }
+ [JsonIgnore]
+ public string? DocumentPath { get; set; }
+
///
/// Load document from JSON text.
///
/// JSON text.
/// AsyncApi document object model.
public static Task FromJsonAsync(string data)
+ => FromJsonAsync(data, null);
+
+ ///
+ /// Load document from JSON text.
+ ///
+ /// JSON text.
+ /// Path to document.
+ /// AsyncApi document object model.
+ public static Task FromJsonAsync(string data, string? documentPath)
{
var document = JsonConvert.DeserializeObject(data, JSONSERIALIZERSETTINGS)!;
+ document.DocumentPath = documentPath;
return UpdateSchemaReferencesAsync(document);
}
@@ -58,6 +72,15 @@ public static Task FromJsonAsync(string data)
/// YAML text.
/// AsyncApi document object model.
public static Task FromYamlAsync(string data)
+ => FromYamlAsync(data, null);
+
+ ///
+ /// Load document from YAML text.
+ ///
+ /// YAML text.
+ /// Path to document.
+ /// AsyncApi document object model.
+ public static Task FromYamlAsync(string data, string? documentPath)
{
var deserializer = new DeserializerBuilder().Build();
using var reader = new StringReader(data);
@@ -66,14 +89,17 @@ public static Task FromYamlAsync(string data)
var jObject = JObject.FromObject(yamlDocument)!;
var serializer = JsonSerializer.Create(JSONSERIALIZERSETTINGS);
var doc = jObject.ToObject(serializer)!;
+ doc.DocumentPath = documentPath;
return UpdateSchemaReferencesAsync(doc);
}
- private static Task UpdateSchemaReferencesAsync(AsyncApiDocument document)
- => JsonSchemaReferenceUtilities.UpdateSchemaReferencesAsync(
- document,
- new(new AsyncApiSchemaResolver(document, new SystemTextJsonSchemaGeneratorSettings())))
- .ContinueWith(t => document);
+ private static async Task UpdateSchemaReferencesAsync(AsyncApiDocument document)
+ {
+ await JsonSchemaReferenceUtilities.UpdateSchemaReferencesAsync(
+ document,
+ new JsonAndYamlReferenceResolver(new AsyncApiSchemaResolver(document, new SystemTextJsonSchemaGeneratorSettings())));
+ return document;
+ }
}
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
}
diff --git a/src/ApiCodeGenerator.OpenApi/ApiCodeGenerator.OpenApi.csproj b/src/ApiCodeGenerator.OpenApi/ApiCodeGenerator.OpenApi.csproj
index 3dff82d..50b5434 100644
--- a/src/ApiCodeGenerator.OpenApi/ApiCodeGenerator.OpenApi.csproj
+++ b/src/ApiCodeGenerator.OpenApi/ApiCodeGenerator.OpenApi.csproj
@@ -16,9 +16,10 @@
+
-
\ No newline at end of file
+
diff --git a/src/ApiCodeGenerator.OpenApi/ContentGeneratorBase.cs b/src/ApiCodeGenerator.OpenApi/ContentGeneratorBase.cs
index f92eecb..0aff74d 100644
--- a/src/ApiCodeGenerator.OpenApi/ContentGeneratorBase.cs
+++ b/src/ApiCodeGenerator.OpenApi/ContentGeneratorBase.cs
@@ -11,6 +11,7 @@
using NSwag;
using NSwag.CodeGeneration;
using NSwag.CodeGeneration.CSharp;
+using YamlDotNet.Core;
namespace ApiCodeGenerator.OpenApi
{
@@ -126,9 +127,24 @@ protected static async Task ReadAndProcessOpenApiDocument(Gener
var documentStr = context.DocumentReader!.ReadToEnd();
documentStr = InvokePreprocessors(documentStr, context.Preprocessors, context.DocumentPath, context.Logger);
- var openApiDocument = !(documentStr.StartsWith("{") && documentStr.EndsWith("}"))
- ? await OpenApiYamlDocument.FromYamlAsync(documentStr)
- : await OpenApiDocument.FromJsonAsync(documentStr);
+ OpenApiDocument openApiDocument;
+
+ try
+ {
+ openApiDocument = await OpenApiDocument.FromJsonAsync(documentStr, context.DocumentPath);
+ }
+ catch (JsonException ex)
+ {
+ try
+ {
+ openApiDocument = await OpenApiYamlDocument.FromYamlAsync(documentStr, context.DocumentPath);
+ }
+ catch (YamlException ex2)
+ {
+ throw new InvalidOperationException(
+ $"Can not read document as JSON ({ex.Message}) or YAML ({ex2.Message}).");
+ }
+ }
openApiDocument = InvokePreprocessors(openApiDocument, context.Preprocessors, context.DocumentPath, context.Logger);
return openApiDocument;
diff --git a/test/ApiCodeGenerator.AsyncApi.Tests/AsyncApiContentGeneratorTests.cs b/test/ApiCodeGenerator.AsyncApi.Tests/AsyncApiContentGeneratorTests.cs
index 38b4d27..c43ef14 100644
--- a/test/ApiCodeGenerator.AsyncApi.Tests/AsyncApiContentGeneratorTests.cs
+++ b/test/ApiCodeGenerator.AsyncApi.Tests/AsyncApiContentGeneratorTests.cs
@@ -164,6 +164,22 @@ public async Task LoadApiDocument_WithModelPreprocess()
Assert.That(sch, Is.EqualTo("{\"$schema\":\"http://json-schema.org/draft-04/schema#\",\"properties\":{\"processedModel\":{}}}"));
}
+ [TestCase("externalRef.json")]
+ [TestCase("externalRef.yaml")]
+ public async Task LoadApiDocument_WithExternalRef(string documentPath)
+ {
+ var settingsJson = new JObject();
+ var context = CreateContext(settingsJson);
+ context.DocumentReader = await TestHelpers.LoadApiDocumentAsync(documentPath);
+ context.DocumentPath = documentPath;
+
+ var contentGenerator = (FakeContentGenerator)await FakeContentGenerator.CreateAsync(context);
+
+ var document = contentGenerator.Document;
+
+ Assert.NotNull(document.Components?.Messages["lightMeasured"].Reference);
+ }
+
private static Func?, object?> GetSettingsFactory(string json)
=> (t, s, v) => (s ?? new()).Deserialize(new StringReader(json), t);
diff --git a/test/ApiCodeGenerator.AsyncApi.Tests/asyncApi/externalRef.json b/test/ApiCodeGenerator.AsyncApi.Tests/asyncApi/externalRef.json
new file mode 100644
index 0000000..bdb2233
--- /dev/null
+++ b/test/ApiCodeGenerator.AsyncApi.Tests/asyncApi/externalRef.json
@@ -0,0 +1,15 @@
+{
+ "asyncapi": "2.6.0",
+ "info": {
+ "title": "Document with reference to external schema",
+ "version": "1.0"
+ },
+ "channels": {},
+ "components": {
+ "messages": {
+ "lightMeasured": {
+ "$ref": "asyncapi.json#/components/messages/lightMeasured"
+ }
+ }
+ }
+}
diff --git a/test/ApiCodeGenerator.AsyncApi.Tests/asyncApi/externalRef.yaml b/test/ApiCodeGenerator.AsyncApi.Tests/asyncApi/externalRef.yaml
new file mode 100644
index 0000000..2f63c62
--- /dev/null
+++ b/test/ApiCodeGenerator.AsyncApi.Tests/asyncApi/externalRef.yaml
@@ -0,0 +1,9 @@
+asyncapi: "2.6.0"
+info:
+ title: Document with reference to external schema
+ version: "1.0"
+channels: {}
+components:
+ messages:
+ lightMeasured:
+ $ref: asyncapi.yml#/components/messages/lightMeasured
diff --git a/test/ApiCodeGenerator.OpenApi.Tests/ContentGeneratorFactoryTests.cs b/test/ApiCodeGenerator.OpenApi.Tests/ContentGeneratorFactoryTests.cs
index a2e8dc0..5cf9070 100644
--- a/test/ApiCodeGenerator.OpenApi.Tests/ContentGeneratorFactoryTests.cs
+++ b/test/ApiCodeGenerator.OpenApi.Tests/ContentGeneratorFactoryTests.cs
@@ -158,7 +158,8 @@ public async Task LoadOpenApiDocument_WithModelPreprocess()
public async Task LoadOpenApiDocument_FromYaml()
{
var context = CreateContext(new());
- var schemaText = await File.ReadAllTextAsync(TestHelpers.GetSwaggerPath("testSchema.yaml"));
+ context.DocumentPath = TestHelpers.GetSwaggerPath("testSchema.yaml");
+ var schemaText = await File.ReadAllTextAsync(context.DocumentPath);
context.DocumentReader = new StringReader(schemaText);
var gen = (CSharpClientContentGenerator)await CSharpClientContentGenerator.CreateAsync(context);
@@ -166,6 +167,23 @@ public async Task LoadOpenApiDocument_FromYaml()
var openApiDocument = GetDocument(gen.Generator);
Assert.NotNull(openApiDocument);
+ Assert.NotNull(openApiDocument!.DocumentPath);
+ }
+
+ [TestCase("externalRef.json")]
+ public async Task LoadOpenApiDocument_ExternalRef(string documentPath)
+ {
+ var context = CreateContext(new());
+ context.DocumentPath = TestHelpers.GetSwaggerPath(documentPath);
+ var documentContent = await File.ReadAllTextAsync(context.DocumentPath);
+ context.DocumentReader = new StringReader(documentContent);
+
+ var gen = (CSharpClientContentGenerator)await CSharpClientContentGenerator.CreateAsync(context);
+
+ var openApiDocument = GetDocument(gen.Generator);
+
+ Assert.NotNull(openApiDocument);
+ Assert.NotNull(openApiDocument!.Definitions["test"].AllOf.Single().Reference);
}
private GeneratorContext CreateContext(JObject settingsJson, Core.ExtensionManager.Extensions? extension = null)
diff --git a/test/ApiCodeGenerator.OpenApi.Tests/swagger/externalRef.json b/test/ApiCodeGenerator.OpenApi.Tests/swagger/externalRef.json
new file mode 100644
index 0000000..a10c58b
--- /dev/null
+++ b/test/ApiCodeGenerator.OpenApi.Tests/swagger/externalRef.json
@@ -0,0 +1,19 @@
+{
+ "openapi": "3.0.0",
+ "components": {
+ "schemas": {
+ "test": {
+ "allOf": [
+ {
+ "$ref": "testSchema.json#/definitions/testOperResponse"
+ }
+ ],
+ "properties": {
+ "prop": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ }
+}