From 50649b334724d1ee528f6979a611e9258b13d0e1 Mon Sep 17 00:00:00 2001 From: Elize van der Riet Date: Mon, 15 May 2023 21:58:46 +0200 Subject: [PATCH 1/4] ISSUE #1228: Resolve path where it is an external reference --- .../V3/OpenApiPathItemDeserializer.cs | 7 +++++++ .../V3/OpenApiV3VersionService.cs | 14 ++++++++++++++ src/Microsoft.OpenApi/Models/OpenApiDocument.cs | 3 +++ src/Microsoft.OpenApi/Models/ReferenceType.cs | 7 ++++++- src/Microsoft.OpenApi/Services/OpenApiWalker.cs | 10 +++++++--- 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiPathItemDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiPathItemDeserializer.cs index 3bb10a555..371e7da80 100644 --- a/src/Microsoft.OpenApi.Readers/V3/OpenApiPathItemDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiPathItemDeserializer.cs @@ -56,6 +56,13 @@ public static OpenApiPathItem LoadPathItem(ParseNode node) { var mapNode = node.CheckMapNode("PathItem"); + var pointer = mapNode.GetReferencePointer(); + if (pointer != null) + { + var refObject = mapNode.GetReferencedObject(ReferenceType.Path, pointer); + return refObject; + } + var pathItem = new OpenApiPathItem(); ParseMap(mapNode, pathItem, _pathItemFixedFields, _pathItemPatternFields); diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs index ebe372d40..6c71c378a 100644 --- a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs +++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs @@ -129,6 +129,20 @@ public OpenApiReference ConvertToOpenApiReference( } id = localSegments[3]; } + else if (id.StartsWith("/paths/")) + { + var localSegments = segments[1].Split('/'); + if (localSegments.Length == 3) + { + // The reference of a path may contain JSON escape character ~1 for the forward-slash character, replace this otherwise + // the reference cannot be resolved. + id = localSegments[2].Replace("~1", "/"); + } + else + { + throw new OpenApiException("Referenced Path mismatch"); + } + } else { openApiReference.IsFragrament = true; diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 5177e4f45..c172d84c7 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -504,6 +504,9 @@ internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool case ReferenceType.Callback: return this.Components.Callbacks[reference.Id]; + case ReferenceType.Path: + return this.Paths[reference.Id]; + default: throw new OpenApiException(Properties.SRResource.InvalidReferenceType); } diff --git a/src/Microsoft.OpenApi/Models/ReferenceType.cs b/src/Microsoft.OpenApi/Models/ReferenceType.cs index 6ac0c9ed2..9d8984c96 100644 --- a/src/Microsoft.OpenApi/Models/ReferenceType.cs +++ b/src/Microsoft.OpenApi/Models/ReferenceType.cs @@ -58,6 +58,11 @@ public enum ReferenceType /// /// Tags item. /// - [Display("tags")] Tag + [Display("tags")] Tag, + + /// + /// Paths item. + /// + [Display("paths")] Path } } diff --git a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs index 3d9e978c9..25cd20dee 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -449,8 +449,12 @@ internal void Walk(OpenApiPathItem pathItem) if (pathItem != null) { - Walk(OpenApiConstants.Parameters, () => Walk(pathItem.Parameters)); - Walk(pathItem.Operations); + // The path may be a reference + if (!ProcessAsReference(pathItem)) + { + Walk(OpenApiConstants.Parameters, () => Walk(pathItem.Parameters)); + Walk(pathItem.Operations); + } } _visitor.Visit(pathItem as IOpenApiExtensible); From 6a4633807be2e1f311dbb51fadeb1cccc99fc725 Mon Sep 17 00:00:00 2001 From: Elize van der Riet Date: Mon, 15 May 2023 22:26:31 +0200 Subject: [PATCH 2/4] ISSUE #1228: Add unit test for path external reference to ConvertToOpenApiReferenceV3Tests --- .../ConvertToOpenApiReferenceV3Tests.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/ConvertToOpenApiReferenceV3Tests.cs b/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/ConvertToOpenApiReferenceV3Tests.cs index f7368b09b..6b94a161b 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/ConvertToOpenApiReferenceV3Tests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/ReferenceService/ConvertToOpenApiReferenceV3Tests.cs @@ -124,5 +124,24 @@ public void ParseLocalFileReference() reference.Type.Should().Be(referenceType); reference.ExternalResource.Should().Be(input); } + + [Fact] + public void ParseExternalPathReference() + { + // Arrange + var versionService = new OpenApiV3VersionService(Diagnostic); + var externalResource = "externalSchema.json"; + var referenceJsonEscaped = "/paths/~1applications~1{AppUUID}~1services~1{ServiceName}"; + var input = $"{externalResource}#{referenceJsonEscaped}"; + var id = "/applications/{AppUUID}/services/{ServiceName}"; + + // Act + var reference = versionService.ConvertToOpenApiReference(input, null); + + // Assert + reference.Type.Should().BeNull(); + reference.ExternalResource.Should().Be(externalResource); + reference.Id.Should().Be(id); + } } } From c329b37bc7e87a403f5f32db296f879326dea655 Mon Sep 17 00:00:00 2001 From: Elize van der Riet Date: Wed, 17 May 2023 10:04:30 +0200 Subject: [PATCH 3/4] ISSUE #1228: Changes for review comments --- .../V3/OpenApiV3VersionService.cs | 6 +++--- src/Microsoft.OpenApi/Services/OpenApiWalker.cs | 11 ++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs index 6c71c378a..85000fc19 100644 --- a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs +++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs @@ -131,12 +131,12 @@ public OpenApiReference ConvertToOpenApiReference( } else if (id.StartsWith("/paths/")) { - var localSegments = segments[1].Split('/'); - if (localSegments.Length == 3) + var localSegments = segments[1].Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + if (localSegments.Length == 2) { // The reference of a path may contain JSON escape character ~1 for the forward-slash character, replace this otherwise // the reference cannot be resolved. - id = localSegments[2].Replace("~1", "/"); + id = localSegments[1].Replace("~1", "/"); } else { diff --git a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs index 25cd20dee..1da6cc9cf 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs @@ -447,14 +447,11 @@ internal void Walk(OpenApiPathItem pathItem) _visitor.Visit(pathItem); - if (pathItem != null) + // The path may be a reference + if (pathItem != null && !ProcessAsReference(pathItem)) { - // The path may be a reference - if (!ProcessAsReference(pathItem)) - { - Walk(OpenApiConstants.Parameters, () => Walk(pathItem.Parameters)); - Walk(pathItem.Operations); - } + Walk(OpenApiConstants.Parameters, () => Walk(pathItem.Parameters)); + Walk(pathItem.Operations); } _visitor.Visit(pathItem as IOpenApiExtensible); From e833d1bac94e1d3393ccce5f46f18e9b2c71301f Mon Sep 17 00:00:00 2001 From: BuzzyLizzy Date: Fri, 9 Jun 2023 13:54:20 +0200 Subject: [PATCH 4/4] ISSUE-1228: Change char array to static readonly for path separator character --- src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs index 85000fc19..889ce4cb8 100644 --- a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs +++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs @@ -21,6 +21,8 @@ internal class OpenApiV3VersionService : IOpenApiVersionService { public OpenApiDiagnostic Diagnostic { get; } + private static readonly char[] _pathSeparator = new char[] { '/' }; + /// /// Create Parsing Context /// @@ -131,7 +133,7 @@ public OpenApiReference ConvertToOpenApiReference( } else if (id.StartsWith("/paths/")) { - var localSegments = segments[1].Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + var localSegments = segments[1].Split(_pathSeparator, StringSplitOptions.RemoveEmptyEntries); if (localSegments.Length == 2) { // The reference of a path may contain JSON escape character ~1 for the forward-slash character, replace this otherwise