Skip to content

Commit de49dcd

Browse files
committed
Modified IOpenApiReferenceable comparers to handle self-referencing references.
Corrected typo in OpenApiSecuritySchemeComparer parameters.
1 parent 1a2a22a commit de49dcd

File tree

7 files changed

+189
-25
lines changed

7 files changed

+189
-25
lines changed

src/Microsoft.OpenApi/Services/OpenApiExampleComparer.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,35 @@ public override void Compare(
4242
return;
4343
}
4444

45-
new OpenApiReferenceComparer<OpenApiExample>()
46-
.Compare(sourceExample.Reference, targetExample.Reference, comparisonContext);
45+
if (sourceExample.Reference != null
46+
&& targetExample.Reference != null
47+
&& sourceExample.Reference.Id != targetExample.Reference.Id)
48+
{
49+
WalkAndAddOpenApiDifference(
50+
comparisonContext,
51+
OpenApiConstants.DollarRef,
52+
new OpenApiDifference
53+
{
54+
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update,
55+
SourceValue = sourceExample.Reference,
56+
TargetValue = targetExample.Reference,
57+
OpenApiComparedElementType = typeof(OpenApiReference)
58+
});
59+
60+
return;
61+
}
62+
63+
if (sourceExample.Reference != null)
64+
{
65+
sourceExample = (OpenApiExample)comparisonContext.SourceDocument.ResolveReference(
66+
sourceExample.Reference);
67+
}
68+
69+
if (targetExample.Reference != null)
70+
{
71+
targetExample = (OpenApiExample)comparisonContext.TargetDocument.ResolveReference(
72+
targetExample.Reference);
73+
}
4774

4875
WalkAndCompare(comparisonContext, OpenApiConstants.Description,
4976
() => Compare(sourceExample.Description, targetExample.Description, comparisonContext));

src/Microsoft.OpenApi/Services/OpenApiParameterComparer.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,35 @@ public override void Compare(
4343
return;
4444
}
4545

46-
new OpenApiReferenceComparer<OpenApiParameter>()
47-
.Compare(sourceParameter.Reference, targetParameter.Reference, comparisonContext);
46+
if (sourceParameter.Reference != null
47+
&& targetParameter.Reference != null
48+
&& sourceParameter.Reference.Id != targetParameter.Reference.Id)
49+
{
50+
WalkAndAddOpenApiDifference(
51+
comparisonContext,
52+
OpenApiConstants.DollarRef,
53+
new OpenApiDifference
54+
{
55+
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update,
56+
SourceValue = sourceParameter.Reference,
57+
TargetValue = targetParameter.Reference,
58+
OpenApiComparedElementType = typeof(OpenApiReference)
59+
});
60+
61+
return;
62+
}
63+
64+
if (sourceParameter.Reference != null)
65+
{
66+
sourceParameter = (OpenApiParameter)comparisonContext.SourceDocument.ResolveReference(
67+
sourceParameter.Reference);
68+
}
69+
70+
if (targetParameter.Reference != null)
71+
{
72+
targetParameter = (OpenApiParameter)comparisonContext.TargetDocument.ResolveReference(
73+
targetParameter.Reference);
74+
}
4875

