Skip to content
8 changes: 5 additions & 3 deletions src/Microsoft.OpenApi/Reader/V31/OpenApiV31Deserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,11 @@ private static (string, string) GetReferenceIdAndExternalResource(string pointer
string refId = !pointer.Contains('#') ? pointer : refSegments.Last();

var isExternalResource = !refSegments.First().StartsWith("#");
string externalResource = isExternalResource
? $"{refSegments.First()}/{refSegments[1].TrimEnd('#')}"
: null;
string externalResource = null;
if (isExternalResource && pointer.Contains('#'))
{
externalResource = $"{refSegments.First()}/{refSegments[1].TrimEnd('#')}";
}

return (refId, externalResource);
}
Expand Down
23 changes: 23 additions & 0 deletions src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,22 @@
/// </summary>
public class OpenApiWorkspace
{
private Dictionary<Uri, OpenApiDocument> _documents = new();
private readonly Dictionary<string, Uri> _documentsIdRegistry = new();
private readonly Dictionary<Uri, Stream> _artifactsRegistry = new();
private readonly Dictionary<Uri, IOpenApiReferenceable> _IOpenApiReferenceableRegistry = new();

/// <summary>
/// A list of OpenApiDocuments contained in the workspace
/// </summary>
public IEnumerable<OpenApiDocument> Documents
{
get
{
return _documents.Values;
}
}

/// <summary>
/// The base location from where all relative references are resolved
/// </summary>
Expand Down Expand Up @@ -96,6 +108,17 @@
}
}

/// <summary>
/// Add an OpenApiDocument to the workspace.
/// </summary>
/// <param name="location"></param>
/// <param name="document"></param>
public void AddDocument(string location, OpenApiDocument document)
{
document.Workspace = this;
_documents.Add(ToLocationUrl(location), document);
}

/// <summary>
/// Retrieves the document id given a key.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Reader;
using Microsoft.OpenApi.Tests;
using Microsoft.OpenApi.Writers;
using Microsoft.OpenApi.Services;
using Xunit;
using System.Linq;

namespace Microsoft.OpenApi.Readers.Tests.V31Tests
{
Expand Down Expand Up @@ -392,7 +394,7 @@ public void ParseDocumentsWithReusablePathItemInWebhooksSucceeds()
new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_1 });

var outputWriter = new StringWriter(CultureInfo.InvariantCulture);
var writer = new OpenApiJsonWriter(outputWriter, new() { InlineLocalReferences = true } );
var writer = new OpenApiJsonWriter(outputWriter, new() { InlineLocalReferences = true });
actual.OpenApiDocument.SerializeAsV31(writer);
var serialized = outputWriter.ToString();
}
Expand Down Expand Up @@ -445,7 +447,7 @@ public void ParseDocumentWithPatternPropertiesInSchemaWorks()
}
}
};

// Serialization
var mediaType = result.OpenApiDocument.Paths["/example"].Operations[OperationType.Get].Responses["200"].Content["application/json"];

Expand All @@ -461,7 +463,7 @@ public void ParseDocumentWithPatternPropertiesInSchemaWorks()
type: string
prop3:
type: string";

var actualMediaType = mediaType.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_1);

// Assert
Expand All @@ -484,5 +486,49 @@ public void ParseDocumentWithReferenceByIdGetsResolved()
Assert.Equal("object", requestBodySchema.Type);
Assert.Equal("string", parameterSchema.Type);
}

[Fact]
public async Task ExternalDocumentDereferenceToOpenApiDocumentUsingJsonPointerWorks()
{
// Arrange
var path = Path.Combine(Directory.GetCurrentDirectory(), SampleFolderPath);

var settings = new OpenApiReaderSettings
{
LoadExternalRefs = true,
BaseUrl = new(path),
};

// Act
var result = await OpenApiDocument.LoadAsync(Path.Combine(SampleFolderPath, "externalRefByJsonPointer.yaml"), settings);
var responseSchema = result.OpenApiDocument.Paths["/resource"].Operations[OperationType.Get].Responses["200"].Content["application/json"].Schema;

// Assert
result.OpenApiDocument.Workspace.Contains("./externalResource.yaml");
responseSchema.Properties.Count.Should().Be(2); // reference has been resolved
}

[Fact]
public async Task ParseExternalDocumentDereferenceToOpenApiDocumentByIdWorks()
{
// Arrange
var path = Path.Combine(Directory.GetCurrentDirectory(), SampleFolderPath);

var settings = new OpenApiReaderSettings
{
LoadExternalRefs = true,
BaseUrl = new(path),
};

// Act
var result = await OpenApiDocument.LoadAsync(Path.Combine(SampleFolderPath, "externalRefById.yaml"), settings);
var doc2 = OpenApiDocument.Load(Path.Combine(SampleFolderPath, "externalResource.yaml")).OpenApiDocument;

var requestBodySchema = result.OpenApiDocument.Paths["/resource"].Operations[OperationType.Get].Parameters.First().Schema;
result.OpenApiDocument.Workspace.RegisterComponents(doc2);

// Assert
requestBodySchema.Properties.Count.Should().Be(2); // reference has been resolved
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
openapi: 3.1.0
info:
title: ReferenceById
version: 1.0.0
paths:
/resource:
get:
parameters:
- name: id
in: query
required: true
schema:
$ref: 'https://example.com/schemas/user.json'
components: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
openapi: 3.1.0
info:
title: ReferenceById
version: 1.0.0
paths:
/resource:
get:
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: './externalResource.yaml#/components/schemas/todo'
components: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
openapi: 3.1.0
info:
title: ReferencedById
version: 1.0.0
paths: {}
components:
schemas:
todo:
type: object
properties:
id:
type: string
name:
type: string
user:
$id: 'https://example.com/schemas/user.json'
type: object
properties:
id:
type: string
name:
type: string
2 changes: 2 additions & 0 deletions test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1507,6 +1507,8 @@ namespace Microsoft.OpenApi.Services
public OpenApiWorkspace(Microsoft.OpenApi.Services.OpenApiWorkspace workspace) { }
public OpenApiWorkspace(System.Uri baseUrl) { }
public System.Uri BaseUrl { get; }
public System.Collections.Generic.IEnumerable<Microsoft.OpenApi.Models.OpenApiDocument> Documents { get; }
public void AddDocument(string location, Microsoft.OpenApi.Models.OpenApiDocument document) { }
public void AddDocumentId(string key, System.Uri value) { }
public int ComponentsCount() { }
public bool Contains(string location) { }
Expand Down
Loading