4976
WalkAndCompare(
5077
comparisonContext,

src/Microsoft.OpenApi/Services/OpenApiRequestBodyComparer.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,35 @@ public override void Compare(
4242
return;
4343
}
4444

45-
new OpenApiReferenceComparer<OpenApiRequestBody>()
46-
.Compare(sourceRequestBody.Reference, targetRequestBody.Reference, comparisonContext);
45+
if (sourceRequestBody.Reference != null
46+
&& targetRequestBody.Reference != null
47+
&& sourceRequestBody.Reference.Id != targetRequestBody.Reference.Id)
48+
{
49+
WalkAndAddOpenApiDifference(
50+
comparisonContext,
51+
OpenApiConstants.DollarRef,
52+
new OpenApiDifference
53+
{
54+
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update,
55+
SourceValue = sourceRequestBody.Reference,
56+
TargetValue = targetRequestBody.Reference,
57+
OpenApiComparedElementType = typeof(OpenApiReference)
58+
});
59+
60+
return;
61+
}
62+
63+
if (sourceRequestBody.Reference != null)
64+
{
65+
sourceRequestBody = (OpenApiRequestBody)comparisonContext.SourceDocument.ResolveReference(
66+
sourceRequestBody.Reference);
67+
}
68+
69+
if (targetRequestBody.Reference != null)
70+
{
71+
targetRequestBody = (OpenApiRequestBody)comparisonContext.TargetDocument.ResolveReference(
72+
targetRequestBody.Reference);
73+
}
4774

4875
WalkAndCompare(comparisonContext, OpenApiConstants.Description,
4976
() => Compare(sourceRequestBody.Description, targetRequestBody.Description, comparisonContext));

src/Microsoft.OpenApi/Services/OpenApiSecuritySchemeComparer.cs

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,26 @@ public class OpenApiSecuritySchemeComparer : OpenApiComparerBase<OpenApiSecurity
1313
/// <summary>
1414
/// Executes comparision against source and target <see cref="OpenApiSecurityScheme"/>.
1515
/// </summary>
16-
/// <param name="sourcecSecurityScheme">The source.</param>
16+
/// <param name="sourceSecurityScheme">The source.</param>
1717
/// <param name="targetSecurityScheme">The target.</param>
1818
/// <param name="comparisonContext">Context under which to compare the source and target.</param>
1919
public override void Compare(
20-
OpenApiSecurityScheme sourcecSecurityScheme,
20+
OpenApiSecurityScheme sourceSecurityScheme,
2121
OpenApiSecurityScheme targetSecurityScheme,
2222
ComparisonContext comparisonContext)
2323
{
24-
if (sourcecSecurityScheme == null && targetSecurityScheme == null)
24+
if (sourceSecurityScheme == null && targetSecurityScheme == null)
2525
{
2626
return;
2727
}
2828

29-
if (sourcecSecurityScheme == null || targetSecurityScheme == null)
29+
if (sourceSecurityScheme == null || targetSecurityScheme == null)
3030
{
3131
comparisonContext.AddOpenApiDifference(
3232
new OpenApiDifference
3333
{
3434
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update,
35-
SourceValue = sourcecSecurityScheme,
35+
SourceValue = sourceSecurityScheme,
3636
TargetValue = targetSecurityScheme,
3737
OpenApiComparedElementType = typeof(OpenApiSecurityScheme),
3838
Pointer = comparisonContext.PathString
@@ -41,40 +41,66 @@ public override void Compare(
4141
return;
4242
}
4343

44-
new OpenApiReferenceComparer<OpenApiSecurityScheme>()
45-
.Compare(sourcecSecurityScheme.Reference, targetSecurityScheme.Reference,
46-
comparisonContext);
44+
if (sourceSecurityScheme.Reference != null
45+
&& targetSecurityScheme.Reference != null
46+
&& sourceSecurityScheme.Reference.Id != targetSecurityScheme.Reference.Id)
47+
{
48+
WalkAndAddOpenApiDifference(
49+
comparisonContext,
50+
OpenApiConstants.DollarRef,
51+
new OpenApiDifference
52+
{
53+
OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update,
54+
SourceValue = sourceSecurityScheme.Reference,
55+
TargetValue = targetSecurityScheme.Reference,
56+
OpenApiComparedElementType = typeof(OpenApiReference)
57+
});
58+
59+
return;
60+
}
61+
62+
if (sourceSecurityScheme.Reference != null)
63+
{
64+
sourceSecurityScheme = (OpenApiSecurityScheme)comparisonContext.SourceDocument.ResolveReference(
65+
sourceSecurityScheme.Reference);
66+
}
67+
68+
if (targetSecurityScheme.Reference != null)
69+
{
70+
targetSecurityScheme = (OpenApiSecurityScheme)comparisonContext.TargetDocument.ResolveReference(
71+
targetSecurityScheme.Reference);
72+
}
4773

4874
WalkAndCompare(comparisonContext, OpenApiConstants.Description,
49-
() => Compare(sourcecSecurityScheme.Description, targetSecurityScheme.Description, comparisonContext));
75+
() => Compare(sourceSecurityScheme.Description, targetSecurityScheme.Description, comparisonContext));
5076

5177
WalkAndCompare(comparisonContext, OpenApiConstants.Type,
52-
() => Compare<SecuritySchemeType>(sourcecSecurityScheme.Type, targetSecurityScheme.Type,
78+
() => Compare<SecuritySchemeType>(sourceSecurityScheme.Type, targetSecurityScheme.Type,
5379
comparisonContext));
5480

5581
WalkAndCompare(comparisonContext, OpenApiConstants.Name,
56-
() => Compare(sourcecSecurityScheme.Name, targetSecurityScheme.Name, comparisonContext));
82+
() => Compare(sourceSecurityScheme.Name, targetSecurityScheme.Name, comparisonContext));
5783

5884
WalkAndCompare(comparisonContext, OpenApiConstants.In,
59-
() => Compare<ParameterLocation>(sourcecSecurityScheme.In, targetSecurityScheme.In, comparisonContext));
85+
() => Compare<ParameterLocation>(sourceSecurityScheme.In, targetSecurityScheme.In, comparisonContext));
6086

6187
WalkAndCompare(comparisonContext, OpenApiConstants.Scheme,
62-
() => Compare(sourcecSecurityScheme.Scheme, targetSecurityScheme.Scheme, comparisonContext));
88+
() => Compare(sourceSecurityScheme.Scheme, targetSecurityScheme.Scheme, comparisonContext));
6389

6490
WalkAndCompare(comparisonContext, OpenApiConstants.BearerFormat,
65-
() => Compare(sourcecSecurityScheme.BearerFormat, targetSecurityScheme.BearerFormat,
91+
() => Compare(sourceSecurityScheme.BearerFormat, targetSecurityScheme.BearerFormat,
6692
comparisonContext));
6793

6894
WalkAndCompare(comparisonContext, OpenApiConstants.OpenIdConnectUrl,
69-
() => Compare(sourcecSecurityScheme.OpenIdConnectUrl, targetSecurityScheme.OpenIdConnectUrl,
95+
() => Compare(sourceSecurityScheme.OpenIdConnectUrl, targetSecurityScheme.OpenIdConnectUrl,
7096
comparisonContext));
7197

7298
WalkAndCompare(
7399
comparisonContext,
74100
OpenApiConstants.Flows,
75101
() => comparisonContext
76102
.GetComparer<OpenApiOAuthFlows>()
77-
.Compare(sourcecSecurityScheme.Flows, targetSecurityScheme.Flows, comparisonContext));
103+
.Compare(sourceSecurityScheme.Flows, targetSecurityScheme.Flows, comparisonContext));
78104
}
79105
}
80106
}

test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,7 @@ namespace Microsoft.OpenApi.Services
11141114
public class OpenApiSecuritySchemeComparer : Microsoft.OpenApi.Services.OpenApiComparerBase<Microsoft.OpenApi.Models.OpenApiSecurityScheme>
11151115
{
11161116
public OpenApiSecuritySchemeComparer() { }
1117-
public override void Compare(Microsoft.OpenApi.Models.OpenApiSecurityScheme sourcecSecurityScheme, Microsoft.OpenApi.Models.OpenApiSecurityScheme targetSecurityScheme, Microsoft.OpenApi.Services.ComparisonContext comparisonContext) { }
1117+
public override void Compare(Microsoft.OpenApi.Models.OpenApiSecurityScheme sourceSecurityScheme, Microsoft.OpenApi.Models.OpenApiSecurityScheme targetSecurityScheme, Microsoft.OpenApi.Services.ComparisonContext comparisonContext) { }
11181118
}
11191119
public class OpenApiServerComparer : Microsoft.OpenApi.Services.OpenApiComparerBase<Microsoft.OpenApi.Models.OpenApiServer>
11201120
{

test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTestCases.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2251,7 +2251,12 @@ public static IEnumerable<object[]> GetTestCasesForOpenApiComparerShouldSucceed(
22512251
TargetValue = new OpenApiSecurityScheme
22522252
{
22532253
Description = "Test",
2254-
Name = "Test"
2254+
Name = "Test",
2255+
Reference = new OpenApiReference()
2256+
{
2257+
Type = ReferenceType.SecurityScheme,
2258+
Id = "scheme4"
2259+
}
22552260
}
22562261
},
22572262
new OpenApiDifference
@@ -2294,7 +2299,12 @@ public static IEnumerable<object[]> GetTestCasesForOpenApiComparerShouldSucceed(
22942299
SourceValue = new OpenApiSecurityScheme
22952300
{
22962301
Description = "Test",
2297-
Name = "Test"
2302+
Name = "Test",
2303+
Reference = new OpenApiReference()
2304+
{
2305+
Type = ReferenceType.SecurityScheme,
2306+
Id = "scheme3"
2307+
}
22982308
},
22992309
TargetValue = null
23002310
},

test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Linq;
66
using FluentAssertions;
7+
using Microsoft.OpenApi.Interfaces;
78
using Microsoft.OpenApi.Models;
89
using Microsoft.OpenApi.Services;
910
using Xunit;
@@ -33,10 +34,56 @@ public void OpenApiComparerShouldSucceed(
3334
{
3435
_output.WriteLine(testCaseName);
3536

37+
// Ensure that all references are corrected to mirror what happens in the MapNode.
38+
new OpenApiWalker(new SelfReferenceFixer()).Walk(source);
39+
new OpenApiWalker(new SelfReferenceFixer()).Walk(target);
40+
3641
var differences = OpenApiComparer.Compare(source, target).ToList();
42+
43+
var actualPaths = differences.Select(x => x.Pointer).ToList();
44+
var expectedPaths = expectedDifferences.Select(x => x.Pointer).ToList();
45+
3746
differences.Count().Should().Be(expectedDifferences.Count);
3847

3948
differences.Should().BeEquivalentTo(expectedDifferences);
4049
}
50+
51+
/// <summary>
52+
/// MapNode adds a self-reference for all IOpenApiReferenceable instances.
53+
/// This visitor mimics that behavior for C# created objects.
54+
/// </summary>
55+
internal class SelfReferenceFixer : OpenApiVisitorBase
56+
{
57+
public override void Visit(OpenApiComponents components)
58+
{
59+
UpdateNullReferences(components.Schemas, ReferenceType.Schema);
60+
UpdateNullReferences(components.Responses, ReferenceType.Response);
61+
UpdateNullReferences(components.Parameters, ReferenceType.Parameter);
62+
UpdateNullReferences(components.Examples, ReferenceType.Example);
63+
UpdateNullReferences(components.RequestBodies, ReferenceType.RequestBody);
64+
UpdateNullReferences(components.Headers, ReferenceType.Header);
65+
UpdateNullReferences(components.SecuritySchemes, ReferenceType.SecurityScheme);
66+
UpdateNullReferences(components.Links, ReferenceType.Link);
67+
UpdateNullReferences(components.Callbacks, ReferenceType.Callback);
68+
}
69+
70+
private void UpdateNullReferences<T>(IDictionary<string, T> mapping, ReferenceType referenceType)
71+
where T : IOpenApiReferenceable
72+
{
73+
foreach (var kvp in mapping)
74+
{
75+
var element = kvp.Value;
76+
77+
if (!element.UnresolvedReference && (element.Reference == null))
78+
{
79+
element.Reference = new OpenApiReference()
80+
{
81+
Id = kvp.Key,
82+
Type = referenceType,
83+
};
84+
}
85+
}
86+
}
87+
}
4188
}
4289
}

0 commit comments

Comments
 (0)